diff options
author | Hans Goudey <h.goudey@me.com> | 2021-06-24 06:55:01 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-06-24 07:28:13 +0300 |
commit | 379f116a2ac929883d29519b41c1e63cffe6e591 (patch) | |
tree | 41d1765abca0f0e1190a1d1d703af265dfd541b0 | |
parent | 2b343c74d5e80098eee60d3127ff8117fb8633be (diff) | |
parent | f7fbb518c8194e0a416321f5856e947d5592e131 (diff) |
Merge branch 'master' into temp-geometry-nodes-curve-deform-node
644 files changed, 17904 insertions, 8226 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f315fa87236..297e32bd67e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -836,7 +836,7 @@ if(WITH_PYTHON) # because UNIX will search for the old Python paths which may not exist. # giving errors about missing paths before this case is met. if(DEFINED PYTHON_VERSION AND "${PYTHON_VERSION}" VERSION_LESS "3.9") - message(FATAL_ERROR "At least Python 3.9 is required to build") + message(FATAL_ERROR "At least Python 3.9 is required to build, but found Python ${PYTHON_VERSION}") endif() file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/scripts/addons") diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake index 005616a9f8d..c08ecebc069 100644 --- a/build_files/build_environment/cmake/tbb.cmake +++ b/build_files/build_environment/cmake/tbb.cmake @@ -79,6 +79,7 @@ if(WIN32) # Normal collection of build artifacts COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb_debug.dll ${HARVEST_TARGET}/tbb/bin/tbb_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.lib COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy_debug.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy_debug.lib COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_debug.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_proxy_debug.dll diff --git a/build_files/cmake/Modules/FindGflags.cmake b/build_files/cmake/Modules/FindGflags.cmake index 71a05b423ed..e6da056925f 100644 --- a/build_files/cmake/Modules/FindGflags.cmake +++ b/build_files/cmake/Modules/FindGflags.cmake @@ -472,8 +472,7 @@ if(NOT GFLAGS_FOUND) gflags_report_not_found( "Could not find gflags include directory, set GFLAGS_INCLUDE_DIR " "to directory containing gflags/gflags.h") - endif(NOT GFLAGS_INCLUDE_DIR OR - NOT EXISTS ${GFLAGS_INCLUDE_DIR}) + endif() find_library(GFLAGS_LIBRARY NAMES gflags PATHS ${GFLAGS_LIBRARY_DIR_HINTS} @@ -484,8 +483,7 @@ if(NOT GFLAGS_FOUND) gflags_report_not_found( "Could not find gflags library, set GFLAGS_LIBRARY " "to full path to libgflags.") - endif(NOT GFLAGS_LIBRARY OR - NOT EXISTS ${GFLAGS_LIBRARY}) + endif() # gflags typically requires a threading library (which is OS dependent), note # that this defines the CMAKE_THREAD_LIBS_INIT variable. If we are able to @@ -560,8 +558,7 @@ if(NOT GFLAGS_FOUND) gflags_report_not_found( "Caller defined GFLAGS_INCLUDE_DIR:" " ${GFLAGS_INCLUDE_DIR} does not contain gflags/gflags.h header.") - endif(GFLAGS_INCLUDE_DIR AND - NOT EXISTS ${GFLAGS_INCLUDE_DIR}/gflags/gflags.h) + endif() # TODO: This regex for gflags library is pretty primitive, we use lowercase # for comparison to handle Windows using CamelCase library names, could # this check be better? @@ -571,8 +568,7 @@ if(NOT GFLAGS_FOUND) gflags_report_not_found( "Caller defined GFLAGS_LIBRARY: " "${GFLAGS_LIBRARY} does not match gflags.") - endif(GFLAGS_LIBRARY AND - NOT "${LOWERCASE_GFLAGS_LIBRARY}" MATCHES ".*gflags[^/]*") + endif() gflags_reset_find_library_prefix() diff --git a/build_files/cmake/Modules/FindNanoVDB.cmake b/build_files/cmake/Modules/FindNanoVDB.cmake index 95d76fb39af..e830f770c03 100644 --- a/build_files/cmake/Modules/FindNanoVDB.cmake +++ b/build_files/cmake/Modules/FindNanoVDB.cmake @@ -40,7 +40,7 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(NanoVDB DEFAULT_MSG IF(NANOVDB_FOUND) SET(NANOVDB_INCLUDE_DIRS ${NANOVDB_INCLUDE_DIR}) -ENDIF(NANOVDB_FOUND) +ENDIF() MARK_AS_ADVANCED( NANOVDB_INCLUDE_DIR diff --git a/build_files/cmake/Modules/FindOpenCOLLADA.cmake b/build_files/cmake/Modules/FindOpenCOLLADA.cmake index 00898923fbd..4e83fd04f38 100644 --- a/build_files/cmake/Modules/FindOpenCOLLADA.cmake +++ b/build_files/cmake/Modules/FindOpenCOLLADA.cmake @@ -46,7 +46,7 @@ SET(_opencollada_FIND_COMPONENTS ) # Fedora openCOLLADA package links these statically -# note that order is important here ot it wont link +# note that order is important here or it won't link SET(_opencollada_FIND_STATIC_COMPONENTS buffer ftoa diff --git a/build_files/cmake/Modules/FindPythonLibsUnix.cmake b/build_files/cmake/Modules/FindPythonLibsUnix.cmake index 78f8e03807f..a6fb463432b 100644 --- a/build_files/cmake/Modules/FindPythonLibsUnix.cmake +++ b/build_files/cmake/Modules/FindPythonLibsUnix.cmake @@ -44,7 +44,7 @@ SET(PYTHON_LINKFLAGS "-Xlinker -export-dynamic" CACHE STRING "Linker flags for p MARK_AS_ADVANCED(PYTHON_LINKFLAGS) -# if the user passes these defines as args, we dont want to overwrite +# if the user passes these defines as args, we don't want to overwrite SET(_IS_INC_DEF OFF) SET(_IS_INC_CONF_DEF OFF) SET(_IS_LIB_DEF OFF) @@ -143,7 +143,7 @@ IF((NOT _IS_INC_DEF) OR (NOT _IS_INC_CONF_DEF) OR (NOT _IS_LIB_DEF) OR (NOT _IS_ SET(_PYTHON_ABI_FLAGS "${_CURRENT_ABI_FLAGS}") break() ELSE() - # ensure we dont find values from 2 different ABI versions + # ensure we don't find values from 2 different ABI versions IF(NOT _IS_INC_DEF) UNSET(PYTHON_INCLUDE_DIR CACHE) ENDIF() diff --git a/build_files/cmake/Modules/Findsse2neon.cmake b/build_files/cmake/Modules/Findsse2neon.cmake index 2159dfac114..5c163326251 100644 --- a/build_files/cmake/Modules/Findsse2neon.cmake +++ b/build_files/cmake/Modules/Findsse2neon.cmake @@ -40,7 +40,7 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(sse2neon DEFAULT_MSG IF(SSE2NEON_FOUND) SET(SSE2NEON_INCLUDE_DIRS ${SSE2NEON_INCLUDE_DIR}) -ENDIF(SSE2NEON_FOUND) +ENDIF() MARK_AS_ADVANCED( SSE2NEON_INCLUDE_DIR diff --git a/build_files/cmake/clang_array_check.py b/build_files/cmake/clang_array_check.py index 57311623cda..09ff4f413db 100644 --- a/build_files/cmake/clang_array_check.py +++ b/build_files/cmake/clang_array_check.py @@ -305,7 +305,7 @@ def file_check_arg_sizes(tu): for i, node_child in enumerate(children): children = list(node_child.get_children()) - # skip if we dont have an index... + # skip if we don't have an index... size_def = args_size_definition.get(i, -1) if size_def == -1: @@ -354,7 +354,7 @@ def file_check_arg_sizes(tu): filepath # always the same but useful when running threaded )) - # we dont really care what we are looking at, just scan entire file for + # we don't really care what we are looking at, just scan entire file for # function calls. def recursive_func_call_check(node): diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 5ae5ed7c29e..8ad3f77c7d3 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -694,7 +694,7 @@ macro(message_first_run) endmacro() # when we have warnings as errors applied globally this -# needs to be removed for some external libs which we dont maintain. +# needs to be removed for some external libs which we don't maintain. # utility macro macro(remove_cc_flag @@ -794,7 +794,7 @@ macro(remove_extra_strict_flags) endmacro() # note, we can only append flags on a single file so we need to negate the options. -# at the moment we cant shut up ffmpeg deprecations, so use this, but will +# at the moment we can't shut up ffmpeg deprecations, so use this, but will # probably add more removals here. macro(remove_strict_c_flags_file filenames) @@ -963,14 +963,6 @@ macro(blender_project_hack_post) unset(_reset_standard_cflags_rel) unset(_reset_standard_cxxflags_rel) - # ------------------------------------------------------------------ - # workaround for omission in cmake 2.8.4's GNU.cmake, fixed in 2.8.5 - if(CMAKE_COMPILER_IS_GNUCC) - if(NOT DARWIN) - set(CMAKE_INCLUDE_SYSTEM_FLAG_C "-isystem ") - endif() - endif() - endmacro() # pair of macros to allow libraries to be specify files to install, but to diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index fe9dd6a58de..90188751fc0 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -388,6 +388,10 @@ endif() if(WITH_TBB) find_package(TBB) + if(NOT TBB_FOUND) + message(WARNING "TBB not found, disabling WITH_TBB") + set(WITH_TBB OFF) + endif() endif() if(WITH_POTRACE) diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index fcfde5a07ba..7f62399ac4f 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -457,6 +457,10 @@ endif() if(WITH_TBB) find_package_wrapper(TBB) + if(NOT TBB_FOUND) + message(WARNING "TBB not found, disabling WITH_TBB") + set(WITH_TBB OFF) + endif() endif() if(WITH_XR_OPENXR) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 99a2cb7966f..a0e91199c72 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -261,8 +261,10 @@ if(NOT DEFINED LIBDIR) else() message(FATAL_ERROR "32 bit compiler detected, blender no longer provides pre-build libraries for 32 bit windows, please set the LIBDIR cmake variable to your own library folder") endif() - # Can be 1910..1912 - if(MSVC_VERSION GREATER 1919) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30130) + message(STATUS "Visual Studio 2022 detected.") + set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) + elseif(MSVC_VERSION GREATER 1919) message(STATUS "Visual Studio 2019 detected.") set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) elseif(MSVC_VERSION GREATER 1909) @@ -548,7 +550,6 @@ if(WITH_OPENIMAGEIO) set(OPENIMAGEIO_LIBRARIES ${OIIO_OPTIMIZED} ${OIIO_DEBUG}) set(OPENIMAGEIO_DEFINITIONS "-DUSE_TBB=0") - set(OPENCOLORIO_DEFINITIONS "-DDOpenColorIO_SKIP_IMPORTS") set(OPENIMAGEIO_IDIFF "${OPENIMAGEIO}/bin/idiff.exe") add_definitions(-DOIIO_STATIC_DEFINE) add_definitions(-DOIIO_NO_SSE=1) @@ -594,7 +595,7 @@ if(WITH_OPENCOLORIO) debug ${OPENCOLORIO_LIBPATH}/libexpatdMD.lib debug ${OPENCOLORIO_LIBPATH}/pystring_d.lib ) - set(OPENCOLORIO_DEFINITIONS) + set(OPENCOLORIO_DEFINITIONS "-DOpenColorIO_SKIP_IMPORTS") endif() if(WITH_OPENVDB) diff --git a/build_files/windows/autodetect_msvc.cmd b/build_files/windows/autodetect_msvc.cmd index 85b3c095d1f..6cee4765b93 100644 --- a/build_files/windows/autodetect_msvc.cmd +++ b/build_files/windows/autodetect_msvc.cmd @@ -6,6 +6,9 @@ if %ERRORLEVEL% EQU 0 goto DetectionComplete call "%~dp0\detect_msvc2019.cmd" if %ERRORLEVEL% EQU 0 goto DetectionComplete +call "%~dp0\detect_msvc2022.cmd" +if %ERRORLEVEL% EQU 0 goto DetectionComplete + echo Compiler Detection failed. Use verbose switch for more information. exit /b 1 diff --git a/build_files/windows/check_libraries.cmd b/build_files/windows/check_libraries.cmd index 8e43cda9eb7..c495ee6eee9 100644 --- a/build_files/windows/check_libraries.cmd +++ b/build_files/windows/check_libraries.cmd @@ -1,5 +1,6 @@ if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15 if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15 +if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15 set BUILD_VS_SVNDIR=win64_%BUILD_VS_LIBDIRPOST% set BUILD_VS_LIBDIR="%BLENDER_DIR%..\lib\%BUILD_VS_SVNDIR%" diff --git a/build_files/windows/configure_msbuild.cmd b/build_files/windows/configure_msbuild.cmd index be69f99a8e8..a9267e8cfbb 100644 --- a/build_files/windows/configure_msbuild.cmd +++ b/build_files/windows/configure_msbuild.cmd @@ -19,10 +19,10 @@ if "%WITH_PYDEBUG%"=="1" ( set PYDEBUG_CMAKE_ARGS=-DWINDOWS_PYTHON_DEBUG=On ) -if "%BUILD_VS_YEAR%"=="2019" ( - set BUILD_PLATFORM_SELECT=-A %MSBUILD_PLATFORM% -) else ( +if "%BUILD_VS_YEAR%"=="2017" ( set BUILD_GENERATOR_POST=%WINDOWS_ARCH% +) else ( + set BUILD_PLATFORM_SELECT=-A %MSBUILD_PLATFORM% ) set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -G "Visual Studio %BUILD_VS_VER% %BUILD_VS_YEAR%%BUILD_GENERATOR_POST%" %BUILD_PLATFORM_SELECT% %TESTS_CMAKE_ARGS% %CLANG_CMAKE_ARGS% %ASAN_CMAKE_ARGS% %PYDEBUG_CMAKE_ARGS% diff --git a/build_files/windows/detect_msvc2022.cmd b/build_files/windows/detect_msvc2022.cmd new file mode 100644 index 00000000000..f30210f4e38 --- /dev/null +++ b/build_files/windows/detect_msvc2022.cmd @@ -0,0 +1,3 @@ +set BUILD_VS_VER=17 +set BUILD_VS_YEAR=2022 +call "%~dp0\detect_msvc_vswhere.cmd" diff --git a/build_files/windows/parse_arguments.cmd b/build_files/windows/parse_arguments.cmd index 54dc41ece87..000b98c992e 100644 --- a/build_files/windows/parse_arguments.cmd +++ b/build_files/windows/parse_arguments.cmd @@ -66,6 +66,14 @@ if NOT "%1" == "" ( ) else if "%1" == "2019b" ( set BUILD_VS_YEAR=2019 set VSWHERE_ARGS=-products Microsoft.VisualStudio.Product.BuildTools + ) else if "%1" == "2022" ( + set BUILD_VS_YEAR=2022 + ) else if "%1" == "2022pre" ( + set BUILD_VS_YEAR=2022 + set VSWHERE_ARGS=-prerelease + ) else if "%1" == "2022b" ( + set BUILD_VS_YEAR=2022 + set VSWHERE_ARGS=-products Microsoft.VisualStudio.Product.BuildTools ) else if "%1" == "packagename" ( set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -DCPACK_OVERRIDE_PACKAGENAME="%2" shift /1 diff --git a/doc/python_api/requirements.txt b/doc/python_api/requirements.txt index 7040fd190f3..a854d2a267b 100644 --- a/doc/python_api/requirements.txt +++ b/doc/python_api/requirements.txt @@ -1,2 +1,13 @@ -Sphinx==3.5.3 +sphinx==3.5.4 + +# Sphinx dependencies that are important +Jinja2==2.11.3 +Pygments==2.9.0 +docutils==0.16 +snowballstemmer==2.1.0 +babel==2.9.1 +requests==2.25.1 + +# Only needed to match the theme used for the official documentation. +# Without this theme, the default theme will be used. sphinx_rtd_theme==0.5.2 diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h index 03fd0112095..6bc92278a33 100644 --- a/extern/mantaflow/preprocessed/gitinfo.h +++ b/extern/mantaflow/preprocessed/gitinfo.h @@ -1,3 +1,3 @@ -#define MANTA_GIT_VERSION "commit 9c505cd22e289b98c9aa717efba8ef3201c7e458" +#define MANTA_GIT_VERSION "commit 8fbebe02459b7f72575872c20961f7cb757db408" diff --git a/extern/mantaflow/preprocessed/kernel.h b/extern/mantaflow/preprocessed/kernel.h index 90e30cd21e1..dbcc2342a11 100644 --- a/extern/mantaflow/preprocessed/kernel.h +++ b/extern/mantaflow/preprocessed/kernel.h @@ -71,6 +71,19 @@ class ParticleBase; for (int j = bnd; j < (grid).getSizeY() - bnd; ++j) \ for (int i = bnd; i < (grid).getSizeX() - bnd; ++i) +#define FOR_NEIGHBORS_BND(grid, radius, bnd) \ + for (int zj = ((grid).is3D() ? std::max(bnd, k - radius) : 0); \ + zj <= ((grid).is3D() ? std::min(k + radius, (grid).getSizeZ() - 1 - bnd) : 0); \ + zj++) \ + for (int yj = std::max(bnd, j - radius); \ + yj <= std::min(j + radius, (grid).getSizeY() - 1 - bnd); \ + yj++) \ + for (int xj = std::max(bnd, i - radius); \ + xj <= std::min(i + radius, (grid).getSizeX() - 1 - bnd); \ + xj++) + +#define FOR_NEIGHBORS(grid, radius) FOR_NEIGHBORS_BND(grid, radius, 0) + //! Basic data structure for kernel data, initialized based on kernel type (e.g. single, idx, etc). struct KernelBase { int maxX, maxY, maxZ, minZ, maxT, minT; diff --git a/extern/mantaflow/preprocessed/plugin/flip.cpp b/extern/mantaflow/preprocessed/plugin/flip.cpp index d6fd3437959..8757958d4b0 100644 --- a/extern/mantaflow/preprocessed/plugin/flip.cpp +++ b/extern/mantaflow/preprocessed/plugin/flip.cpp @@ -822,33 +822,29 @@ struct ComputeUnionLevelsetPindex : public KernelBase { { const Vec3 gridPos = Vec3(i, j, k) + Vec3(0.5); // shifted by half cell Real phiv = radius * 1.0; // outside + const int r = int(radius) + 1; - int r = int(radius) + 1; - int rZ = phi.is3D() ? r : 0; - for (int zj = k - rZ; zj <= k + rZ; zj++) - for (int yj = j - r; yj <= j + r; yj++) - for (int xj = i - r; xj <= i + r; xj++) { - if (!phi.isInBounds(Vec3i(xj, yj, zj))) - continue; + FOR_NEIGHBORS(phi, r) + { - // note, for the particle indices in indexSys the access is periodic (ie, dont skip for - // eg inBounds(sx,10,10) - IndexInt isysIdxS = index.index(xj, yj, zj); - IndexInt pStart = index(isysIdxS), pEnd = 0; - if (phi.isInBounds(isysIdxS + 1)) - pEnd = index(isysIdxS + 1); - else - pEnd = indexSys.size(); - - // now loop over particles in cell - for (IndexInt p = pStart; p < pEnd; ++p) { - const int psrc = indexSys[p].sourceIndex; - if (ptype && ((*ptype)[psrc] & exclude)) - continue; - const Vec3 pos = parts[psrc].pos; - phiv = std::min(phiv, fabs(norm(gridPos - pos)) - radius); - } - } + // note, for the particle indices in indexSys the access is periodic (ie, dont skip for eg + // inBounds(sx,10,10) + IndexInt isysIdxS = index.index(xj, yj, zj); + IndexInt pStart = index(isysIdxS), pEnd = 0; + if (phi.isInBounds(isysIdxS + 1)) + pEnd = index(isysIdxS + 1); + else + pEnd = indexSys.size(); + + // now loop over particles in cell + for (IndexInt p = pStart; p < pEnd; ++p) { + const int psrc = indexSys[p].sourceIndex; + if (ptype && ((*ptype)[psrc] & exclude)) + continue; + const Vec3 pos = parts[psrc].pos; + phiv = std::min(phiv, fabs(norm(gridPos - pos)) - radius); + } + } phi(i, j, k) = phiv; } inline const Grid<int> &getArg0() @@ -1026,39 +1022,35 @@ struct ComputeAveragedLevelsetWeight : public KernelBase { // loop over neighborhood, similar to ComputeUnionLevelsetPindex const Real sradiusInv = 1. / (4. * radius * radius); - int r = int(1. * radius) + 1; - int rZ = phi.is3D() ? r : 0; + const int r = int(radius) + 1; // accumulators Real wacc = 0.; Vec3 pacc = Vec3(0.); Real racc = 0.; - for (int zj = k - rZ; zj <= k + rZ; zj++) - for (int yj = j - r; yj <= j + r; yj++) - for (int xj = i - r; xj <= i + r; xj++) { - if (!phi.isInBounds(Vec3i(xj, yj, zj))) - continue; + FOR_NEIGHBORS(phi, r) + { - IndexInt isysIdxS = index.index(xj, yj, zj); - IndexInt pStart = index(isysIdxS), pEnd = 0; - if (phi.isInBounds(isysIdxS + 1)) - pEnd = index(isysIdxS + 1); - else - pEnd = indexSys.size(); - for (IndexInt p = pStart; p < pEnd; ++p) { - IndexInt psrc = indexSys[p].sourceIndex; - if (ptype && ((*ptype)[psrc] & exclude)) - continue; + IndexInt isysIdxS = index.index(xj, yj, zj); + IndexInt pStart = index(isysIdxS), pEnd = 0; + if (phi.isInBounds(isysIdxS + 1)) + pEnd = index(isysIdxS + 1); + else + pEnd = indexSys.size(); + for (IndexInt p = pStart; p < pEnd; ++p) { + IndexInt psrc = indexSys[p].sourceIndex; + if (ptype && ((*ptype)[psrc] & exclude)) + continue; - Vec3 pos = parts[psrc].pos; - Real s = normSquare(gridPos - pos) * sradiusInv; - // Real w = std::max(0., cubed(1.-s) ); - Real w = std::max(0., (1. - s)); // a bit smoother - wacc += w; - racc += radius * w; - pacc += pos * w; - } - } + Vec3 pos = parts[psrc].pos; + Real s = normSquare(gridPos - pos) * sradiusInv; + // Real w = std::max(0., cubed(1.-s) ); + Real w = std::max(0., (1. - s)); // a bit smoother + wacc += w; + racc += radius * w; + pacc += pos * w; + } + } if (wacc > VECTOR_EPSILON) { racc /= wacc; diff --git a/extern/mantaflow/preprocessed/plugin/meshplugins.cpp b/extern/mantaflow/preprocessed/plugin/meshplugins.cpp index cf315429d65..043660db20b 100644 --- a/extern/mantaflow/preprocessed/plugin/meshplugins.cpp +++ b/extern/mantaflow/preprocessed/plugin/meshplugins.cpp @@ -234,10 +234,10 @@ void subdivideMesh( normalize(ne2); // Real thisArea = sqrMag(cross(-e2,e0)); - // small angle approximation says sin(x) = arcsin(x) = x, - // arccos(x) = pi/2 - arcsin(x), - // cos(x) = dot(A,B), - // so angle is approximately 1 - dot(A,B). + // small angle approximation says sin(x) = arcsin(x) = x, + // arccos(x) = pi/2 - arcsin(x), + // cos(x) = dot(A,B), + // so angle is approximately 1 - dot(A,B). Real angle[3]; angle[0] = 1.0 - dot(ne0, -ne2); angle[1] = 1.0 - dot(ne1, -ne0); diff --git a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp index 2f876376f53..7a1d8224d94 100644 --- a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp +++ b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp @@ -2287,10 +2287,9 @@ struct knFlipComputePotentialTrappedAir : public KernelBase { const Vec3 &vj = scaleFromManta * v.getCentered(x, y, z); const Vec3 xij = xi - xj; const Vec3 vij = vi - vj; - Real h = !pot.is3D() ? - 1.414 * radius : - 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius for h, due - // to squared resp. cubic neighbor area + Real h = !pot.is3D() ? 1.414 * radius : + 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius + // for h, due to squared resp. cubic neighbor area vdiff += norm(vij) * (1 - dot(getNormalized(vij), getNormalized(xij))) * (1 - norm(xij) / h); } diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp index 283ed7d0adc..50cd9e3db5c 100644 --- a/intern/cycles/blender/blender_light.cpp +++ b/intern/cycles/blender/blender_light.cpp @@ -34,12 +34,17 @@ void BlenderSync::sync_light(BL::Object &b_parent, bool *use_portal) { /* test if we need to sync */ - Light *light; ObjectKey key(b_parent, persistent_id, b_ob_instance, false); BL::Light b_light(b_ob.data()); + Light *light = light_map.find(key); + + /* Check if the transform was modified, in case a linked collection is moved we do not get a + * specific depsgraph update (T88515). This also mimics the behavior for Objects. */ + const bool tfm_updated = (light && light->get_tfm() != tfm); + /* Update if either object or light data changed. */ - if (!light_map.add_or_update(&light, b_ob, b_parent, key)) { + if (!light_map.add_or_update(&light, b_ob, b_parent, key) && !tfm_updated) { Shader *shader; if (!shader_map.add_or_update(&shader, b_light)) { if (light->get_is_portal()) diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index 785ca6b4e01..c58d2eb6e04 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -289,11 +289,10 @@ static PyObject *render_func(PyObject * /*self*/, PyObject *args) RNA_pointer_create(NULL, &RNA_Depsgraph, (ID *)PyLong_AsVoidPtr(pydepsgraph), &depsgraphptr); BL::Depsgraph b_depsgraph(depsgraphptr); - /* Allow Blender to execute other Python scripts, and isolate TBB tasks so we - * don't get deadlocks with Blender threads accessing shared data like images. */ + /* Allow Blender to execute other Python scripts. */ python_thread_state_save(&session->python_thread_state); - tbb::this_task_arena::isolate([&] { session->render(b_depsgraph); }); + session->render(b_depsgraph); python_thread_state_restore(&session->python_thread_state); @@ -330,8 +329,7 @@ static PyObject *bake_func(PyObject * /*self*/, PyObject *args) python_thread_state_save(&session->python_thread_state); - tbb::this_task_arena::isolate( - [&] { session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height); }); + session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height); python_thread_state_restore(&session->python_thread_state); @@ -377,7 +375,7 @@ static PyObject *reset_func(PyObject * /*self*/, PyObject *args) python_thread_state_save(&session->python_thread_state); - tbb::this_task_arena::isolate([&] { session->reset_session(b_data, b_depsgraph); }); + session->reset_session(b_data, b_depsgraph); python_thread_state_restore(&session->python_thread_state); @@ -399,7 +397,7 @@ static PyObject *sync_func(PyObject * /*self*/, PyObject *args) python_thread_state_save(&session->python_thread_state); - tbb::this_task_arena::isolate([&] { session->synchronize(b_depsgraph); }); + session->synchronize(b_depsgraph); python_thread_state_restore(&session->python_thread_state); diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index b008dfa376f..392fec4d57b 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -1196,16 +1196,18 @@ class OptiXDevice : public CUDADevice { const CUDAContextScope scope(cuContext); + const bool use_fast_trace_bvh = (bvh->params.bvh_type == SceneParams::BVH_STATIC); + // Compute memory usage OptixAccelBufferSizes sizes = {}; OptixAccelBuildOptions options = {}; options.operation = operation; - if (background) { - // Prefer best performance and lowest memory consumption in background + if (use_fast_trace_bvh) { + VLOG(2) << "Using fast to trace OptiX BVH"; options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE | OPTIX_BUILD_FLAG_ALLOW_COMPACTION; } else { - // Prefer fast updates in viewport + VLOG(2) << "Using fast to update OptiX BVH"; options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_BUILD | OPTIX_BUILD_FLAG_ALLOW_UPDATE; } @@ -1253,15 +1255,16 @@ class OptiXDevice : public CUDADevice { out_data.device_pointer, sizes.outputSizeInBytes, &out_handle, - background ? &compacted_size_prop : NULL, - background ? 1 : 0)); + use_fast_trace_bvh ? &compacted_size_prop : NULL, + use_fast_trace_bvh ? 1 : 0)); bvh->traversable_handle = static_cast<uint64_t>(out_handle); // Wait for all operations to finish check_result_cuda_ret(cuStreamSynchronize(NULL)); - // Compact acceleration structure to save memory (do not do this in viewport for faster builds) - if (background) { + // Compact acceleration structure to save memory (only if using fast trace as the + // OPTIX_BUILD_FLAG_ALLOW_COMPACTION flag is only set in this case). + if (use_fast_trace_bvh) { uint64_t compacted_size = sizes.outputSizeInBytes; check_result_cuda_ret( cuMemcpyDtoH(&compacted_size, compacted_size_prop.result, sizeof(compacted_size))); @@ -1306,6 +1309,8 @@ class OptiXDevice : public CUDADevice { return; } + const bool use_fast_trace_bvh = (bvh->params.bvh_type == SceneParams::BVH_STATIC); + free_bvh_memory_delayed(); BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh); @@ -1315,10 +1320,10 @@ class OptiXDevice : public CUDADevice { if (!bvh->params.top_level) { assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1); - // Refit is only possible in viewport for now (because AS is built with - // OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above) OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD; - if (refit && !background) { + /* Refit is only possible when using fast to trace BVH (because AS is built with + * OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above). */ + if (refit && !use_fast_trace_bvh) { assert(bvh_optix->traversable_handle != 0); operation = OPTIX_BUILD_OPERATION_UPDATE; } diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp index 33ce3502879..c53ec668938 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/render/alembic_read.cpp @@ -736,13 +736,14 @@ static void process_uvs(CachedData &cache, const IV2fGeomParam::Sample &sample, double time) { - if (scope != kFacevaryingScope) { + if (scope != kFacevaryingScope && scope != kVaryingScope && scope != kVertexScope) { return; } const array<int> *uv_loops = cache.uv_loops.data_for_time_no_check(time).get_data_or_null(); - if (!uv_loops) { + /* It's ok to not have loop indices, as long as the scope is not face-varying. */ + if (!uv_loops && scope == kFacevaryingScope) { return; } @@ -766,9 +767,27 @@ static void process_uvs(CachedData &cache, const uint32_t *indices = sample.getIndices()->get(); const V2f *values = sample.getVals()->get(); - for (const int uv_loop_index : *uv_loops) { - const uint32_t index = indices[uv_loop_index]; - *data_float2++ = make_float2(values[index][0], values[index][1]); + if (scope == kFacevaryingScope) { + for (const int uv_loop_index : *uv_loops) { + const uint32_t index = indices[uv_loop_index]; + *data_float2++ = make_float2(values[index][0], values[index][1]); + } + } + else if (scope == kVaryingScope || scope == kVertexScope) { + if (triangles) { + for (size_t i = 0; i < triangles->size(); i++) { + const int3 t = (*triangles)[i]; + *data_float2++ = make_float2(values[t.x][0], values[t.x][1]); + *data_float2++ = make_float2(values[t.y][0], values[t.y][1]); + *data_float2++ = make_float2(values[t.z][0], values[t.z][1]); + } + } + else if (corners) { + for (size_t i = 0; i < corners->size(); i++) { + const int c = (*corners)[i]; + *data_float2++ = make_float2(values[c][0], values[c][1]); + } + } } attribute.data.add_data(data, time); diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt index 51cc47fa704..65a692acd03 100644 --- a/intern/cycles/test/CMakeLists.txt +++ b/intern/cycles/test/CMakeLists.txt @@ -15,7 +15,7 @@ if(WITH_GTESTS) Include(GTestTesting) - # Otherwise we get warnings here that we cant fix in external projects + # Otherwise we get warnings here that we can't fix in external projects remove_strict_flags() endif() diff --git a/intern/cycles/util/util_color.h b/intern/cycles/util/util_color.h index 203c0b289f6..40c2c431aca 100644 --- a/intern/cycles/util/util_color.h +++ b/intern/cycles/util/util_color.h @@ -223,12 +223,14 @@ ccl_device_inline ssef fastpow24(const ssef &arg) ssef x = fastpow<0x3F4CCCCD, 0x4F55A7FB>(arg); // error max = 0.17 avg = 0.0018 |avg| = 0.05 ssef arg2 = arg * arg; ssef arg4 = arg2 * arg2; - x = improve_5throot_solution(x, - arg4); /* error max = 0.018 avg = 0.0031 |avg| = 0.0031 */ - x = improve_5throot_solution(x, - arg4); /* error max = 0.00021 avg = 1.6e-05 |avg| = 1.6e-05 */ - x = improve_5throot_solution(x, - arg4); /* error max = 6.1e-07 avg = 5.2e-08 |avg| = 1.1e-07 */ + + /* error max = 0.018 avg = 0.0031 |avg| = 0.0031 */ + x = improve_5throot_solution(x, arg4); + /* error max = 0.00021 avg = 1.6e-05 |avg| = 1.6e-05 */ + x = improve_5throot_solution(x, arg4); + /* error max = 6.1e-07 avg = 5.2e-08 |avg| = 1.1e-07 */ + x = improve_5throot_solution(x, arg4); + return x * (x * x); } diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/util_system.cpp index 03bc5aea1dd..b010881058b 100644 --- a/intern/cycles/util/util_system.cpp +++ b/intern/cycles/util/util_system.cpp @@ -282,8 +282,8 @@ static CPUCapabilities &system_cpu_capabilities() /* actual opcode for xgetbv */ __asm__(".byte 0x0f, 0x01, 0xd0" : "=a"(xcr_feature_mask), "=d"(edx) : "c"(0)); # elif defined(_MSC_VER) && defined(_XCR_XFEATURE_ENABLED_MASK) - xcr_feature_mask = (uint32_t)_xgetbv( - _XCR_XFEATURE_ENABLED_MASK); /* min VS2010 SP1 compiler is required */ + /* Minimum VS2010 SP1 compiler is required. */ + xcr_feature_mask = (uint32_t)_xgetbv(_XCR_XFEATURE_ENABLED_MASK); # else xcr_feature_mask = 0; # endif diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h index 0c22cf82688..17e2a7dc05b 100644 --- a/intern/ffmpeg/ffmpeg_compat.h +++ b/intern/ffmpeg/ffmpeg_compat.h @@ -93,40 +93,21 @@ void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt) #endif FFMPEG_INLINE -void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp) +int64_t timestamp_from_pts_or_dts(int64_t pts, int64_t dts) { - int i; - - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - - st->cur_dts = av_rescale(timestamp, - st->time_base.den * (int64_t)ref_st->time_base.num, - st->time_base.num * (int64_t)ref_st->time_base.den); + /* Some videos do not have any pts values, use dts instead in those cases if + * possible. Usually when this happens dts can act as pts because as all frames + * should then be presented in their decoded in order. IE pts == dts. */ + if (pts == AV_NOPTS_VALUE) { + return dts; } + return pts; } FFMPEG_INLINE -void av_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp) -{ - my_update_cur_dts(s, ref_st, timestamp); -} - -FFMPEG_INLINE -int64_t av_get_pts_from_frame(AVFormatContext *avctx, AVFrame *picture) +int64_t av_get_pts_from_frame(AVFrame *picture) { - int64_t pts; - pts = picture->pts; - - if (pts == AV_NOPTS_VALUE) { - pts = picture->pkt_dts; - } - if (pts == AV_NOPTS_VALUE) { - pts = 0; - } - - (void)avctx; - return pts; + return timestamp_from_pts_or_dts(picture->pts, picture->pkt_dts); } /* -------------------------------------------------------------------- */ diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 1b5cdb3cda0..1815a46591a 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -370,6 +370,7 @@ elseif(WIN32) intern/GHOST_DropTargetWin32.cpp intern/GHOST_SystemWin32.cpp intern/GHOST_WindowWin32.cpp + intern/GHOST_Wintab.cpp intern/GHOST_ContextD3D.h intern/GHOST_DisplayManagerWin32.h @@ -377,6 +378,7 @@ elseif(WIN32) intern/GHOST_SystemWin32.h intern/GHOST_TaskbarWin32.h intern/GHOST_WindowWin32.h + intern/GHOST_Wintab.h ) if(NOT WITH_GL_EGL) @@ -481,10 +483,12 @@ if(WITH_XR_OPENXR) shlwapi ) elseif(UNIX AND NOT APPLE) - list(APPEND XR_PLATFORM_DEFINES - -DXR_OS_LINUX - -DXR_USE_PLATFORM_XLIB - ) + list(APPEND XR_PLATFORM_DEFINES -DXR_OS_LINUX) + if (WITH_GL_EGL) + list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_EGL) + else() + list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_XLIB) + endif() endif() add_definitions(-DWITH_XR_OPENXR ${XR_PLATFORM_DEFINES}) diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 3a8d0fbfecf..7efbd00c2eb 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -105,7 +105,9 @@ typedef enum { typedef enum { GHOST_kTabletAutomatic = 0, - GHOST_kTabletNative, + /* Show as Windows Ink to users to match "Use Windows Ink" in tablet utilities, + * but we use the dependent Windows Pointer API. */ + GHOST_kTabletWinPointer, GHOST_kTabletWintab, } GHOST_TTabletAPI; @@ -168,7 +170,7 @@ typedef enum { GHOST_kButtonMaskRight, GHOST_kButtonMaskButton4, GHOST_kButtonMaskButton5, - /* Trackballs and programmable buttons */ + /* Trackballs and programmable buttons. */ GHOST_kButtonMaskButton6, GHOST_kButtonMaskButton7, GHOST_kButtonNumMasks @@ -177,15 +179,15 @@ typedef enum { typedef enum { GHOST_kEventUnknown = 0, - GHOST_kEventCursorMove, /// Mouse move event - GHOST_kEventButtonDown, /// Mouse button event - GHOST_kEventButtonUp, /// Mouse button event - GHOST_kEventWheel, /// Mouse wheel event - GHOST_kEventTrackpad, /// Trackpad event + GHOST_kEventCursorMove, /* Mouse move event. */ + GHOST_kEventButtonDown, /* Mouse button event. */ + GHOST_kEventButtonUp, /* Mouse button event. */ + GHOST_kEventWheel, /* Mouse wheel event. */ + GHOST_kEventTrackpad, /* Trackpad event. */ #ifdef WITH_INPUT_NDOF - GHOST_kEventNDOFMotion, /// N degree of freedom device motion event - GHOST_kEventNDOFButton, /// N degree of freedom device button event + GHOST_kEventNDOFMotion, /* N degree of freedom device motion event. */ + GHOST_kEventNDOFButton, /* N degree of freedom device button event. */ #endif GHOST_kEventKeyDown, @@ -207,8 +209,8 @@ typedef enum { GHOST_kEventDraggingExited, GHOST_kEventDraggingDropDone, - GHOST_kEventOpenMainFile, // Needed for Cocoa to open double-clicked .blend file at startup - GHOST_kEventNativeResolutionChange, // Needed for Cocoa when window moves to other display + GHOST_kEventOpenMainFile, /* Needed for Cocoa to open double-clicked .blend file at startup. */ + GHOST_kEventNativeResolutionChange, /* Needed for Cocoa when window moves to other display. */ GHOST_kEventTimer, @@ -281,7 +283,7 @@ typedef enum { GHOST_kKeyPeriod = '.', GHOST_kKeySlash = '/', - // Number keys + /* Number keys. */ GHOST_kKey0 = '0', GHOST_kKey1, GHOST_kKey2, @@ -296,7 +298,7 @@ typedef enum { GHOST_kKeySemicolon = ';', GHOST_kKeyEqual = '=', - // Character keys + /* Character keys. */ GHOST_kKeyA = 'A', GHOST_kKeyB, GHOST_kKeyC, @@ -335,9 +337,9 @@ typedef enum { GHOST_kKeyRightControl, GHOST_kKeyLeftAlt, GHOST_kKeyRightAlt, - GHOST_kKeyOS, // Command key on Apple, Windows key(s) on Windows - GHOST_kKeyGrLess, // German PC only! - GHOST_kKeyApp, /* Also known as menu key. */ + GHOST_kKeyOS, /* Command key on Apple, Windows key(s) on Windows. */ + GHOST_kKeyGrLess, /* German PC only! */ + GHOST_kKeyApp, /* Also known as menu key. */ GHOST_kKeyCapsLock, GHOST_kKeyNumLock, @@ -358,7 +360,7 @@ typedef enum { GHOST_kKeyUpPage, GHOST_kKeyDownPage, - // Numpad keys + /* Numpad keys. */ GHOST_kKeyNumpad0, GHOST_kKeyNumpad1, GHOST_kKeyNumpad2, @@ -376,7 +378,7 @@ typedef enum { GHOST_kKeyNumpadAsterisk, GHOST_kKeyNumpadSlash, - // Function keys + /* Function keys. */ GHOST_kKeyF1, GHOST_kKeyF2, GHOST_kKeyF3, @@ -402,7 +404,7 @@ typedef enum { GHOST_kKeyF23, GHOST_kKeyF24, - // Multimedia keypad buttons + /* Multimedia keypad buttons. */ GHOST_kKeyMediaPlay, GHOST_kKeyMediaStop, GHOST_kKeyMediaFirst, @@ -479,9 +481,9 @@ typedef struct { typedef enum { GHOST_kDragnDropTypeUnknown = 0, - GHOST_kDragnDropTypeFilenames, /*Array of strings representing file names (full path) */ - GHOST_kDragnDropTypeString, /* Unformatted text UTF-8 string */ - GHOST_kDragnDropTypeBitmap /*Bitmap image data */ + GHOST_kDragnDropTypeFilenames, /* Array of strings representing file names (full path). */ + GHOST_kDragnDropTypeString, /* Unformatted text UTF-8 string. */ + GHOST_kDragnDropTypeBitmap /* Bitmap image data. */ } GHOST_TDragnDropTypes; typedef struct { @@ -527,18 +529,23 @@ typedef enum { #ifdef WITH_INPUT_NDOF typedef struct { /** N-degree of freedom device data v3 [GSoC 2010] */ - // Each component normally ranges from -1 to +1, but can exceed that. - // These use blender standard view coordinates, with positive rotations being CCW about the axis. - float tx, ty, tz; // translation - float rx, ry, rz; // rotation: - // axis = (rx,ry,rz).normalized - // amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg] - float dt; // time since previous NDOF Motion event - GHOST_TProgress progress; // Starting, InProgress or Finishing (for modal handlers) + /* Each component normally ranges from -1 to +1, but can exceed that. + * These use blender standard view coordinates, + * with positive rotations being CCW about the axis. */ + /* translation: */ + float tx, ty, tz; + /* rotation: + * - `axis = (rx,ry,rz).normalized` + * - `amount = (rx,ry,rz).magnitude` [in revolutions, 1.0 = 360 deg]. */ + float rx, ry, rz; + /** Time since previous NDOF Motion event */ + float dt; + /** Starting, #GHOST_kInProgress or #GHOST_kFinishing (for modal handlers) */ + GHOST_TProgress progress; } GHOST_TEventNDOFMotionData; typedef enum { GHOST_kPress, GHOST_kRelease } GHOST_TButtonAction; -// good for mouse or other buttons too, hmmm? +/* Good for mouse or other buttons too, hmmm? */ typedef struct { GHOST_TButtonAction action; @@ -561,7 +568,7 @@ typedef struct { */ /** The ascii code for the key event ('\0' if none). */ char ascii; - /** The unicode character. if the length is 6, not NULL terminated if all 6 are set */ + /** The unicode character. if the length is 6, not NULL terminated if all 6 are set. */ char utf8_buf[6]; /** Generated by auto-repeat. */ @@ -594,7 +601,8 @@ typedef void *GHOST_TEmbedderWindowID; #endif // _WIN32 #ifndef _WIN32 -// I can't use "Window" from "<X11/Xlib.h>" because it conflits with Window defined in winlay.h +/* I can't use "Window" from `X11/Xlib.h` + * because it conflicts with Window defined in `winlay.h`. */ typedef int GHOST_TEmbedderWindowID; #endif // _WIN32 @@ -642,8 +650,10 @@ typedef void *(*GHOST_XrGraphicsContextBindFn)(void); typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_ContextHandle graphics_context); typedef void (*GHOST_XrDrawViewFn)(const struct GHOST_XrDrawViewInfo *draw_view, void *customdata); -/* An array of GHOST_TXrGraphicsBinding items defining the candidate bindings to use. The first - * available candidate will be chosen, so order defines priority. */ +/** + * An array of #GHOST_TXrGraphicsBinding items defining the candidate bindings to use. + * The first available candidate will be chosen, so order defines priority. + */ typedef const GHOST_TXrGraphicsBinding *GHOST_XrGraphicsBindingCandidates; typedef struct { @@ -684,7 +694,7 @@ typedef struct GHOST_XrDrawViewInfo { float angle_up, angle_down; } fov; - /** Set if the buffer should be submitted with a srgb transfer applied. */ + /** Set if the buffer should be submitted with a SRGB transfer applied. */ char expects_srgb_buffer; } GHOST_XrDrawViewInfo; @@ -734,7 +744,7 @@ typedef struct GHOST_XrActionSpaceInfo { typedef struct GHOST_XrActionBindingInfo { const char *action_name; GHOST_TUns32 count_interaction_paths; - /** Interaction path: User (subaction) path + component path. */ + /** Interaction path: User (sub-action) path + component path. */ const char **interaction_paths; } GHOST_XrActionBindingInfo; diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp index bac7057d953..770ead5962e 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.cpp +++ b/intern/ghost/intern/GHOST_ContextEGL.cpp @@ -149,6 +149,9 @@ static bool egl_chk(bool result, const char *file = NULL, int line = 0, const ch static_cast<unsigned int>(error), code ? code : "<Unknown>", msg ? msg : "<Unknown>"); + (void)(file); + (void)(line); + (void)(text); #endif } @@ -285,6 +288,21 @@ GHOST_TSuccess GHOST_ContextEGL::getSwapInterval(int &intervalOut) return GHOST_kSuccess; } +EGLDisplay GHOST_ContextEGL::getDisplay() const +{ + return m_display; +} + +EGLConfig GHOST_ContextEGL::getConfig() const +{ + return m_config; +} + +EGLContext GHOST_ContextEGL::getContext() const +{ + return m_context; +} + GHOST_TSuccess GHOST_ContextEGL::activateDrawingContext() { if (m_display) { @@ -456,9 +474,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() attrib_list.push_back(EGL_NONE); - EGLConfig config; - - if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &config, 1, &num_config))) + if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &m_config, 1, &num_config))) goto error; // A common error is to assume that ChooseConfig worked because it returned EGL_TRUE @@ -466,7 +482,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() goto error; if (m_nativeWindow != 0) { - m_surface = ::eglCreateWindowSurface(m_display, config, m_nativeWindow, NULL); + m_surface = ::eglCreateWindowSurface(m_display, m_config, m_nativeWindow, NULL); } else { static const EGLint pb_attrib_list[] = { @@ -476,7 +492,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() 1, EGL_NONE, }; - m_surface = ::eglCreatePbufferSurface(m_display, config, pb_attrib_list); + m_surface = ::eglCreatePbufferSurface(m_display, m_config, pb_attrib_list); } if (!EGL_CHK(m_surface != EGL_NO_SURFACE)) @@ -577,7 +593,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() attrib_list.push_back(EGL_NONE); - m_context = ::eglCreateContext(m_display, config, m_sharedContext, &(attrib_list[0])); + m_context = ::eglCreateContext(m_display, m_config, m_sharedContext, &(attrib_list[0])); if (!EGL_CHK(m_context != EGL_NO_CONTEXT)) goto error; diff --git a/intern/ghost/intern/GHOST_ContextEGL.h b/intern/ghost/intern/GHOST_ContextEGL.h index f828271d88d..170647177c2 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.h +++ b/intern/ghost/intern/GHOST_ContextEGL.h @@ -36,6 +36,9 @@ #endif class GHOST_ContextEGL : public GHOST_Context { + /* XR code needs low level graphics data to send to OpenXR. */ + friend class GHOST_XrGraphicsBindingOpenGL; + public: /** * Constructor. @@ -100,6 +103,12 @@ class GHOST_ContextEGL : public GHOST_Context { */ GHOST_TSuccess getSwapInterval(int &intervalOut); + EGLDisplay getDisplay() const; + + EGLConfig getConfig() const; + + EGLContext getContext() const; + private: bool initContextEGLEW(); @@ -117,6 +126,7 @@ class GHOST_ContextEGL : public GHOST_Context { EGLContext m_context; EGLSurface m_surface; EGLDisplay m_display; + EGLConfig m_config; EGLint m_swap_interval; diff --git a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp index b836f256b27..11134ad1054 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp @@ -160,7 +160,7 @@ GHOST_TSuccess GHOST_DisplayManagerSDL::setCurrentDisplaySetting( else { /* this is a problem for the BGE player :S, perhaps SDL2 will resolve at some point. * we really need SDL_SetDisplayModeForDisplay() to become an API func! - campbell */ - printf("no windows available, cant fullscreen\n"); + printf("no windows available, can't fullscreen\n"); /* do not fail, we will try again later when the window is created - wander */ return GHOST_kSuccess; diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h index e9e688b76ab..5508d34e96c 100644 --- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h +++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h @@ -31,7 +31,11 @@ class GHOST_IXrGraphicsBinding { public: union { #if defined(WITH_GHOST_X11) +# if defined(WITH_GL_EGL) + XrGraphicsBindingEGLMNDX egl; +# else XrGraphicsBindingOpenGLXlibKHR glx; +# endif #elif defined(WIN32) XrGraphicsBindingOpenGLWin32KHR wgl; XrGraphicsBindingD3D11KHR d3d11; diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index 2a7123b293e..9915520691f 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -239,7 +239,7 @@ class GHOST_System : public GHOST_ISystem { * Set which tablet API to use. Only affects Windows, other platforms have a single API. * \param api: Enum indicating which API to use. */ - void setTabletAPI(GHOST_TTabletAPI api); + virtual void setTabletAPI(GHOST_TTabletAPI api) override; GHOST_TTabletAPI getTabletAPI(void); #ifdef WITH_INPUT_NDOF diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 8862d0457f2..eccef18bee2 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -376,7 +376,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); - // cant use setCursorPosition because the mouse may have no focus! + // can't use setCursorPosition because the mouse may have no focus! if (x_new != x_root || y_new != y_root) { if (1) { //xme.time > m_last_warp) { /* when wrapping we don't need to add an event because the diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index c3a243cc22c..a70e5b91674 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -139,7 +139,6 @@ static void initRawInput() #undef DEVICE_COUNT } -typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND); GHOST_SystemWin32::GHOST_SystemWin32() @@ -867,15 +866,151 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, { GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - if (type == GHOST_kEventButtonDown) { - window->updateMouseCapture(MousePressed); + GHOST_TabletData td = window->getTabletData(); + + /* Move mouse to button event position. */ + if (window->getTabletData().Active != GHOST_kTabletModeNone) { + /* Tablet should be handling in between mouse moves, only move to event position. */ + DWORD msgPos = ::GetMessagePos(); + int msgPosX = GET_X_LPARAM(msgPos); + int msgPosY = GET_Y_LPARAM(msgPos); + system->pushEvent(new GHOST_EventCursor( + ::GetMessageTime(), GHOST_kEventCursorMove, window, msgPosX, msgPosY, td)); } - else if (type == GHOST_kEventButtonUp) { - window->updateMouseCapture(MouseReleased); + + window->updateMouseCapture(type == GHOST_kEventButtonDown ? MousePressed : MouseReleased); + return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td); +} + +void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window) +{ + GHOST_Wintab *wt = window->getWintab(); + if (!wt) { + return; } - return new GHOST_EventButton( - system->getMilliSeconds(), type, window, mask, window->getTabletData()); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + + std::vector<GHOST_WintabInfoWin32> wintabInfo; + wt->getInput(wintabInfo); + + /* Wintab provided coordinates are untrusted until a Wintab and Win32 button down event match. + * This is checked on every button down event, and revoked if there is a mismatch. This can + * happen when Wintab incorrectly scales cursor position or is in mouse mode. + * + * If Wintab was never trusted while processing this Win32 event, a fallback Ghost cursor move + * event is created at the position of the Win32 WT_PACKET event. */ + bool mouseMoveHandled; + bool useWintabPos; + mouseMoveHandled = useWintabPos = wt->trustCoordinates(); + + for (GHOST_WintabInfoWin32 &info : wintabInfo) { + switch (info.type) { + case GHOST_kEventCursorMove: { + if (!useWintabPos) { + continue; + } + + wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y); + system->pushEvent(new GHOST_EventCursor( + info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData)); + + break; + } + case GHOST_kEventButtonDown: { + UINT message; + switch (info.button) { + case GHOST_kButtonMaskLeft: + message = WM_LBUTTONDOWN; + break; + case GHOST_kButtonMaskRight: + message = WM_RBUTTONDOWN; + break; + case GHOST_kButtonMaskMiddle: + message = WM_MBUTTONDOWN; + break; + default: + continue; + } + + /* Wintab buttons are modal, but the API does not inform us what mode a pressed button is + * in. Only issue button events if we can steal an equivalent Win32 button event from the + * event queue. */ + MSG msg; + if (PeekMessage(&msg, window->getHWND(), message, message, PM_NOYIELD) && + msg.message != WM_QUIT) { + + /* Test for Win32/Wintab button down match. */ + useWintabPos = wt->testCoordinates(msg.pt.x, msg.pt.y, info.x, info.y); + if (!useWintabPos) { + continue; + } + + /* Steal the Win32 event which was previously peeked. */ + PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD); + + /* Move cursor to button location, to prevent incorrect cursor position when + * transitioning from unsynchronized Win32 to Wintab cursor control. */ + wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y); + system->pushEvent(new GHOST_EventCursor( + info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData)); + + window->updateMouseCapture(MousePressed); + system->pushEvent( + new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData)); + + mouseMoveHandled = true; + break; + } + } + case GHOST_kEventButtonUp: { + if (!useWintabPos) { + continue; + } + + UINT message; + switch (info.button) { + case GHOST_kButtonMaskLeft: + message = WM_LBUTTONUP; + break; + case GHOST_kButtonMaskRight: + message = WM_RBUTTONUP; + break; + case GHOST_kButtonMaskMiddle: + message = WM_MBUTTONUP; + break; + default: + continue; + } + + /* Wintab buttons are modal, but the API does not inform us what mode a pressed button is + * in. Only issue button events if we can steal an equivalent Win32 button event from the + * event queue. */ + MSG msg; + if (PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD) && + msg.message != WM_QUIT) { + + window->updateMouseCapture(MouseReleased); + system->pushEvent( + new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData)); + } + break; + } + default: + break; + } + } + + /* Fallback cursor movement if Wintab position were never trusted while processing this event. */ + if (!mouseMoveHandled) { + DWORD pos = GetMessagePos(); + int x = GET_X_LPARAM(pos); + int y = GET_Y_LPARAM(pos); + + /* TODO supply tablet data */ + system->pushEvent(new GHOST_EventCursor( + system->getMilliSeconds(), GHOST_kEventCursorMove, window, x, y, GHOST_TABLET_DATA_NONE)); + } } void GHOST_SystemWin32::processPointerEvent( @@ -883,7 +1018,7 @@ void GHOST_SystemWin32::processPointerEvent( { /* Pointer events might fire when changing windows for a device which is set to use Wintab, even * when when Wintab is left enabled but set to the bottom of Wintab overlap order. */ - if (!window->useTabletAPI(GHOST_kTabletNative)) { + if (!window->usingTabletAPI(GHOST_kTabletWinPointer)) { return; } @@ -894,20 +1029,21 @@ void GHOST_SystemWin32::processPointerEvent( return; } - if (!pointerInfo[0].isPrimary) { - eventHandled = true; - return; // For multi-touch displays we ignore these events - } - switch (type) { - case WM_POINTERENTER: - window->m_tabletInRange = true; - system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time, - GHOST_kEventCursorMove, - window, - pointerInfo[0].pixelLocation.x, - pointerInfo[0].pixelLocation.y, - pointerInfo[0].tabletData)); + case WM_POINTERUPDATE: + /* Coalesced pointer events are reverse chronological order, reorder chronologically. + * Only contiguous move events are coalesced. */ + for (GHOST_TUns32 i = pointerInfo.size(); i-- > 0;) { + system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time, + GHOST_kEventCursorMove, + window, + pointerInfo[i].pixelLocation.x, + pointerInfo[i].pixelLocation.y, + pointerInfo[i].tabletData)); + } + + /* Leave event unhandled so that system cursor is moved. */ + break; case WM_POINTERDOWN: /* Move cursor to point of contact because GHOST_EventButton does not include position. */ @@ -923,18 +1059,10 @@ void GHOST_SystemWin32::processPointerEvent( pointerInfo[0].buttonMask, pointerInfo[0].tabletData)); window->updateMouseCapture(MousePressed); - break; - case WM_POINTERUPDATE: - /* Coalesced pointer events are reverse chronological order, reorder chronologically. - * Only contiguous move events are coalesced. */ - for (GHOST_TUns32 i = pointerInfo.size(); i-- > 0;) { - system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time, - GHOST_kEventCursorMove, - window, - pointerInfo[i].pixelLocation.x, - pointerInfo[i].pixelLocation.y, - pointerInfo[i].tabletData)); - } + + /* Mark event handled so that mouse button events are not generated. */ + eventHandled = true; + break; case WM_POINTERUP: system->pushEvent(new GHOST_EventButton(pointerInfo[0].time, @@ -943,16 +1071,14 @@ void GHOST_SystemWin32::processPointerEvent( pointerInfo[0].buttonMask, pointerInfo[0].tabletData)); window->updateMouseCapture(MouseReleased); - break; - case WM_POINTERLEAVE: - window->m_tabletInRange = false; + + /* Mark event handled so that mouse button events are not generated. */ + eventHandled = true; + break; default: break; } - - eventHandled = true; - system->setCursorPosition(pointerInfo[0].pixelLocation.x, pointerInfo[0].pixelLocation.y); } GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window) @@ -960,18 +1086,14 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind GHOST_TInt32 x_screen, y_screen; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - if (window->m_tabletInRange) { - if (window->useTabletAPI(GHOST_kTabletNative)) { - /* Tablet input handled in WM_POINTER* events. WM_MOUSEMOVE events in response to tablet - * input aren't normally generated when using WM_POINTER events, but manually moving the - * system cursor as we do in WM_POINTER handling does. */ - return NULL; - } + if (window->getTabletData().Active != GHOST_kTabletModeNone) { + /* While pen devices are in range, cursor movement is handled by tablet input processing. */ + return NULL; } system->getCursorPosition(x_screen, y_screen); - if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) { + if (window->getCursorGrabModeIsWarp()) { GHOST_TInt32 x_new = x_screen; GHOST_TInt32 y_new = y_screen; GHOST_TInt32 x_accum, y_accum; @@ -983,7 +1105,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind } /* Could also clamp to screen bounds wrap with a window outside the view will fail atm. - * Use offset of 8 in case the window is at screen bounds. */ + * Use inset in case the window is at screen bounds. */ bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); @@ -999,7 +1121,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind window, x_screen + x_accum, y_screen + y_accum, - window->getTabletData()); + GHOST_TABLET_DATA_NONE); } } else { @@ -1008,7 +1130,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind window, x_screen, y_screen, - window->getTabletData()); + GHOST_TABLET_DATA_NONE); } return NULL; } @@ -1118,6 +1240,23 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA return event; } +GHOST_Event *GHOST_SystemWin32::processWindowSizeEvent(GHOST_WindowWin32 *window) +{ + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + GHOST_Event *sizeEvent = new GHOST_Event( + system->getMilliSeconds(), GHOST_kEventWindowSize, window); + + /* We get WM_SIZE before we fully init. Do not dispatch before we are continuously resizing. */ + if (window->m_inLiveResize) { + system->pushEvent(sizeEvent); + system->dispatchEvents(); + return NULL; + } + else { + return sizeEvent; + } +} + GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_WindowWin32 *window) { @@ -1125,7 +1264,6 @@ GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, if (type == GHOST_kEventWindowActivate) { system->getWindowManager()->setActiveWindow(window); - window->bringTabletContextToFront(); } return new GHOST_Event(system->getMilliSeconds(), type, window); @@ -1153,6 +1291,31 @@ GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType, system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data)); } +void GHOST_SystemWin32::setTabletAPI(GHOST_TTabletAPI api) +{ + GHOST_System::setTabletAPI(api); + + /* If API is set to WinPointer (Windows Ink), unload Wintab so that trouble drivers don't disable + * Windows Ink. Load Wintab when API is Automatic because decision logic relies on knowing + * whether a Wintab device is present. */ + const bool loadWintab = GHOST_kTabletWinPointer != api; + GHOST_WindowManager *wm = getWindowManager(); + + for (GHOST_IWindow *win : wm->getWindows()) { + GHOST_WindowWin32 *windowWin32 = (GHOST_WindowWin32 *)win; + if (loadWintab) { + windowWin32->loadWintab(GHOST_kWindowStateMinimized != windowWin32->getState()); + + if (windowWin32->usingTabletAPI(GHOST_kTabletWintab)) { + windowWin32->resetPointerPenInfo(); + } + } + else { + windowWin32->closeWintab(); + } + } +} + void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax) { minmax->ptMinTrackSize.x = 320; @@ -1387,33 +1550,100 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, case SC_KEYMENU: eventHandled = true; break; - case SC_RESTORE: + case SC_RESTORE: { ::ShowWindow(hwnd, SW_RESTORE); window->setState(window->getState()); + + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + wt->enable(); + } + eventHandled = true; break; + } + case SC_MAXIMIZE: { + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + wt->enable(); + } + /* Don't report event as handled so that default handling occurs. */ + break; + } + case SC_MINIMIZE: { + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + wt->disable(); + } + /* Don't report event as handled so that default handling occurs. */ + break; + } } break; //////////////////////////////////////////////////////////////////////// // Wintab events, processed //////////////////////////////////////////////////////////////////////// - case WT_PACKET: - window->processWin32TabletEvent(wParam, lParam); + case WT_CSRCHANGE: { + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + wt->updateCursorInfo(); + } + eventHandled = true; + break; + } + case WT_PROXIMITY: { + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + bool inRange = LOWORD(lParam); + if (inRange) { + /* Some devices don't emit WT_CSRCHANGE events, so update cursor info here. */ + wt->updateCursorInfo(); + } + else { + wt->leaveRange(); + } + } + eventHandled = true; break; - case WT_CSRCHANGE: - case WT_PROXIMITY: - window->processWin32TabletInitEvent(); + } + case WT_INFOCHANGE: { + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + wt->processInfoChange(lParam); + + if (window->usingTabletAPI(GHOST_kTabletWintab)) { + window->resetPointerPenInfo(); + } + } + eventHandled = true; + break; + } + case WT_PACKET: + processWintabEvent(window); + eventHandled = true; break; //////////////////////////////////////////////////////////////////////// // Pointer events, processed //////////////////////////////////////////////////////////////////////// - case WM_POINTERENTER: - case WM_POINTERDOWN: case WM_POINTERUPDATE: + case WM_POINTERDOWN: case WM_POINTERUP: - case WM_POINTERLEAVE: processPointerEvent(msg, window, wParam, lParam, eventHandled); break; + case WM_POINTERLEAVE: { + GHOST_TUns32 pointerId = GET_POINTERID_WPARAM(wParam); + POINTER_INFO pointerInfo; + if (!GetPointerInfo(pointerId, &pointerInfo)) { + break; + } + + /* Reset pointer pen info if pen device has left tracking range. */ + if (pointerInfo.pointerType == PT_PEN && !IS_POINTER_INRANGE_WPARAM(wParam)) { + window->resetPointerPenInfo(); + eventHandled = true; + } + break; + } //////////////////////////////////////////////////////////////////////// // Mouse events, processed //////////////////////////////////////////////////////////////////////// @@ -1452,7 +1682,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; case WM_MOUSEMOVE: + if (!window->m_mousePresent) { + TRACKMOUSEEVENT tme = {sizeof(tme)}; + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hwnd; + TrackMouseEvent(&tme); + window->m_mousePresent = true; + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + wt->gainFocus(); + } + } + event = processCursorEvent(window); + break; case WM_MOUSEWHEEL: { /* The WM_MOUSEWHEEL message is sent to the focus window @@ -1487,7 +1730,17 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, window->loadCursor(true, GHOST_kStandardCursorDefault); } break; - + case WM_MOUSELEAVE: { + window->m_mousePresent = false; + if (window->getTabletData().Active == GHOST_kTabletModeNone) { + processCursorEvent(window); + } + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + wt->loseFocus(); + } + break; + } //////////////////////////////////////////////////////////////////////// // Mouse events, ignored //////////////////////////////////////////////////////////////////////// @@ -1535,7 +1788,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * will not be dispatched to OUR active window if we minimize one of OUR windows. */ if (LOWORD(wParam) == WA_INACTIVE) window->lostMouseCapture(); - window->processWin32TabletActivateEvent(GET_WM_ACTIVATE_STATE(wParam, lParam)); + lResult = ::DefWindowProc(hwnd, msg, wParam, lParam); break; } @@ -1577,6 +1830,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, /* Let DefWindowProc handle it. */ break; case WM_SIZING: + event = processWindowSizeEvent(window); + break; case WM_SIZE: /* The WM_SIZE message is sent to a window after its size has changed. * The WM_SIZE and WM_MOVE messages are not sent if an application handles the @@ -1584,15 +1839,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * to perform any move or size change processing during the WM_WINDOWPOSCHANGED * message without calling DefWindowProc. */ - /* we get first WM_SIZE before we fully init. - * So, do not dispatch before we continuously resizing. */ - if (window->m_inLiveResize) { - system->pushEvent(processWindowEvent(GHOST_kEventWindowSize, window)); - system->dispatchEvents(); - } - else { - event = processWindowEvent(GHOST_kEventWindowSize, window); - } + event = processWindowSizeEvent(window); break; case WM_CAPTURECHANGED: window->lostMouseCapture(); @@ -1643,6 +1890,21 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, SWP_NOZORDER | SWP_NOACTIVATE); } break; + case WM_DISPLAYCHANGE: { + GHOST_Wintab *wt = window->getWintab(); + if (wt) { + wt->remapCoordinates(); + } + break; + } + case WM_KILLFOCUS: + /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard + * focus. We want to prevent this if a window is still active and it loses focus to + * nowhere. */ + if (!wParam && hwnd == ::GetActiveWindow()) { + ::SetFocus(hwnd); + } + break; //////////////////////////////////////////////////////////////////////// // Window events, ignored //////////////////////////////////////////////////////////////////////// @@ -1679,12 +1941,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * object associated with the window. */ break; - case WM_KILLFOCUS: - /* The WM_KILLFOCUS message is sent to a window immediately before it loses the - * keyboard focus. We want to prevent this if a window is still active and it loses - * focus to nowhere. */ - if (!wParam && hwnd == ::GetActiveWindow()) - ::SetFocus(hwnd); case WM_SHOWWINDOW: /* The WM_SHOWWINDOW message is sent to a window when the window is * about to be hidden or shown. */ diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 51c0c984710..7dd61421d4c 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -265,6 +265,16 @@ class GHOST_SystemWin32 : public GHOST_System { int mouseY, void *data); + /*************************************************************************************** + ** Modify tablet API + ***************************************************************************************/ + + /** + * Set which tablet API to use. + * \param api: Enum indicating which API to use. + */ + void setTabletAPI(GHOST_TTabletAPI api) override; + protected: /** * Initializes the system. @@ -309,6 +319,12 @@ class GHOST_SystemWin32 : public GHOST_System { GHOST_TButtonMask mask); /** + * Creates tablet events from Wintab events. + * \param window: The window receiving the event (the active window). + */ + static void processWintabEvent(GHOST_WindowWin32 *window); + + /** * Creates tablet events from pointer events. * \param type: The type of pointer event. * \param window: The window receiving the event (the active window). @@ -352,6 +368,13 @@ class GHOST_SystemWin32 : public GHOST_System { GHOST_TKey processSpecialKey(short vKey, short scanCode) const; /** + * Creates a window size event. + * \param window: The window receiving the event (the active window). + * \return The event created. + */ + static GHOST_Event *processWindowSizeEvent(GHOST_WindowWin32 *window); + + /** * Creates a window event. * \param type: The type of event to create. * \param window: The window receiving the event (the active window). diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index df7e8ba59df..34b44fde48a 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -2563,7 +2563,7 @@ static bool is_filler_char(char c) return isspace(c) || c == '_' || c == '-' || c == ';' || c == ':'; } -/* These C functions are copied from Wine 3.12's wintab.c */ +/* These C functions are copied from Wine 3.12's `wintab.c` */ static bool match_token(const char *haystack, const char *needle) { const char *h, *n; diff --git a/intern/ghost/intern/GHOST_WindowSDL.cpp b/intern/ghost/intern/GHOST_WindowSDL.cpp index dcb1ab8c78c..ff0c506feb7 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.cpp +++ b/intern/ghost/intern/GHOST_WindowSDL.cpp @@ -556,7 +556,7 @@ static SDL_Cursor *sdl_ghost_CreateCursor( return cursor; } -/* TODO, this is currently never freed but it wont leak either. */ +/* TODO, this is currently never freed but it won't leak either. */ static SDL_Cursor *getStandardCursorShape(GHOST_TStandardCursor shape) { if (sdl_std_cursor_array[0] == NULL) { diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index eeafe333633..33c79daf11d 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -21,8 +21,6 @@ * \ingroup GHOST */ -#define _USE_MATH_DEFINES - #include "GHOST_WindowWin32.h" #include "GHOST_ContextD3D.h" #include "GHOST_ContextNone.h" @@ -72,7 +70,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, bool is_debug, bool dialog) : GHOST_Window(width, height, state, wantStereoVisual, false), - m_tabletInRange(false), + m_mousePresent(false), m_inLiveResize(false), m_system(system), m_hDC(0), @@ -82,6 +80,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_nPressedButtons(0), m_customCursor(0), m_wantAlphaBackground(alphaBackground), + m_wintab(NULL), + m_lastPointerTabletData(GHOST_TABLET_DATA_NONE), m_normal_state(GHOST_kWindowStateNormal), m_user32(NULL), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), @@ -90,10 +90,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); RECT win_rect = {left, top, (long)(left + width), (long)(top + height)}; - // Initialize tablet variables - memset(&m_wintab, 0, sizeof(m_wintab)); - m_tabletData = GHOST_TABLET_DATA_NONE; - DWORD style = parentwindow ? WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX : WS_OVERLAPPEDWINDOW; @@ -218,65 +214,10 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, } // Initialize Wintab - m_wintab.handle = ::LoadLibrary("Wintab32.dll"); - if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) { - // Get API functions - m_wintab.info = (GHOST_WIN32_WTInfo)::GetProcAddress(m_wintab.handle, "WTInfoA"); - m_wintab.open = (GHOST_WIN32_WTOpen)::GetProcAddress(m_wintab.handle, "WTOpenA"); - m_wintab.close = (GHOST_WIN32_WTClose)::GetProcAddress(m_wintab.handle, "WTClose"); - m_wintab.packet = (GHOST_WIN32_WTPacket)::GetProcAddress(m_wintab.handle, "WTPacket"); - m_wintab.enable = (GHOST_WIN32_WTEnable)::GetProcAddress(m_wintab.handle, "WTEnable"); - m_wintab.overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(m_wintab.handle, "WTOverlap"); - - // Let's see if we can initialize tablet here. - // Check if WinTab available by getting system context info. - LOGCONTEXT lc = {0}; - lc.lcOptions |= CXO_SYSTEM; - if (m_wintab.open && m_wintab.info && m_wintab.info(WTI_DEFSYSCTX, 0, &lc)) { - // Now init the tablet - /* The maximum tablet size, pressure and orientation (tilt) */ - AXIS TabletX, TabletY, Pressure, Orientation[3]; - - // Open a Wintab context - - // Open the context - lc.lcPktData = PACKETDATA; - lc.lcPktMode = PACKETMODE; - lc.lcOptions |= CXO_MESSAGES; - lc.lcMoveMask = PACKETDATA; - - /* Set the entire tablet as active */ - m_wintab.info(WTI_DEVICES, DVC_X, &TabletX); - m_wintab.info(WTI_DEVICES, DVC_Y, &TabletY); - - /* get the max pressure, to divide into a float */ - BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure); - if (pressureSupport) - m_wintab.maxPressure = Pressure.axMax; - else - m_wintab.maxPressure = 0; - - /* get the max tilt axes, to divide into floats */ - BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation); - if (tiltSupport) { - /* does the tablet support azimuth ([0]) and altitude ([1]) */ - if (Orientation[0].axResolution && Orientation[1].axResolution) { - /* all this assumes the minimum is 0 */ - m_wintab.maxAzimuth = Orientation[0].axMax; - m_wintab.maxAltitude = Orientation[1].axMax; - } - else { /* No so don't do tilt stuff. */ - m_wintab.maxAzimuth = m_wintab.maxAltitude = 0; - } - } - - // The Wintab spec says we must open the context disabled if we are using cursor masks. - m_wintab.tablet = m_wintab.open(m_hWnd, &lc, FALSE); - if (m_wintab.enable && m_wintab.tablet) { - m_wintab.enable(m_wintab.tablet, TRUE); - } - } + if (system->getTabletAPI() != GHOST_kTabletWinPointer) { + loadWintab(GHOST_kWindowStateMinimized != state); } + CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar); } @@ -289,14 +230,7 @@ GHOST_WindowWin32::~GHOST_WindowWin32() m_Bar = NULL; } - if (m_wintab.handle) { - if (m_wintab.close && m_wintab.tablet) { - m_wintab.close(m_wintab.tablet); - } - - FreeLibrary(m_wintab.handle); - memset(&m_wintab, 0, sizeof(m_wintab)); - } + closeWintab(); if (m_user32) { FreeLibrary(m_user32); @@ -913,20 +847,16 @@ GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorSha GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam) { - if (!useTabletAPI(GHOST_kTabletNative)) { - return GHOST_kFailure; - } - GHOST_TInt32 pointerId = GET_POINTERID_WPARAM(wParam); GHOST_TInt32 isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam); GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); - GHOST_TUns32 outCount; + GHOST_TUns32 outCount = 0; - if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) { + if (!(GetPointerPenInfoHistory(pointerId, &outCount, NULL))) { return GHOST_kFailure; } - auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount); + std::vector<POINTER_PEN_INFO> pointerPenInfo(outCount); outPointerInfo.resize(outCount); if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { @@ -988,148 +918,77 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( } } + if (!outPointerInfo.empty()) { + m_lastPointerTabletData = outPointerInfo.back().tabletData; + } + return GHOST_kSuccess; } -void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state) +void GHOST_WindowWin32::resetPointerPenInfo() { - if (!useTabletAPI(GHOST_kTabletWintab)) { - return; - } - - if (m_wintab.enable && m_wintab.tablet) { - m_wintab.enable(m_wintab.tablet, state); - - if (m_wintab.overlap && state) { - m_wintab.overlap(m_wintab.tablet, TRUE); - } - } + m_lastPointerTabletData = GHOST_TABLET_DATA_NONE; } -bool GHOST_WindowWin32::useTabletAPI(GHOST_TTabletAPI api) const +GHOST_Wintab *GHOST_WindowWin32::getWintab() const { - if (m_system->getTabletAPI() == api) { - return true; - } - else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) { - if (m_wintab.tablet) - return api == GHOST_kTabletWintab; - else - return api == GHOST_kTabletNative; - } - else { - return false; - } + return m_wintab; } -void GHOST_WindowWin32::processWin32TabletInitEvent() +void GHOST_WindowWin32::loadWintab(bool enable) { - if (!useTabletAPI(GHOST_kTabletWintab)) { - return; - } - - // Let's see if we can initialize tablet here - if (m_wintab.info && m_wintab.tablet) { - AXIS Pressure, Orientation[3]; /* The maximum tablet size */ - - BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure); - if (pressureSupport) - m_wintab.maxPressure = Pressure.axMax; - else - m_wintab.maxPressure = 0; - - BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation); - if (tiltSupport) { - /* does the tablet support azimuth ([0]) and altitude ([1]) */ - if (Orientation[0].axResolution && Orientation[1].axResolution) { - m_wintab.maxAzimuth = Orientation[0].axMax; - m_wintab.maxAltitude = Orientation[1].axMax; - } - else { /* No so don't do tilt stuff. */ - m_wintab.maxAzimuth = m_wintab.maxAltitude = 0; + if (!m_wintab) { + if (m_wintab = GHOST_Wintab::loadWintab(m_hWnd)) { + if (enable) { + m_wintab->enable(); + + /* Focus Wintab if cursor is inside this window. This ensures Wintab is enabled when the + * tablet is used to change the Tablet API. */ + GHOST_TInt32 x, y; + if (m_system->getCursorPosition(x, y)) { + GHOST_Rect rect; + getClientBounds(rect); + + if (rect.isInside(x, y)) { + m_wintab->gainFocus(); + } + } } } } +} - m_tabletData.Active = GHOST_kTabletModeNone; +void GHOST_WindowWin32::closeWintab() +{ + delete m_wintab; + m_wintab = NULL; } -void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam) +bool GHOST_WindowWin32::usingTabletAPI(GHOST_TTabletAPI api) const { - if (!useTabletAPI(GHOST_kTabletWintab)) { - return; + if (m_system->getTabletAPI() == api) { + return true; } - - if (m_wintab.packet && m_wintab.tablet) { - PACKET pkt; - if (m_wintab.packet((HCTX)lParam, wParam, &pkt)) { - switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */ - case 0: - m_tabletData.Active = GHOST_kTabletModeNone; /* puck - not yet supported */ - break; - case 1: - m_tabletData.Active = GHOST_kTabletModeStylus; /* stylus */ - break; - case 2: - m_tabletData.Active = GHOST_kTabletModeEraser; /* eraser */ - break; - } - - if (m_wintab.maxPressure > 0) { - m_tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure; - } - else { - m_tabletData.Pressure = 1.0f; - } - - if ((m_wintab.maxAzimuth > 0) && (m_wintab.maxAltitude > 0)) { - ORIENTATION ort = pkt.pkOrientation; - float vecLen; - float altRad, azmRad; /* in radians */ - - /* - * from the wintab spec: - * orAzimuth Specifies the clockwise rotation of the - * cursor about the z axis through a full circular range. - * - * orAltitude Specifies the angle with the x-y plane - * through a signed, semicircular range. Positive values - * specify an angle upward toward the positive z axis; - * negative values specify an angle downward toward the negative z axis. - * - * wintab.h defines .orAltitude as a UINT but documents .orAltitude - * as positive for upward angles and negative for downward angles. - * WACOM uses negative altitude values to show that the pen is inverted; - * therefore we cast .orAltitude as an (int) and then use the absolute value. - */ - - /* convert raw fixed point data to radians */ - altRad = (float)((fabs((float)ort.orAltitude) / (float)m_wintab.maxAltitude) * M_PI / 2.0); - azmRad = (float)(((float)ort.orAzimuth / (float)m_wintab.maxAzimuth) * M_PI * 2.0); - - /* find length of the stylus' projected vector on the XY plane */ - vecLen = cos(altRad); - - /* from there calculate X and Y components based on azimuth */ - m_tabletData.Xtilt = sin(azmRad) * vecLen; - m_tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen); - } - else { - m_tabletData.Xtilt = 0.0f; - m_tabletData.Ytilt = 0.0f; - } + else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) { + if (m_wintab && m_wintab->devicesPresent()) { + return api == GHOST_kTabletWintab; + } + else { + return api == GHOST_kTabletWinPointer; } } + else { + return false; + } } -void GHOST_WindowWin32::bringTabletContextToFront() +GHOST_TabletData GHOST_WindowWin32::getTabletData() { - if (!useTabletAPI(GHOST_kTabletWintab)) { - return; + if (usingTabletAPI(GHOST_kTabletWintab)) { + return m_wintab ? m_wintab->getLastTabletData() : GHOST_TABLET_DATA_NONE; } - - if (m_wintab.overlap && m_wintab.tablet) { - m_wintab.overlap(m_wintab.tablet, TRUE); + else { + return m_lastPointerTabletData; } } diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index a13bd876667..f28ba266ed1 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -30,34 +30,18 @@ #include "GHOST_TaskbarWin32.h" #include "GHOST_Window.h" +#include "GHOST_Wintab.h" #ifdef WITH_INPUT_IME # include "GHOST_ImeWin32.h" #endif #include <vector> -#include <wintab.h> -// PACKETDATA and PACKETMODE modify structs in pktdef.h, so make sure they come first -#define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR) -#define PACKETMODE PK_BUTTONS -#include <pktdef.h> - class GHOST_SystemWin32; class GHOST_DropTargetWin32; -// typedefs for WinTab functions to allow dynamic loading -typedef UINT(API *GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID); -typedef HCTX(API *GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL); -typedef BOOL(API *GHOST_WIN32_WTClose)(HCTX); -typedef BOOL(API *GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID); -typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL); -typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL); - // typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND); -#ifndef USER_DEFAULT_SCREEN_DPI -# define USER_DEFAULT_SCREEN_DPI 96 -#endif // USER_DEFAULT_SCREEN_DPI struct GHOST_PointerInfoWin32 { GHOST_TInt32 pointerId; @@ -65,7 +49,6 @@ struct GHOST_PointerInfoWin32 { GHOST_TButtonMask buttonMask; POINT pixelLocation; GHOST_TUns64 time; - GHOST_TabletData tabletData; }; @@ -259,16 +242,11 @@ class GHOST_WindowWin32 : public GHOST_Window { HCURSOR getStandardCursor(GHOST_TStandardCursor shape) const; void loadCursor(bool visible, GHOST_TStandardCursor cursorShape) const; - const GHOST_TabletData &getTabletData() - { - return m_tabletData; - } - /** * Query whether given tablet API should be used. * \param api: Tablet API to test. */ - bool useTabletAPI(GHOST_TTabletAPI api) const; + bool usingTabletAPI(GHOST_TTabletAPI api) const; /** * Translate WM_POINTER events into GHOST_PointerInfoWin32 structs. @@ -281,10 +259,34 @@ class GHOST_WindowWin32 : public GHOST_Window { WPARAM wParam, LPARAM lParam); - void processWin32TabletActivateEvent(WORD state); - void processWin32TabletInitEvent(); - void processWin32TabletEvent(WPARAM wParam, LPARAM lParam); - void bringTabletContextToFront(); + /** + * Resets pointer pen tablet state. + */ + void resetPointerPenInfo(); + + /** + * Retrieves pointer to Wintab if Wintab is the set Tablet API. + * \return Pointer to Wintab member. + */ + GHOST_Wintab *getWintab() const; + + /** + * Loads Wintab context for the window. + * \param enable: True if Wintab should be enabled after loading. Wintab should not be enabled if + * the window is minimized. + */ + void loadWintab(bool enable); + + /** + * Closes Wintab for the window. + */ + void closeWintab(); + + /** + * Get the most recent Windows Pointer tablet data. + * \return Most recent pointer tablet data. + */ + GHOST_TabletData getTabletData(); GHOST_TSuccess beginFullScreen() const { @@ -298,10 +300,10 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_TUns16 getDPIHint() override; - /** Whether a tablet stylus is being tracked. */ - bool m_tabletInRange; + /** True if the mouse is either over or captured by the window. */ + bool m_mousePresent; - /** if the window currently resizing */ + /** True if the window currently resizing. */ bool m_inLiveResize; #ifdef WITH_INPUT_IME @@ -385,27 +387,11 @@ class GHOST_WindowWin32 : public GHOST_Window { static const wchar_t *s_windowClassName; static const int s_maxTitleLength; - /** Tablet data for GHOST */ - GHOST_TabletData m_tabletData; - - /* Wintab API */ - struct { - /** `WinTab.dll` handle. */ - HMODULE handle = NULL; - - /** API functions */ - GHOST_WIN32_WTInfo info; - GHOST_WIN32_WTOpen open; - GHOST_WIN32_WTClose close; - GHOST_WIN32_WTPacket packet; - GHOST_WIN32_WTEnable enable; - GHOST_WIN32_WTOverlap overlap; - - /** Stores the Tablet context if detected Tablet features using `WinTab.dll` */ - HCTX tablet; - LONG maxPressure; - LONG maxAzimuth, maxAltitude; - } m_wintab; + /** Pointer to Wintab manager if Wintab is loaded. */ + GHOST_Wintab *m_wintab; + + /** Most recent tablet data. */ + GHOST_TabletData m_lastPointerTabletData; GHOST_TWindowState m_normal_state; diff --git a/intern/ghost/intern/GHOST_Wintab.cpp b/intern/ghost/intern/GHOST_Wintab.cpp new file mode 100644 index 00000000000..cf0309b1521 --- /dev/null +++ b/intern/ghost/intern/GHOST_Wintab.cpp @@ -0,0 +1,491 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup GHOST + */ + +#define _USE_MATH_DEFINES + +#include "GHOST_Wintab.h" + +GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd) +{ + /* Load Wintab library if available. */ + + auto handle = unique_hmodule(::LoadLibrary("Wintab32.dll"), &::FreeLibrary); + if (!handle) { + return nullptr; + } + + /* Get Wintab functions. */ + + auto info = (GHOST_WIN32_WTInfo)::GetProcAddress(handle.get(), "WTInfoA"); + if (!info) { + return nullptr; + } + + auto open = (GHOST_WIN32_WTOpen)::GetProcAddress(handle.get(), "WTOpenA"); + if (!open) { + return nullptr; + } + + auto get = (GHOST_WIN32_WTGet)::GetProcAddress(handle.get(), "WTGetA"); + if (!get) { + return nullptr; + } + + auto set = (GHOST_WIN32_WTSet)::GetProcAddress(handle.get(), "WTSetA"); + if (!set) { + return nullptr; + } + + auto close = (GHOST_WIN32_WTClose)::GetProcAddress(handle.get(), "WTClose"); + if (!close) { + return nullptr; + } + + auto packetsGet = (GHOST_WIN32_WTPacketsGet)::GetProcAddress(handle.get(), "WTPacketsGet"); + if (!packetsGet) { + return nullptr; + } + + auto queueSizeGet = (GHOST_WIN32_WTQueueSizeGet)::GetProcAddress(handle.get(), "WTQueueSizeGet"); + if (!queueSizeGet) { + return nullptr; + } + + auto queueSizeSet = (GHOST_WIN32_WTQueueSizeSet)::GetProcAddress(handle.get(), "WTQueueSizeSet"); + if (!queueSizeSet) { + return nullptr; + } + + auto enable = (GHOST_WIN32_WTEnable)::GetProcAddress(handle.get(), "WTEnable"); + if (!enable) { + return nullptr; + } + + auto overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(handle.get(), "WTOverlap"); + if (!overlap) { + return nullptr; + } + + /* Build Wintab context. */ + + LOGCONTEXT lc = {0}; + if (!info(WTI_DEFSYSCTX, 0, &lc)) { + return nullptr; + } + + Coord tablet, system; + extractCoordinates(lc, tablet, system); + modifyContext(lc); + + /* The Wintab spec says we must open the context disabled if we are using cursor masks. */ + auto hctx = unique_hctx(open(hwnd, &lc, FALSE), close); + if (!hctx) { + return nullptr; + } + + /* Wintab provides no way to determine the maximum queue size aside from checking if attempts + * to change the queue size are successful. */ + const int maxQueue = 500; + int queueSize = queueSizeGet(hctx.get()); + + while (queueSize < maxQueue) { + int testSize = min(queueSize + 16, maxQueue); + if (queueSizeSet(hctx.get(), testSize)) { + queueSize = testSize; + } + else { + /* From Windows Wintab Documentation for WTQueueSizeSet: + * "If the return value is zero, the context has no queue because the function deletes the + * original queue before attempting to create a new one. The application must continue + * calling the function with a smaller queue size until the function returns a non - zero + * value." + * + * In our case we start with a known valid queue size and in the event of failure roll + * back to the last valid queue size. The Wintab spec dates back to 16 bit Windows, thus + * assumes memory recently deallocated may not be available, which is no longer a practical + * concern. */ + if (!queueSizeSet(hctx.get(), queueSize)) { + /* If a previously valid queue size is no longer valid, there is likely something wrong in + * the Wintab implementation and we should not use it. */ + return nullptr; + } + break; + } + } + + return new GHOST_Wintab(hwnd, + std::move(handle), + info, + get, + set, + packetsGet, + enable, + overlap, + std::move(hctx), + tablet, + system, + queueSize); +} + +void GHOST_Wintab::modifyContext(LOGCONTEXT &lc) +{ + lc.lcPktData = PACKETDATA; + lc.lcPktMode = PACKETMODE; + lc.lcMoveMask = PACKETDATA; + lc.lcOptions |= CXO_CSRMESSAGES | CXO_MESSAGES; + + /* Tablet scaling is handled manually because some drivers don't handle HIDPI or multi-display + * correctly; reset tablet scale factors to un-scaled tablet coordinates. */ + lc.lcOutOrgX = lc.lcInOrgX; + lc.lcOutOrgY = lc.lcInOrgY; + lc.lcOutExtX = lc.lcInExtX; + lc.lcOutExtY = lc.lcInExtY; +} + +void GHOST_Wintab::extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system) +{ + tablet.x.org = lc.lcInOrgX; + tablet.x.ext = lc.lcInExtX; + tablet.y.org = lc.lcInOrgY; + tablet.y.ext = lc.lcInExtY; + + system.x.org = lc.lcSysOrgX; + system.x.ext = lc.lcSysExtX; + system.y.org = lc.lcSysOrgY; + /* Wintab maps y origin to the tablet's bottom; invert y to match Windows y origin mapping to the + * screen top. */ + system.y.ext = -lc.lcSysExtY; +} + +GHOST_Wintab::GHOST_Wintab(HWND hwnd, + unique_hmodule handle, + GHOST_WIN32_WTInfo info, + GHOST_WIN32_WTGet get, + GHOST_WIN32_WTSet set, + GHOST_WIN32_WTPacketsGet packetsGet, + GHOST_WIN32_WTEnable enable, + GHOST_WIN32_WTOverlap overlap, + unique_hctx hctx, + Coord tablet, + Coord system, + int queueSize) + : m_handle{std::move(handle)}, + m_fpInfo{info}, + m_fpGet{get}, + m_fpSet{set}, + m_fpPacketsGet{packetsGet}, + m_fpEnable{enable}, + m_fpOverlap{overlap}, + m_context{std::move(hctx)}, + m_tabletCoord{tablet}, + m_systemCoord{system}, + m_pkts{queueSize} +{ + m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices); + updateCursorInfo(); +} + +void GHOST_Wintab::enable() +{ + m_fpEnable(m_context.get(), true); + m_enabled = true; +} + +void GHOST_Wintab::disable() +{ + if (m_focused) { + loseFocus(); + } + m_fpEnable(m_context.get(), false); + m_enabled = false; +} + +void GHOST_Wintab::gainFocus() +{ + m_fpOverlap(m_context.get(), true); + m_focused = true; +} + +void GHOST_Wintab::loseFocus() +{ + if (m_lastTabletData.Active != GHOST_kTabletModeNone) { + leaveRange(); + } + + /* Mouse mode of tablet or display layout may change when Wintab or Window is inactive. Don't + * trust for mouse movement until re-verified. */ + m_coordTrusted = false; + + m_fpOverlap(m_context.get(), false); + m_focused = false; +} + +void GHOST_Wintab::leaveRange() +{ + /* Button state can't be tracked while out of range, reset it. */ + m_buttons = 0; + /* Set to none to indicate tablet is inactive. */ + m_lastTabletData = GHOST_TABLET_DATA_NONE; + /* Clear the packet queue. */ + m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data()); +} + +void GHOST_Wintab::remapCoordinates() +{ + LOGCONTEXT lc = {0}; + + if (m_fpInfo(WTI_DEFSYSCTX, 0, &lc)) { + extractCoordinates(lc, m_tabletCoord, m_systemCoord); + modifyContext(lc); + + m_fpSet(m_context.get(), &lc); + } +} + +void GHOST_Wintab::updateCursorInfo() +{ + AXIS Pressure, Orientation[3]; + + BOOL pressureSupport = m_fpInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure); + m_maxPressure = pressureSupport ? Pressure.axMax : 0; + + BOOL tiltSupport = m_fpInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation); + /* Check if tablet supports azimuth [0] and altitude [1], encoded in axResolution. */ + if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) { + m_maxAzimuth = Orientation[0].axMax; + m_maxAltitude = Orientation[1].axMax; + } + else { + m_maxAzimuth = m_maxAltitude = 0; + } +} + +void GHOST_Wintab::processInfoChange(LPARAM lParam) +{ + /* Update number of connected Wintab digitizers. */ + if (LOWORD(lParam) == WTI_INTERFACE && HIWORD(lParam) == IFC_NDEVICES) { + m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices); + } +} + +bool GHOST_Wintab::devicesPresent() +{ + return m_numDevices > 0; +} + +GHOST_TabletData GHOST_Wintab::getLastTabletData() +{ + return m_lastTabletData; +} + +void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo) +{ + const int numPackets = m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data()); + outWintabInfo.resize(numPackets); + size_t outExtent = 0; + + for (int i = 0; i < numPackets; i++) { + PACKET pkt = m_pkts[i]; + GHOST_WintabInfoWin32 &out = outWintabInfo[i + outExtent]; + + out.tabletData = GHOST_TABLET_DATA_NONE; + /* % 3 for multiple devices ("DualTrack"). */ + switch (pkt.pkCursor % 3) { + case 0: + /* Puck - processed as mouse. */ + out.tabletData.Active = GHOST_kTabletModeNone; + break; + case 1: + out.tabletData.Active = GHOST_kTabletModeStylus; + break; + case 2: + out.tabletData.Active = GHOST_kTabletModeEraser; + break; + } + + out.x = pkt.pkX; + out.y = pkt.pkY; + + if (m_maxPressure > 0) { + out.tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure; + } + + if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) { + ORIENTATION ort = pkt.pkOrientation; + float vecLen; + float altRad, azmRad; /* In radians. */ + + /* + * From the wintab spec: + * orAzimuth: Specifies the clockwise rotation of the cursor about the z axis through a + * full circular range. + * orAltitude: Specifies the angle with the x-y plane through a signed, semicircular range. + * Positive values specify an angle upward toward the positive z axis; negative values + * specify an angle downward toward the negative z axis. + * + * wintab.h defines orAltitude as a UINT but documents orAltitude as positive for upward + * angles and negative for downward angles. WACOM uses negative altitude values to show that + * the pen is inverted; therefore we cast orAltitude as an (int) and then use the absolute + * value. + */ + + /* Convert raw fixed point data to radians. */ + altRad = (float)((fabs((float)ort.orAltitude) / (float)m_maxAltitude) * M_PI / 2.0); + azmRad = (float)(((float)ort.orAzimuth / (float)m_maxAzimuth) * M_PI * 2.0); + + /* Find length of the stylus' projected vector on the XY plane. */ + vecLen = cos(altRad); + + /* From there calculate X and Y components based on azimuth. */ + out.tabletData.Xtilt = sin(azmRad) * vecLen; + out.tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen); + } + + out.time = pkt.pkTime; + + /* Some Wintab libraries don't handle relative button input, so we track button presses + * manually. */ + out.button = GHOST_kButtonMaskNone; + out.type = GHOST_kEventCursorMove; + + DWORD buttonsChanged = m_buttons ^ pkt.pkButtons; + WORD buttonIndex = 0; + GHOST_WintabInfoWin32 buttonRef = out; + int buttons = 0; + + while (buttonsChanged) { + if (buttonsChanged & 1) { + /* Find the index for the changed button from the button map. */ + GHOST_TButtonMask button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex); + + if (button != GHOST_kButtonMaskNone) { + /* Extend output if multiple buttons are pressed. We don't extend input until we confirm + * a Wintab buttons maps to a system button. */ + if (buttons > 0) { + outWintabInfo.resize(outWintabInfo.size() + 1); + outExtent++; + GHOST_WintabInfoWin32 &out = outWintabInfo[i + outExtent]; + out = buttonRef; + } + buttons++; + + out.button = button; + if (buttonsChanged & pkt.pkButtons) { + out.type = GHOST_kEventButtonDown; + } + else { + out.type = GHOST_kEventButtonUp; + } + } + + m_buttons ^= 1 << buttonIndex; + } + + buttonsChanged >>= 1; + buttonIndex++; + } + } + + if (!outWintabInfo.empty()) { + m_lastTabletData = outWintabInfo.back().tabletData; + } +} + +GHOST_TButtonMask GHOST_Wintab::mapWintabToGhostButton(UINT cursor, WORD physicalButton) +{ + const WORD numButtons = 32; + BYTE logicalButtons[numButtons] = {0}; + BYTE systemButtons[numButtons] = {0}; + + if (!m_fpInfo(WTI_CURSORS + cursor, CSR_BUTTONMAP, &logicalButtons) || + !m_fpInfo(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &systemButtons)) { + return GHOST_kButtonMaskNone; + } + + if (physicalButton >= numButtons) { + return GHOST_kButtonMaskNone; + } + + BYTE lb = logicalButtons[physicalButton]; + + if (lb >= numButtons) { + return GHOST_kButtonMaskNone; + } + + switch (systemButtons[lb]) { + case SBN_LCLICK: + return GHOST_kButtonMaskLeft; + case SBN_RCLICK: + return GHOST_kButtonMaskRight; + case SBN_MCLICK: + return GHOST_kButtonMaskMiddle; + default: + return GHOST_kButtonMaskNone; + } +} + +void GHOST_Wintab::mapWintabToSysCoordinates(int x_in, int y_in, int &x_out, int &y_out) +{ + /* Maps from range [in.org, in.org + abs(in.ext)] to [out.org, out.org + abs(out.ext)], in + * reverse if in.ext and out.ext have differing sign. */ + auto remap = [](int inPoint, Range in, Range out) -> int { + int absInExt = abs(in.ext); + int absOutExt = abs(out.ext); + + /* Translate input from range [in.org, in.org + absInExt] to [0, absInExt] */ + int inMagnitude = inPoint - in.org; + + /* If signs of extents differ, reverse input over range. */ + if ((in.ext < 0) != (out.ext < 0)) { + inMagnitude = absInExt - inMagnitude; + } + + /* Scale from [0, absInExt] to [0, absOutExt]. */ + int outMagnitude = inMagnitude * absOutExt / absInExt; + + /* Translate from range [0, absOutExt] to [out.org, out.org + absOutExt]. */ + int outPoint = outMagnitude + out.org; + + return outPoint; + }; + + x_out = remap(x_in, m_tabletCoord.x, m_systemCoord.x); + y_out = remap(y_in, m_tabletCoord.y, m_systemCoord.y); +} + +bool GHOST_Wintab::trustCoordinates() +{ + return m_coordTrusted; +} + +bool GHOST_Wintab::testCoordinates(int sysX, int sysY, int wtX, int wtY) +{ + mapWintabToSysCoordinates(wtX, wtY, wtX, wtY); + + /* Allow off by one pixel tolerance in case of rounding error. */ + if (abs(sysX - wtX) <= 1 && abs(sysY - wtY) <= 1) { + m_coordTrusted = true; + return true; + } + else { + m_coordTrusted = false; + return false; + } +} diff --git a/intern/ghost/intern/GHOST_Wintab.h b/intern/ghost/intern/GHOST_Wintab.h new file mode 100644 index 00000000000..75017aa67d9 --- /dev/null +++ b/intern/ghost/intern/GHOST_Wintab.h @@ -0,0 +1,250 @@ +/* + * 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_WintabWin32 class. + */ + +/* Wacom's Wintab documentation is periodically offline, moved, and increasingly hidden away. You + * can find a (painstakingly) archived copy of the documentation at + * https://web.archive.org/web/20201122230125/https://developer-docs-legacy.wacom.com/display/DevDocs/Windows+Wintab+Documentation + */ + +#pragma once + +#include <memory> +#include <vector> +#include <wtypes.h> + +#include "GHOST_Types.h" + +#include <wintab.h> +/* PACKETDATA and PACKETMODE modify structs in pktdef.h, so make sure they come first. */ +#define PACKETDATA \ + (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_X | PK_Y | PK_TIME) +#define PACKETMODE 0 +#include <pktdef.h> + +/* Typedefs for Wintab functions to allow dynamic loading. */ +typedef UINT(API *GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID); +typedef BOOL(API *GHOST_WIN32_WTGet)(HCTX, LPLOGCONTEXTA); +typedef BOOL(API *GHOST_WIN32_WTSet)(HCTX, LPLOGCONTEXTA); +typedef HCTX(API *GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL); +typedef BOOL(API *GHOST_WIN32_WTClose)(HCTX); +typedef int(API *GHOST_WIN32_WTPacketsGet)(HCTX, int, LPVOID); +typedef int(API *GHOST_WIN32_WTQueueSizeGet)(HCTX); +typedef BOOL(API *GHOST_WIN32_WTQueueSizeSet)(HCTX, int); +typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL); +typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL); + +/* Typedefs for Wintab and Windows resource management. */ +typedef std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&::FreeLibrary)> unique_hmodule; +typedef std::unique_ptr<std::remove_pointer_t<HCTX>, GHOST_WIN32_WTClose> unique_hctx; + +struct GHOST_WintabInfoWin32 { + GHOST_TInt32 x, y; + GHOST_TEventType type; + GHOST_TButtonMask button; + GHOST_TUns64 time; + GHOST_TabletData tabletData; +}; + +class GHOST_Wintab { + public: + /** + * Loads Wintab if available. + * \param hwnd: Window to attach Wintab context to. + */ + static GHOST_Wintab *loadWintab(HWND hwnd); + + /** + * Enables Wintab context. + */ + void enable(); + + /** + * Disables the Wintab context and unwinds Wintab state. + */ + void disable(); + + /** + * Brings Wintab context to the top of the overlap order. + */ + void gainFocus(); + + /** + * Puts Wintab context at bottom of overlap order and unwinds Wintab state. + */ + void loseFocus(); + + /** + * Clean up when Wintab leaves tracking range. + */ + void leaveRange(); + + /** + * Handle Wintab coordinate changes when DisplayChange events occur. + */ + void remapCoordinates(); + + /** + * Maps Wintab to Win32 display coordinates. + * \param x_in: The tablet x coordinate. + * \param y_in: The tablet y coordinate. + * \param x_out: Output for the Win32 mapped x coordinate. + * \param y_out: Output for the Win32 mapped y coordinate. + */ + void mapWintabToSysCoordinates(int x_in, int y_in, int &x_out, int &y_out); + + /** + * Updates cached Wintab properties for current cursor. + */ + void updateCursorInfo(); + + /** + * Handle Wintab info changes such as change in number of connected tablets. + * \param lParam: LPARAM of the event. + */ + void processInfoChange(LPARAM lParam); + + /** + * Whether Wintab devices are present. + * \return True if Wintab devices are present. + */ + bool devicesPresent(); + + /** + * Translate Wintab packets into GHOST_WintabInfoWin32 structs. + * \param outWintabInfo: Storage to return resulting GHOST_WintabInfoWin32 data. + */ + void getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo); + + /** + * Whether Wintab coordinates should be trusted. + * \return True if Wintab coordinates should be trusted. + */ + bool trustCoordinates(); + + /** + * Tests whether Wintab coordinates can be trusted by comparing Win32 and Wintab reported cursor + * position. + * \param sysX: System cursor x position. + * \param sysY: System cursor y position. + * \param wtX: Wintab cursor x position. + * \param wtY: Wintab cursor y position. + * \return True if Win32 and Wintab cursor positions match within tolerance. + * + * Note: Only test coordinates on button press, not release. This prevents issues when async + * mismatch causes mouse movement to replay and snap back, which is only an issue while drawing. + */ + bool testCoordinates(int sysX, int sysY, int wtX, int wtY); + + /** + * Retrieve the most recent tablet data, or none if pen is not in range. + * \return Most recent tablet data, or none if pen is not in range. + */ + GHOST_TabletData getLastTabletData(); + + private: + /** Wintab DLL handle. */ + unique_hmodule m_handle; + /** Wintab API functions. */ + GHOST_WIN32_WTInfo m_fpInfo = nullptr; + GHOST_WIN32_WTGet m_fpGet = nullptr; + GHOST_WIN32_WTSet m_fpSet = nullptr; + GHOST_WIN32_WTPacketsGet m_fpPacketsGet = nullptr; + GHOST_WIN32_WTEnable m_fpEnable = nullptr; + GHOST_WIN32_WTOverlap m_fpOverlap = nullptr; + + /** Stores the Wintab tablet context. */ + unique_hctx m_context; + /** Whether the context is enabled. */ + bool m_enabled = false; + /** Whether the context has focus and is at the top of overlap order. */ + bool m_focused = false; + + /** Pressed button map. */ + GHOST_TUns8 m_buttons = 0; + + /** Range of a coordinate space. */ + struct Range { + /** Origin of range. */ + int org = 0; + /** Extent of range. */ + int ext = 1; + }; + + /** 2D Coordinate space. */ + struct Coord { + /** Range of x. */ + Range x = {}; + /** Range of y. */ + Range y = {}; + }; + /** Whether Wintab coordinates are trusted. */ + bool m_coordTrusted = false; + /** Tablet input range. */ + Coord m_tabletCoord = {}; + /** System output range. */ + Coord m_systemCoord = {}; + + int m_maxPressure = 0; + int m_maxAzimuth = 0; + int m_maxAltitude = 0; + + /** Number of connected Wintab devices. */ + UINT m_numDevices = 0; + /** Reusable buffer to read in Wintab packets. */ + std::vector<PACKET> m_pkts; + /** Most recently received tablet data, or none if pen is not in range. */ + GHOST_TabletData m_lastTabletData = GHOST_TABLET_DATA_NONE; + + GHOST_Wintab(HWND hwnd, + unique_hmodule handle, + GHOST_WIN32_WTInfo info, + GHOST_WIN32_WTGet get, + GHOST_WIN32_WTSet set, + GHOST_WIN32_WTPacketsGet packetsGet, + GHOST_WIN32_WTEnable enable, + GHOST_WIN32_WTOverlap overlap, + unique_hctx hctx, + Coord tablet, + Coord system, + int queueSize); + + /** + * Convert Wintab system mapped (mouse) buttons into Ghost button mask. + * \param cursor: The Wintab cursor associated to the button. + * \param physicalButton: The physical button ID to inspect. + * \return The system mapped button. + */ + GHOST_TButtonMask mapWintabToGhostButton(UINT cursor, WORD physicalButton); + + /** + * Applies common modifications to Wintab context. + * \param lc: Wintab context to modify. + */ + static void modifyContext(LOGCONTEXT &lc); + + /** + * Extracts tablet and system coordinates from Wintab context. + * \param lc: Wintab context to extract coordinates from. + * \param tablet: Tablet coordinates. + * \param system: System coordinates. + */ + static void extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system); +}; diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp index 172ac40c84f..b10e001df47 100644 --- a/intern/ghost/intern/GHOST_XrAction.cpp +++ b/intern/ghost/intern/GHOST_XrAction.cpp @@ -392,9 +392,10 @@ GHOST_XrActionSet::GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSe { XrActionSetCreateInfo action_set_info{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(action_set_info.actionSetName, info.name); - strcpy(action_set_info.localizedActionSetName, - info.name); /* Just use same name for localized. This can be changed in the future if - necessary. */ + + /* Just use same name for localized. This can be changed in the future if necessary. */ + strcpy(action_set_info.localizedActionSetName, info.name); + action_set_info.priority = 0; /* Use same (default) priority for all action sets. */ CHECK_XR(xrCreateActionSet(instance, &action_set_info, &m_action_set), diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index daad0b8190a..f057e679d56 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -420,6 +420,11 @@ void GHOST_XrContext::getExtensionsToEnable( r_ext_names.push_back(gpu_binding); } +#if defined(WITH_GL_EGL) + assert(openxr_extension_is_available(m_oxr->extensions, XR_MNDX_EGL_ENABLE_EXTENSION_NAME)); + r_ext_names.push_back(XR_MNDX_EGL_ENABLE_EXTENSION_NAME); +#endif + for (const std::string_view &ext : try_ext) { if (openxr_extension_is_available(m_oxr->extensions, ext)) { r_ext_names.push_back(ext.data()); diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp index 30a2a238ccd..aeaa6e6b9e0 100644 --- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -22,7 +22,9 @@ #include <list> #include <sstream> -#if defined(WITH_GHOST_X11) +#if defined(WITH_GL_EGL) +# include "GHOST_ContextEGL.h" +#elif defined(WITH_GHOST_X11) # include "GHOST_ContextGLX.h" #elif defined(WIN32) # include "GHOST_ContextD3D.h" @@ -66,7 +68,9 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { XrSystemId system_id, std::string *r_requirement_info) const override { -#if defined(WITH_GHOST_X11) +#if defined(WITH_GL_EGL) + GHOST_ContextEGL &ctx_gl = static_cast<GHOST_ContextEGL &>(ghost_ctx); +#elif 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); @@ -106,6 +110,15 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { void initFromGhostContext(GHOST_Context &ghost_ctx) override { #if defined(WITH_GHOST_X11) +# if defined(WITH_GL_EGL) + GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx); + + oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX; + oxr_binding.egl.getProcAddress = eglGetProcAddress; + oxr_binding.egl.display = ctx_egl.getDisplay(); + oxr_binding.egl.config = ctx_egl.getConfig(); + oxr_binding.egl.context = ctx_egl.getContext(); +# else GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx); XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig); @@ -117,6 +130,7 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { oxr_binding.glx.visualid = visual_info->visualid; XFree(visual_info); +# endif #elif defined(WIN32) GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx); diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index d09c78e1ea7..a2d3cf2e385 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -74,10 +74,11 @@ class GHOST_XrSession { const GHOST_XrActionProfileInfo *infos); bool attachActionSets(); - /** Action functions to be called post-session start. */ - bool syncActions( - const char *action_set_name = nullptr); /* If action_set_name is nullptr, all attached - * action sets will be synced. */ + /** + * Action functions to be called post-session start. + * \param action_set_name: When `nullptr`, all attached action sets will be synced. + */ + bool syncActions(const char *action_set_name = nullptr); bool applyHapticAction(const char *action_set_name, const char *action_name, const GHOST_TInt64 &duration, diff --git a/intern/ghost/intern/GHOST_Xr_openxr_includes.h b/intern/ghost/intern/GHOST_Xr_openxr_includes.h index d1deaeb0d1a..d6d7cad866c 100644 --- a/intern/ghost/intern/GHOST_Xr_openxr_includes.h +++ b/intern/ghost/intern/GHOST_Xr_openxr_includes.h @@ -42,7 +42,13 @@ # include <d3d12.h> #endif #ifdef WITH_GHOST_X11 -# include <GL/glxew.h> +# ifdef WITH_GL_EGL +/* TODO: Why do we have to create this typedef manually? */ +typedef void (*(*PFNEGLGETPROCADDRESSPROC)(const char *procname))(void); +# include <GL/eglew.h> +# else +# include <GL/glxew.h> +# endif #endif #include <openxr/openxr.h> diff --git a/intern/iksolver/intern/IK_QJacobianSolver.cpp b/intern/iksolver/intern/IK_QJacobianSolver.cpp index 6c2c0bacf48..b8ad0b2c09c 100644 --- a/intern/iksolver/intern/IK_QJacobianSolver.cpp +++ b/intern/iksolver/intern/IK_QJacobianSolver.cpp @@ -89,7 +89,7 @@ bool IK_QJacobianSolver::Setup(IK_QSegment *root, std::list<IK_QTask *> &tasks) if (num_dof == 0) return false; - // compute task id's and assing weights to task + // compute task ids and assign weights to task int primary_size = 0, primary = 0; int secondary_size = 0, secondary = 0; double primary_weight = 0.0, secondary_weight = 0.0; @@ -237,7 +237,7 @@ void IK_QJacobianSolver::ConstrainPoleVector(IK_QSegment *root, std::list<IK_QTa bool IK_QJacobianSolver::UpdateAngles(double &norm) { - // assing each segment a unique id for the jacobian + // assign each segment a unique id for the jacobian std::vector<IK_QSegment *>::iterator seg; IK_QSegment *qseg, *minseg = NULL; double minabsdelta = 1e10, absdelta; diff --git a/intern/libmv/libmv/base/scoped_ptr.h b/intern/libmv/libmv/base/scoped_ptr.h index 9bfcfe1d615..45b5722034c 100644 --- a/intern/libmv/libmv/base/scoped_ptr.h +++ b/intern/libmv/libmv/base/scoped_ptr.h @@ -77,7 +77,7 @@ class scoped_array { void reset(T* new_array) { if (sizeof(T)) { - delete array_; + delete[] array_; } array_ = new_array; } diff --git a/intern/opencolorio/CMakeLists.txt b/intern/opencolorio/CMakeLists.txt index d2336692d22..0b46ae471d2 100644 --- a/intern/opencolorio/CMakeLists.txt +++ b/intern/opencolorio/CMakeLists.txt @@ -48,6 +48,7 @@ if(WITH_OPENCOLORIO) ) add_definitions(${GL_DEFINITIONS}) + add_definitions(${OPENCOLORIO_DEFINITIONS}) list(APPEND INC_SYS ${OPENCOLORIO_INCLUDE_DIRS} @@ -67,9 +68,6 @@ if(WITH_OPENCOLORIO) list(APPEND INC_SYS ${BOOST_INCLUDE_DIR} ) - add_definitions( - -DOpenColorIO_STATIC - ) list(APPEND LIB ${BOOST_LIBRARIES} ) diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 5ab29b1331d2103dae634b987f121c4599459d7 +Subproject ab283053ab455f76f5620b59b823e73bd9f601c diff --git a/release/scripts/addons b/release/scripts/addons -Subproject cdabac54c4fe7c6f8df125814442762aa539172 +Subproject ec07ed4c2e0495bea7fbe0b546d25e35211506a diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py index 239dd1cf79b..73b978d7d2d 100644 --- a/release/scripts/modules/addon_utils.py +++ b/release/scripts/modules/addon_utils.py @@ -358,7 +358,8 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non except Exception as ex: # if the addon doesn't exist, don't print full traceback if type(ex) is ImportError and ex.name == module_name: - print("addon not found:", repr(module_name)) + print("addon not loaded:", repr(module_name)) + print("cause:", str(ex)) else: handle_error(ex) diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py index b17c9eeebc5..f63b6990cdd 100644 --- a/release/scripts/modules/bl_i18n_utils/utils.py +++ b/release/scripts/modules/bl_i18n_utils/utils.py @@ -1572,7 +1572,7 @@ class I18n: if not os.path.isfile(dst): print("WARNING: trying to write as python code into {}, which is not a file! Aborting.".format(dst)) return - prev, txt, nxt, has_trans = self._parser_check_file(dst) + prev, txt, nxt, _has_trans = self._parser_check_file(dst) if prev is None and nxt is None: print("WARNING: Looks like given python file {} has no auto-generated translations yet, will be added " "at the end of the file, you can move that section later if needed...".format(dst)) diff --git a/release/scripts/modules/bl_i18n_utils/utils_cli.py b/release/scripts/modules/bl_i18n_utils/utils_cli.py index 76dd8a740c5..d3750d5e9a4 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_cli.py +++ b/release/scripts/modules/bl_i18n_utils/utils_cli.py @@ -71,7 +71,7 @@ def rtl_process_po(args, settings): po.write(kind="PO", dest=args.dst) -def language_menu(args, settings): +def language_menu(_args, settings): # 'DEFAULT' and en_US are always valid, fully-translated "languages"! stats = {"DEFAULT": 1.0, "en_US": 1.0} diff --git a/release/scripts/modules/bl_i18n_utils/utils_rtl.py b/release/scripts/modules/bl_i18n_utils/utils_rtl.py index 8da1417c468..4b874efa2da 100755 --- a/release/scripts/modules/bl_i18n_utils/utils_rtl.py +++ b/release/scripts/modules/bl_i18n_utils/utils_rtl.py @@ -84,10 +84,10 @@ def protect_format_seq(msg): # LRM = "\u200E" # RLM = "\u200F" LRE = "\u202A" - RLE = "\u202B" +# RLE = "\u202B" PDF = "\u202C" LRO = "\u202D" - RLO = "\u202E" +# RLO = "\u202E" # uctrl = {LRE, RLE, PDF, LRO, RLO} # Most likely incomplete, but seems to cover current needs. format_codes = set("tslfd") diff --git a/release/scripts/modules/bpy_extras/anim_utils.py b/release/scripts/modules/bpy_extras/anim_utils.py index 0868b772a2b..dd19bb8f975 100644 --- a/release/scripts/modules/bpy_extras/anim_utils.py +++ b/release/scripts/modules/bpy_extras/anim_utils.py @@ -156,10 +156,9 @@ def bake_action_iter( # Note: BBONE_PROPS is a list so we can preserve the ordering BBONE_PROPS = [ 'bbone_curveinx', 'bbone_curveoutx', - 'bbone_curveiny', 'bbone_curveouty', + 'bbone_curveinz', 'bbone_curveoutz', 'bbone_rollin', 'bbone_rollout', - 'bbone_scaleinx', 'bbone_scaleoutx', - 'bbone_scaleiny', 'bbone_scaleouty', + 'bbone_scalein', 'bbone_scaleout', 'bbone_easein', 'bbone_easeout' ] diff --git a/release/scripts/modules/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py index b7a15bbbc19..de0b1435803 100644 --- a/release/scripts/modules/keyingsets_utils.py +++ b/release/scripts/modules/keyingsets_utils.py @@ -240,7 +240,7 @@ def RKS_GEN_custom_props(_ksi, _context, ks, data): prop_path = '["%s"]' % bpy.utils.escape_identifier(cprop_name) try: rna_property = data.path_resolve(prop_path, False) - except ValueError as ex: + except ValueError: # This happens when a custom property is set to None. In that case it cannot # be converted to an FCurve-compatible value, so we can't keyframe it anyway. continue diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py index 365d6c0087f..b42539ac44a 100644 --- a/release/scripts/modules/rna_keymap_ui.py +++ b/release/scripts/modules/rna_keymap_ui.py @@ -271,7 +271,7 @@ def draw_filtered(display_keymaps, filter_type, filter_text, layout): # keymap items must match against all. kmi_test_type = [] - # initialize? - so if a if a kmi has a MOD assigned it wont show up. + # initialize? - so if a kmi has a MOD assigned it won't show up. # for kv in key_mod.values(): # kmi_test_dict[kv] = {False} diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 552f7eeb4f3..57fcd6f9752 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -15,16 +15,21 @@ if language == 'DEFAULT': language = os.getenv('LANG', '').split('.')[0] LANG = { +"ar_EG": "ar", "de_DE": "de", -"ru_RU": "ru", -"uk_UA": "uk", "es": "es", +"fi_FI": "fi", "fr_FR": "fr", +"id_ID": "id", "it_IT": "it", "ja_JP": "ja", "ko_KR": "ko", "pt_PT": "pt", "pt_BR": "pt", +"ru_RU": "ru", +"sk_SK": "sk", +"sr_RS": "sr", +"uk_UA": "uk", "vi_VN": "vi", "zh_CN": "zh-hans", "zh_TW": "zh-hant", @@ -58,6 +63,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"), ("bpy.types.fluiddomainsettings.use_collision_border_bottom*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-bottom"), ("bpy.types.fluiddomainsettings.vector_scale_with_magnitude*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-scale-with-magnitude"), + ("bpy.types.movietrackingstabilization.use_2d_stabilization*", "movie_clip/tracking/clip/sidebar/stabilization/panel.html#bpy-types-movietrackingstabilization-use-2d-stabilization"), ("bpy.types.spacespreadsheet.display_context_path_collapsed*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-display-context-path-collapsed"), ("bpy.types.fluiddomainsettings.use_collision_border_front*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-front"), ("bpy.types.fluiddomainsettings.use_collision_border_right*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-right"), @@ -169,6 +175,7 @@ url_manual_mapping = ( ("bpy.types.linestylethicknessmodifier_calligraphy*", "render/freestyle/parameter_editor/line_style/modifiers/thickness/calligraphy.html#bpy-types-linestylethicknessmodifier-calligraphy"), ("bpy.types.rendersettings_simplify_gpencil_onplay*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-onplay"), ("bpy.types.rigidbodyconstraint.breaking_threshold*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-breaking-threshold"), + ("bpy.types.spaceclipeditor.use_manual_calibration*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-manual-calibration"), ("bpy.types.spacedopesheeteditor.show_pose_markers*", "animation/markers.html#bpy-types-spacedopesheeteditor-show-pose-markers"), ("bpy.types.spaceoutliner.use_filter_object_camera*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-camera"), ("bpy.types.spaceoutliner.use_filter_object_others*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-others"), @@ -180,6 +187,7 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.sculpt_mode_mask_opacity*", "sculpt_paint/sculpting/editing/mask.html#bpy-types-view3doverlay-sculpt-mode-mask-opacity"), ("bpy.ops.outliner.collection_indirect_only_clear*", "render/layers/introduction.html#bpy-ops-outliner-collection-indirect-only-clear"), ("bpy.types.cyclesrendersettings.max_subdivisions*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-max-subdivisions"), + ("bpy.types.editbone.bbone_handle_use_scale_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-start"), ("bpy.types.fluiddomainsettings.cache_data_format*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-data-format"), ("bpy.types.fluiddomainsettings.cache_frame_start*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-start"), ("bpy.types.fluiddomainsettings.cache_mesh_format*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-mesh-format"), @@ -200,6 +208,7 @@ url_manual_mapping = ( ("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"), ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"), ("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"), + ("bpy.types.spaceclipeditor.use_grayscale_preview*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-grayscale-preview"), ("bpy.types.spaceoutliner.use_filter_lib_override*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override"), ("bpy.types.spaceoutliner.use_filter_object_empty*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-empty"), ("bpy.types.spaceoutliner.use_filter_object_light*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-light"), @@ -213,11 +222,13 @@ url_manual_mapping = ( ("bpy.ops.armature.rigify_apply_selection_colors*", "addons/rigging/rigify/metarigs.html#bpy-ops-armature-rigify-apply-selection-colors"), ("bpy.types.brushgpencilsettings.fill_layer_mode*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-layer-mode"), ("bpy.types.cyclesrendersettings.use_camera_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-camera-cull"), + ("bpy.types.editbone.bbone_handle_use_ease_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-ease-start"), ("bpy.types.fluiddomainsettings.color_ramp_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field"), ("bpy.types.fluiddomainsettings.guide_vel_factor*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-vel-factor"), ("bpy.types.fluideffectorsettings.use_plane_init*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-use-plane-init"), ("bpy.types.greasepencil.curve_edit_corner_angle*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-corner-angle"), ("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"), + ("bpy.types.movieclipuser.use_render_undistorted*", "editors/clip/display/clip_display.html#bpy-types-movieclipuser-use-render-undistorted"), ("bpy.types.movietrackingcamera.distortion_model*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-distortion-model"), ("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/dimensions.html#bpy-types-rendersettings-resolution-percentage"), ("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"), @@ -236,6 +247,7 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.fill_threshold*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-threshold"), ("bpy.types.clothsettings.vertex_group_pressure*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-pressure"), ("bpy.types.cyclesmaterialsettings.displacement*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-displacement"), + ("bpy.types.editbone.bbone_handle_use_scale_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-end"), ("bpy.types.fluiddomainsettings.adapt_threshold*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-threshold"), ("bpy.types.fluiddomainsettings.cache_directory*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-directory"), ("bpy.types.fluiddomainsettings.cache_frame_end*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-end"), @@ -272,6 +284,8 @@ url_manual_mapping = ( ("bpy.types.clothsettings.internal_compression*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-compression"), ("bpy.types.cyclesrendersettings.dicing_camera*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-camera"), ("bpy.types.cyclesrendersettings.texture_limit*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-texture-limit"), + ("bpy.types.editbone.bbone_custom_handle_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-start"), + ("bpy.types.editbone.bbone_handle_use_ease_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-ease-end"), ("bpy.types.fluiddomainsettings.additional_res*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-additional-res"), ("bpy.types.fluiddomainsettings.dissolve_speed*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-dissolve-speed"), ("bpy.types.fluiddomainsettings.effector_group*", "physics/fluid/type/domain/collections.html#bpy-types-fluiddomainsettings-effector-group"), @@ -294,6 +308,7 @@ url_manual_mapping = ( ("bpy.types.linestylegeometrymodifier_sampling*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sampling.html#bpy-types-linestylegeometrymodifier-sampling"), ("bpy.types.nodesocketinterface*.default_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-default-value"), ("bpy.types.rendersettings.use_persistent_data*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-persistent-data"), + ("bpy.types.spaceclipeditor.show_green_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-green-channel"), ("bpy.types.spaceoutliner.show_restrict_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-restrict-column"), ("bpy.types.spacespreadsheet.object_eval_state*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-object-eval-state"), ("bpy.types.toolsettings.transform_pivot_point*", "editors/3dview/controls/pivot_point/index.html#bpy-types-toolsettings-transform-pivot-point"), @@ -314,6 +329,7 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.velocity_factor*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-factor"), ("bpy.types.fluidflowsettings.velocity_normal*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-normal"), ("bpy.types.geometrynodealignrotationtovector*", "modeling/geometry_nodes/point/align_rotation_to_vector.html#bpy-types-geometrynodealignrotationtovector"), + ("bpy.types.geometrynodeattributevectorrotate*", "modeling/geometry_nodes/attribute/attribute_vector_rotate.html#bpy-types-geometrynodeattributevectorrotate"), ("bpy.types.greasepencil.curve_edit_threshold*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-threshold"), ("bpy.types.materialgpencilstyle.stroke_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-stroke-style"), ("bpy.types.objectlineart.use_crease_override*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-use-crease-override"), @@ -322,6 +338,7 @@ url_manual_mapping = ( ("bpy.types.rendersettings.use_file_extension*", "render/output/properties/output.html#bpy-types-rendersettings-use-file-extension"), ("bpy.types.sculpt.constant_detail_resolution*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-constant-detail-resolution"), ("bpy.types.spaceclipeditor.annotation_source*", "movie_clip/tracking/clip/sidebar/annotation.html#bpy-types-spaceclipeditor-annotation-source"), + ("bpy.types.spaceclipeditor.show_blue_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-blue-channel"), ("bpy.types.spaceoutliner.use_filter_children*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-children"), ("bpy.types.spaceoutliner.use_filter_complete*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-complete"), ("bpy.types.spacesequenceeditor.show_metadata*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-metadata"), @@ -340,6 +357,8 @@ url_manual_mapping = ( ("bpy.types.curve.bevel_factor_mapping_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-start"), ("bpy.types.cyclesobjectsettings.dicing_rate*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-dicing-rate"), ("bpy.types.cyclesrendersettings.use_fast_gi*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-use-fast-gi"), + ("bpy.types.editbone.bbone_custom_handle_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-end"), + ("bpy.types.editbone.bbone_handle_type_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-start"), ("bpy.types.fluiddomainsettings.adapt_margin*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-margin"), ("bpy.types.fluiddomainsettings.burning_rate*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-burning-rate"), ("bpy.types.fluiddomainsettings.guide_parent*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-parent"), @@ -360,6 +379,8 @@ url_manual_mapping = ( ("bpy.types.posebone.use_ik_rotation_control*", "animation/armatures/posing/bone_constraints/inverse_kinematics/introduction.html#bpy-types-posebone-use-ik-rotation-control"), ("bpy.types.rendersettings.use_bake_multires*", "render/cycles/baking.html#bpy-types-rendersettings-use-bake-multires"), ("bpy.types.scenegpencil.antialias_threshold*", "render/cycles/render_settings/grease_pencil.html#bpy-types-scenegpencil-antialias-threshold"), + ("bpy.types.spaceclipeditor.show_red_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-red-channel"), + ("bpy.types.spaceclipeditor.use_mute_footage*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-mute-footage"), ("bpy.types.spacesequenceeditor.overlay_type*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-type"), ("bpy.types.spacesequenceeditor.show_fcurves*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-fcurves"), ("bpy.types.spaceuveditor.sticky_select_mode*", "editors/uv/selecting.html#bpy-types-spaceuveditor-sticky-select-mode"), @@ -436,6 +457,8 @@ url_manual_mapping = ( ("bpy.types.colormanagedviewsettings.gamma*", "render/color_management.html#bpy-types-colormanagedviewsettings-gamma"), ("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"), ("bpy.types.curve.bevel_factor_mapping_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-end"), + ("bpy.types.editbone.bbone_handle_type_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-end"), + ("bpy.types.editbone.use_endroll_as_inroll*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-endroll-as-inroll"), ("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"), ("bpy.types.fluiddomainsettings.flip_ratio*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flip-ratio"), ("bpy.types.fluiddomainsettings.guide_beta*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-beta"), @@ -448,6 +471,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodeattributecolorramp*", "modeling/geometry_nodes/attribute/attribute_color_ramp.html#bpy-types-geometrynodeattributecolorramp"), ("bpy.types.geometrynodeattributeproximity*", "modeling/geometry_nodes/attribute/attribute_proximity.html#bpy-types-geometrynodeattributeproximity"), ("bpy.types.geometrynodeattributerandomize*", "modeling/geometry_nodes/attribute/attribute_randomize.html#bpy-types-geometrynodeattributerandomize"), + ("bpy.types.geometrynodeseparatecomponents*", "modeling/geometry_nodes/geometry/separate_components.html#bpy-types-geometrynodeseparatecomponents"), ("bpy.types.geometrynodesubdivisionsurface*", "modeling/geometry_nodes/mesh/subdivision_surface.html#bpy-types-geometrynodesubdivisionsurface"), ("bpy.types.imageformatsettings.color_mode*", "render/output/properties/output.html#bpy-types-imageformatsettings-color-mode"), ("bpy.types.linestyle*modifier_alongstroke*", "render/freestyle/parameter_editor/line_style/modifiers/color/along_stroke.html#bpy-types-linestyle-modifier-alongstroke"), @@ -466,6 +490,7 @@ url_manual_mapping = ( ("bpy.types.rendersettings.use_compositing*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-compositing"), ("bpy.types.rendersettings.use_placeholder*", "render/output/properties/output.html#bpy-types-rendersettings-use-placeholder"), ("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"), + ("bpy.types.spaceclipeditor.lock_selection*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-lock-selection"), ("bpy.types.spacedopesheeteditor.auto_snap*", "editors/dope_sheet/editing.html#bpy-types-spacedopesheeteditor-auto-snap"), ("bpy.types.spaceoutliner.show_mode_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-mode-column"), ("bpy.types.spacetexteditor.use_match_case*", "editors/text_editor.html#bpy-types-spacetexteditor-use-match-case"), @@ -491,6 +516,7 @@ url_manual_mapping = ( ("bpy.types.colormanagedviewsettings.look*", "render/color_management.html#bpy-types-colormanagedviewsettings-look"), ("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"), ("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"), + ("bpy.types.editbone.use_inherit_rotation*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-inherit-rotation"), ("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"), ("bpy.types.fluiddomainsettings.use_noise*", "physics/fluid/type/domain/gas/noise.html#bpy-types-fluiddomainsettings-use-noise"), ("bpy.types.fluiddomainsettings.use_slice*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-use-slice"), @@ -501,7 +527,9 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.temperature*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-temperature"), ("bpy.types.fluidflowsettings.use_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-texture"), ("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"), + ("bpy.types.geometrynodeattributecurvemap*", "modeling/geometry_nodes/attribute/attribute_curve_map.html#bpy-types-geometrynodeattributecurvemap"), ("bpy.types.geometrynodeattributemaprange*", "modeling/geometry_nodes/attribute/attribute_map_range.html#bpy-types-geometrynodeattributemaprange"), + ("bpy.types.geometrynodeattributetransfer*", "modeling/geometry_nodes/attribute/attribute_transfer.html#bpy-types-geometrynodeattributetransfer"), ("bpy.types.layercollection.hide_viewport*", "editors/outliner/interface.html#bpy-types-layercollection-hide-viewport"), ("bpy.types.layercollection.indirect_only*", "editors/outliner/interface.html#bpy-types-layercollection-indirect-only"), ("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"), @@ -546,6 +574,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.use_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-use-mesh"), ("bpy.types.geometrynodeattributecompare*", "modeling/geometry_nodes/attribute/attribute_compare.html#bpy-types-geometrynodeattributecompare"), ("bpy.types.geometrynodeattributeconvert*", "modeling/geometry_nodes/attribute/attribute_convert.html#bpy-types-geometrynodeattributeconvert"), + ("bpy.types.geometrynodeselectbymaterial*", "modeling/geometry_nodes/material/select_by_material.html#bpy-types-geometrynodeselectbymaterial"), ("bpy.types.material.preview_render_type*", "render/materials/preview.html#bpy-types-material-preview-render-type"), ("bpy.types.materialgpencilstyle.pattern*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-pattern"), ("bpy.types.materialgpencilstyle.texture*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-texture"), @@ -595,10 +624,12 @@ url_manual_mapping = ( ("bpy.types.compositornodedistancematte*", "compositing/types/matte/distance_key.html#bpy-types-compositornodedistancematte"), ("bpy.types.compositornodesetalpha.mode*", "compositing/types/converter/set_alpha.html#bpy-types-compositornodesetalpha-mode"), ("bpy.types.dopesheet.use_filter_invert*", "editors/graph_editor/channels.html#bpy-types-dopesheet-use-filter-invert"), + ("bpy.types.editbone.use_local_location*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-local-location"), ("bpy.types.fluiddomainsettings.gravity*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-gravity"), ("bpy.types.fluidflowsettings.flow_type*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-flow-type"), ("bpy.types.fluidflowsettings.subframes*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-subframes"), ("bpy.types.geometrynodeattributeremove*", "modeling/geometry_nodes/attribute/attribute_remove.html#bpy-types-geometrynodeattributeremove"), + ("bpy.types.geometrynodematerialreplace*", "modeling/geometry_nodes/material/replace.html#bpy-types-geometrynodematerialreplace"), ("bpy.types.geometrynodepointdistribute*", "modeling/geometry_nodes/point/point_distribute.html#bpy-types-geometrynodepointdistribute"), ("bpy.types.gpencillayer.use_mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-mask-layer"), ("bpy.types.greasepencil.use_curve_edit*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-curve-edit"), @@ -621,6 +652,7 @@ url_manual_mapping = ( ("bpy.types.sequenceeditor.show_overlay*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-show-overlay"), ("bpy.types.sequenceeditor.use_prefetch*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-prefetch"), ("bpy.types.soundsequence.show_waveform*", "video_editing/sequencer/sidebar/strip.html#bpy-types-soundsequence-show-waveform"), + ("bpy.types.spaceclipeditor.show_stable*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-stable"), ("bpy.types.spaceoutliner.filter_invert*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-invert"), ("bpy.types.spacetexteditor.show_margin*", "editors/text_editor.html#bpy-types-spacetexteditor-show-margin"), ("bpy.types.spline.radius_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-radius-interpolation"), @@ -647,6 +679,7 @@ url_manual_mapping = ( ("bpy.ops.view3d.localview_remove_from*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview-remove-from"), ("bpy.types.animdata.action_blend_type*", "editors/nla/sidebar.html#bpy-types-animdata-action-blend-type"), ("bpy.types.bakesettings.use_pass_emit*", "render/cycles/baking.html#bpy-types-bakesettings-use-pass-emit"), + ("bpy.types.bone.use_envelope_multiply*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-use-envelope-multiply"), ("bpy.types.brush.boundary_deform_type*", "sculpt_paint/sculpting/tools/boundary.html#bpy-types-brush-boundary-deform-type"), ("bpy.types.brush.cursor_overlay_alpha*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-overlay-alpha"), ("bpy.types.brush.normal_radius_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-normal-radius-factor"), @@ -662,6 +695,9 @@ url_manual_mapping = ( ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierfunctiongenerator"), ("bpy.types.geometrynodeattributeclamp*", "modeling/geometry_nodes/attribute/attribute_clamp.html#bpy-types-geometrynodeattributeclamp"), ("bpy.types.geometrynodecollectioninfo*", "modeling/geometry_nodes/input/collection_info.html#bpy-types-geometrynodecollectioninfo"), + ("bpy.types.geometrynodecurvesubdivide*", "modeling/geometry_nodes/curve/curve_subdivide.html#bpy-types-geometrynodecurvesubdivide"), + ("bpy.types.geometrynodedeletegeometry*", "modeling/geometry_nodes/geometry/delete_geometry.html#bpy-types-geometrynodedeletegeometry"), + ("bpy.types.geometrynodematerialassign*", "modeling/geometry_nodes/material/assign.html#bpy-types-geometrynodematerialassign"), ("bpy.types.geometrynodepointstovolume*", "modeling/geometry_nodes/volume/points_to_volume.html#bpy-types-geometrynodepointstovolume"), ("bpy.types.geometrynodepointtranslate*", "modeling/geometry_nodes/point/point_translate.html#bpy-types-geometrynodepointtranslate"), ("bpy.types.greasepencil.use_multiedit*", "grease_pencil/multiframe.html#bpy-types-greasepencil-use-multiedit"), @@ -721,13 +757,17 @@ url_manual_mapping = ( ("bpy.types.compositornodesplitviewer*", "compositing/types/output/split_viewer.html#bpy-types-compositornodesplitviewer"), ("bpy.types.curve.render_resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-render-resolution-u"), ("bpy.types.dynamicpaintbrushsettings*", "physics/dynamic_paint/brush.html#bpy-types-dynamicpaintbrushsettings"), + ("bpy.types.editbone.use_scale_easing*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-scale-easing"), ("bpy.types.fluiddomainsettings.alpha*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-alpha"), ("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"), ("bpy.types.geometrynodeattributefill*", "modeling/geometry_nodes/attribute/attribute_fill.html#bpy-types-geometrynodeattributefill"), ("bpy.types.geometrynodeattributemath*", "modeling/geometry_nodes/attribute/attribute_math.html#bpy-types-geometrynodeattributemath"), + ("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"), + ("bpy.types.geometrynodeinputmaterial*", "modeling/geometry_nodes/input/material.html#bpy-types-geometrynodeinputmaterial"), ("bpy.types.geometrynodemeshicosphere*", "modeling/geometry_nodes/mesh_primitives/icosphere.html#bpy-types-geometrynodemeshicosphere"), ("bpy.types.geometrynodepointinstance*", "modeling/geometry_nodes/point/point_instance.html#bpy-types-geometrynodepointinstance"), ("bpy.types.geometrynodepointseparate*", "modeling/geometry_nodes/point/point_separate.html#bpy-types-geometrynodepointseparate"), + ("bpy.types.geometrynoderesamplecurve*", "modeling/geometry_nodes/curve/resample_curve.html#bpy-types-geometrynoderesamplecurve"), ("bpy.types.light.use_custom_distance*", "render/eevee/lighting.html#bpy-types-light-use-custom-distance"), ("bpy.types.materialgpencilstyle.flip*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-flip"), ("bpy.types.materialgpencilstyle.mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mode"), @@ -746,6 +786,7 @@ url_manual_mapping = ( ("bpy.types.shadernodebsdftranslucent*", "render/shader_nodes/shader/translucent.html#bpy-types-shadernodebsdftranslucent"), ("bpy.types.shadernodebsdftransparent*", "render/shader_nodes/shader/transparent.html#bpy-types-shadernodebsdftransparent"), ("bpy.types.shadernodevectortransform*", "render/shader_nodes/vector/transform.html#bpy-types-shadernodevectortransform"), + ("bpy.types.spaceclipeditor.show_grid*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-grid"), ("bpy.types.spaceoutliner.filter_text*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-text"), ("bpy.types.spacetexteditor.find_text*", "editors/text_editor.html#bpy-types-spacetexteditor-find-text"), ("bpy.types.spacetexteditor.font_size*", "editors/text_editor.html#bpy-types-spacetexteditor-font-size"), @@ -775,11 +816,12 @@ url_manual_mapping = ( ("bpy.ops.pose.user_transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-user-transforms-clear"), ("bpy.ops.poselib.browse_interactive*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-browse-interactive"), ("bpy.ops.sculpt.set_persistent_base*", "sculpt_paint/sculpting/tools/layer.html#bpy-ops-sculpt-set-persistent-base"), - ("bpy.ops.sequencer.crossfade_sounds*", "video_editing/sequencer/strips/transitions/cross.html#bpy-ops-sequencer-crossfade-sounds"), + ("bpy.ops.sequencer.crossfade_sounds*", "video_editing/sequencer/strips/transitions/sound_crossfade.html#bpy-ops-sequencer-crossfade-sounds"), ("bpy.ops.sequencer.export_subtitles*", "video_editing/preview/introduction.html#bpy-ops-sequencer-export-subtitles"), ("bpy.ops.transform.edge_bevelweight*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-bevelweight"), ("bpy.ops.wm.previews_batch_generate*", "files/blend/previews.html#bpy-ops-wm-previews-batch-generate"), ("bpy.types.bakesettings.cage_object*", "render/cycles/baking.html#bpy-types-bakesettings-cage-object"), + ("bpy.types.bone.use_relative_parent*", "animation/armatures/bones/properties/relations.html#bpy-types-bone-use-relative-parent"), ("bpy.types.brush.auto_smooth_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-auto-smooth-factor"), ("bpy.types.brush.smooth_deform_type*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-smooth-deform-type"), ("bpy.types.brush.use_connected_only*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-connected-only"), @@ -801,6 +843,7 @@ url_manual_mapping = ( ("bpy.types.fluidmodifier.fluid_type*", "physics/fluid/type/index.html#bpy-types-fluidmodifier-fluid-type"), ("bpy.types.functionnodefloatcompare*", "modeling/geometry_nodes/utilities/float_compare.html#bpy-types-functionnodefloatcompare"), ("bpy.types.geometrynodeattributemix*", "modeling/geometry_nodes/attribute/attribute_mix.html#bpy-types-geometrynodeattributemix"), + ("bpy.types.geometrynodecurvereverse*", "modeling/geometry_nodes/curve/curve_reverse.html#bpy-types-geometrynodecurvereverse"), ("bpy.types.geometrynodejoingeometry*", "modeling/geometry_nodes/geometry/join_geometry.html#bpy-types-geometrynodejoingeometry"), ("bpy.types.geometrynodemeshcylinder*", "modeling/geometry_nodes/mesh_primitives/cylinder.html#bpy-types-geometrynodemeshcylinder"), ("bpy.types.geometrynodemeshuvsphere*", "modeling/geometry_nodes/mesh_primitives/uv_sphere.html#bpy-types-geometrynodemeshuvsphere"), @@ -810,6 +853,7 @@ url_manual_mapping = ( ("bpy.types.linestyle*modifier_noise*", "render/freestyle/parameter_editor/line_style/modifiers/color/noise.html#bpy-types-linestyle-modifier-noise"), ("bpy.types.maintainvolumeconstraint*", "animation/constraints/transform/maintain_volume.html#bpy-types-maintainvolumeconstraint"), ("bpy.types.mesh.use_mirror_topology*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-topology"), + ("bpy.types.movieclip.display_aspect*", "editors/clip/display/clip_display.html#bpy-types-movieclip-display-aspect"), ("bpy.types.nodesocketinterface.name*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-name"), ("bpy.types.particleinstancemodifier*", "modeling/modifiers/physics/particle_instance.html#bpy-types-particleinstancemodifier"), ("bpy.types.sequencetransform.offset*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-offset"), @@ -834,6 +878,8 @@ url_manual_mapping = ( ("bpy.ops.curve.match_texture_space*", "modeling/meshes/uv/uv_texture_spaces.html#bpy-ops-curve-match-texture-space"), ("bpy.ops.font.text_paste_from_file*", "modeling/texts/editing.html#bpy-ops-font-text-paste-from-file"), ("bpy.ops.gpencil.frame_clean_loose*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-loose"), + ("bpy.ops.mask.primitive_circle_add*", "movie_clip/masking/scurve.html#bpy-ops-mask-primitive-circle-add"), + ("bpy.ops.mask.primitive_square_add*", "movie_clip/masking/scurve.html#bpy-ops-mask-primitive-square-add"), ("bpy.ops.mesh.primitive_circle_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-circle-add"), ("bpy.ops.mesh.primitive_monkey_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-monkey-add"), ("bpy.ops.mesh.select_face_by_sides*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-face-by-sides"), @@ -845,8 +891,8 @@ url_manual_mapping = ( ("bpy.ops.render.play-rendered-anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"), ("bpy.ops.scene.blenderkit_download*", "addons/3d_view/blenderkit.html#bpy-ops-scene-blenderkit-download"), ("bpy.ops.sculpt.set_pivot_position*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-set-pivot-position"), - ("bpy.ops.sequencer.image_strip_add*", "video_editing/sequencer/strips/movie_image.html#bpy-ops-sequencer-image-strip-add"), - ("bpy.ops.sequencer.movie_strip_add*", "video_editing/sequencer/strips/movie_image.html#bpy-ops-sequencer-movie-strip-add"), + ("bpy.ops.sequencer.image_strip_add*", "video_editing/sequencer/strips/image.html#bpy-ops-sequencer-image-strip-add"), + ("bpy.ops.sequencer.movie_strip_add*", "video_editing/sequencer/strips/movie.html#bpy-ops-sequencer-movie-strip-add"), ("bpy.ops.sequencer.reassign_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reassign-inputs"), ("bpy.ops.sequencer.sound_strip_add*", "video_editing/sequencer/strips/sound.html#bpy-ops-sequencer-sound-strip-add"), ("bpy.ops.ui.remove_override_button*", "files/linked_libraries/library_overrides.html#bpy-ops-ui-remove-override-button"), @@ -870,11 +916,18 @@ url_manual_mapping = ( ("bpy.types.compositornodetranslate*", "compositing/types/distort/translate.html#bpy-types-compositornodetranslate"), ("bpy.types.constraint.target_space*", "animation/constraints/interface/common.html#bpy-types-constraint-target-space"), ("bpy.types.curve.use_deform_bounds*", "modeling/curves/properties/shape.html#bpy-types-curve-use-deform-bounds"), + ("bpy.types.editbone.bbone_curveinx*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-curveinx"), + ("bpy.types.editbone.bbone_curveinz*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-curveinz"), + ("bpy.types.editbone.bbone_scaleout*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scaleout"), + ("bpy.types.editbone.bbone_segments*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-segments"), ("bpy.types.freestylemodulesettings*", "render/freestyle/python.html#bpy-types-freestylemodulesettings"), ("bpy.types.functionnodebooleanmath*", "modeling/geometry_nodes/utilities/boolean_math.html#bpy-types-functionnodebooleanmath"), ("bpy.types.functionnodeinputstring*", "modeling/geometry_nodes/input/string.html#bpy-types-functionnodeinputstring"), ("bpy.types.functionnodeinputvector*", "modeling/geometry_nodes/input/vector.html#bpy-types-functionnodeinputvector"), ("bpy.types.functionnoderandomfloat*", "modeling/geometry_nodes/input/random_float.html#bpy-types-functionnoderandomfloat"), + ("bpy.types.geometrynodecurvelength*", "modeling/geometry_nodes/curve/curve_length.html#bpy-types-geometrynodecurvelength"), + ("bpy.types.geometrynodecurvetomesh*", "modeling/geometry_nodes/curve/curve_to_mesh.html#bpy-types-geometrynodecurvetomesh"), + ("bpy.types.geometrynodemeshtocurve*", "modeling/geometry_nodes/curve/mesh_to_curve.html#bpy-types-geometrynodemeshtocurve"), ("bpy.types.geometrynodepointrotate*", "modeling/geometry_nodes/point/point_rotate.html#bpy-types-geometrynodepointrotate"), ("bpy.types.geometrynodetriangulate*", "modeling/geometry_nodes/mesh/triangulate.html#bpy-types-geometrynodetriangulate"), ("bpy.types.gpencillayer.blend_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-blend-mode"), @@ -938,6 +991,7 @@ url_manual_mapping = ( ("bpy.ops.view3d.blenderkit_search*", "addons/3d_view/blenderkit.html#bpy-ops-view3d-blenderkit-search"), ("bpy.types.armature.pose_position*", "animation/armatures/properties/skeleton.html#bpy-types-armature-pose-position"), ("bpy.types.bakesettings.use_clear*", "render/cycles/baking.html#bpy-types-bakesettings-use-clear"), + ("bpy.types.bone.envelope_distance*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-envelope-distance"), ("bpy.types.brightcontrastmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-brightcontrastmodifier"), ("bpy.types.brush.cursor_color_add*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-color-add"), ("bpy.types.brush.pose_deform_type*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-deform-type"), @@ -965,6 +1019,11 @@ url_manual_mapping = ( ("bpy.types.curve.bevel_resolution*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-resolution"), ("bpy.types.cyclesmaterialsettings*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings"), ("bpy.types.dopesheet.show_summary*", "editors/dope_sheet/introduction.html#bpy-types-dopesheet-show-summary"), + ("bpy.types.editbone.bbone_easeout*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-easeout"), + ("bpy.types.editbone.bbone_rollout*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-rollout"), + ("bpy.types.editbone.bbone_scalein*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scalein"), + ("bpy.types.editbone.inherit_scale*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-inherit-scale"), + ("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"), ("bpy.types.geometrynodeisviewport*", "modeling/geometry_nodes/input/is_viewport.html#bpy-types-geometrynodeisviewport"), ("bpy.types.geometrynodemeshcircle*", "modeling/geometry_nodes/mesh_primitives/circle.html#bpy-types-geometrynodemeshcircle"), ("bpy.types.geometrynodeobjectinfo*", "modeling/geometry_nodes/input/object_info.html#bpy-types-geometrynodeobjectinfo"), @@ -1064,6 +1123,8 @@ url_manual_mapping = ( ("bpy.types.dampedtrackconstraint*", "animation/constraints/tracking/damped_track.html#bpy-types-dampedtrackconstraint"), ("bpy.types.distortednoisetexture*", "render/materials/legacy_textures/types/distorted_noise.html#bpy-types-distortednoisetexture"), ("bpy.types.dopesheet.filter_text*", "editors/graph_editor/channels.html#bpy-types-dopesheet-filter-text"), + ("bpy.types.editbone.bbone_easein*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-easein"), + ("bpy.types.editbone.bbone_rollin*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-rollin"), ("bpy.types.fluideffectorsettings*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings"), ("bpy.types.followtrackconstraint*", "animation/constraints/motion_tracking/follow_track.html#bpy-types-followtrackconstraint"), ("bpy.types.geometrynodeedgesplit*", "modeling/geometry_nodes/mesh/edge_split.html#bpy-types-geometrynodeedgesplit"), @@ -1121,6 +1182,7 @@ url_manual_mapping = ( ("bpy.ops.spreadsheet.toggle_pin*", "editors/spreadsheet.html#bpy-ops-spreadsheet-toggle-pin"), ("bpy.ops.uv.follow_active_quads*", "modeling/meshes/editing/uv.html#bpy-ops-uv-follow-active-quads"), ("bpy.types.arraygpencilmodifier*", "grease_pencil/modifiers/generate/array.html#bpy-types-arraygpencilmodifier"), + ("bpy.types.bone.envelope_weight*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-envelope-weight"), ("bpy.types.brush.use_persistent*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-use-persistent"), ("bpy.types.buildgpencilmodifier*", "grease_pencil/modifiers/generate/build.html#bpy-types-buildgpencilmodifier"), ("bpy.types.colorbalancemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-colorbalancemodifier"), @@ -1139,6 +1201,7 @@ url_manual_mapping = ( ("bpy.types.curve.use_path_clamp*", "modeling/curves/properties/path_animation.html#bpy-types-curve-use-path-clamp"), ("bpy.types.datatransfermodifier*", "modeling/modifiers/modify/data_transfer.html#bpy-types-datatransfermodifier"), ("bpy.types.dynamicpaintmodifier*", "physics/dynamic_paint/index.html#bpy-types-dynamicpaintmodifier"), + ("bpy.types.editbone.use_connect*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-connect"), ("bpy.types.ffmpegsettings.audio*", "render/output/properties/output.html#bpy-types-ffmpegsettings-audio"), ("bpy.types.followpathconstraint*", "animation/constraints/relationship/follow_path.html#bpy-types-followpathconstraint"), ("bpy.types.gaussianblursequence*", "video_editing/sequencer/strips/effects/blur.html#bpy-types-gaussianblursequence"), @@ -1173,6 +1236,7 @@ url_manual_mapping = ( ("bpy.types.shadernodewavelength*", "render/shader_nodes/converter/wavelength.html#bpy-types-shadernodewavelength"), ("bpy.types.shrinkwrapconstraint*", "animation/constraints/relationship/shrinkwrap.html#bpy-types-shrinkwrapconstraint"), ("bpy.types.simpledeformmodifier*", "modeling/modifiers/deform/simple_deform.html#bpy-types-simpledeformmodifier"), + ("bpy.types.spaceclipeditor.show*", "editors/clip/display/index.html#bpy-types-spaceclipeditor-show"), ("bpy.types.spacedopesheeteditor*", "editors/dope_sheet/index.html#bpy-types-spacedopesheeteditor"), ("bpy.types.speedcontrolsequence*", "video_editing/sequencer/strips/effects/speed_control.html#bpy-types-speedcontrolsequence"), ("bpy.types.texturenodecurvetime*", "editors/texture_node/types/input/time.html#bpy-types-texturenodecurvetime"), @@ -1251,6 +1315,7 @@ url_manual_mapping = ( ("bpy.types.cyclesworldsettings*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings"), ("bpy.types.fluiddomainsettings*", "physics/fluid/type/domain/index.html#bpy-types-fluiddomainsettings"), ("bpy.types.geometrynodeboolean*", "modeling/geometry_nodes/mesh/boolean.html#bpy-types-geometrynodeboolean"), + ("bpy.types.geometrynoderaycast*", "modeling/geometry_nodes/geometry/raycast.html#bpy-types-geometrynoderaycast"), ("bpy.types.hookgpencilmodifier*", "grease_pencil/modifiers/deform/hook.html#bpy-types-hookgpencilmodifier"), ("bpy.types.imageformatsettings*", "files/media/image_formats.html#bpy-types-imageformatsettings"), ("bpy.types.kinematicconstraint*", "animation/constraints/tracking/ik_solver.html#bpy-types-kinematicconstraint"), @@ -1259,6 +1324,7 @@ url_manual_mapping = ( ("bpy.types.object.display_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-type"), ("bpy.types.objectlineart.usage*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-usage"), ("bpy.types.particledupliweight*", "physics/particles/emitter/vertex_groups.html#bpy-types-particledupliweight"), + ("bpy.types.posebone.bone_group*", "animation/armatures/bones/properties/relations.html#bpy-types-posebone-bone-group"), ("bpy.types.poseboneconstraints*", "animation/armatures/posing/bone_constraints/index.html#bpy-types-poseboneconstraints"), ("bpy.types.rigidbodyconstraint*", "physics/rigid_body/constraints/index.html#bpy-types-rigidbodyconstraint"), ("bpy.types.rigifyarmaturelayer*", "addons/rigging/rigify/metarigs.html#bpy-types-rigifyarmaturelayer"), @@ -1356,7 +1422,8 @@ url_manual_mapping = ( ("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"), ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifiergenerator"), ("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"), - ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/cross.html#bpy-types-gammacrosssequence"), + ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"), + ("bpy.types.geometrynodeswitch*", "modeling/geometry_nodes/utilities/switch.html#bpy-types-geometrynodeswitch"), ("bpy.types.gpencilsculptguide*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide"), ("bpy.types.huecorrectmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-huecorrectmodifier"), ("bpy.types.imagepaint.stencil*", "sculpt_paint/texture_paint/tool_settings/mask.html#bpy-types-imagepaint-stencil"), @@ -1378,7 +1445,7 @@ url_manual_mapping = ( ("bpy.types.shadernodegeometry*", "render/shader_nodes/input/geometry.html#bpy-types-shadernodegeometry"), ("bpy.types.shadernodehairinfo*", "render/shader_nodes/input/hair_info.html#bpy-types-shadernodehairinfo"), ("bpy.types.shadernodemaprange*", "render/shader_nodes/converter/map_range.html#bpy-types-shadernodemaprange"), - ("bpy.types.shadernodergbcurve*", "render/shader_nodes/color/rgb_curves.html#bpy-types-shadernodergbcurve"), + ("bpy.types.shadernodergbcurve*", "modeling/geometry_nodes/color/rgb_curves.html#bpy-types-shadernodergbcurve"), ("bpy.types.shadernodeseparate*", "render/shader_nodes/converter/combine_separate.html#bpy-types-shadernodeseparate"), ("bpy.types.shadernodetexbrick*", "render/shader_nodes/textures/brick.html#bpy-types-shadernodetexbrick"), ("bpy.types.shadernodetexcoord*", "render/shader_nodes/input/texture_coordinate.html#bpy-types-shadernodetexcoord"), @@ -1477,7 +1544,7 @@ url_manual_mapping = ( ("bpy.types.mesh.use_mirror_y*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-y"), ("bpy.types.mesh.use_mirror_z*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-z"), ("bpy.types.meshcachemodifier*", "modeling/modifiers/modify/mesh_cache.html#bpy-types-meshcachemodifier"), - ("bpy.types.movieclipsequence*", "video_editing/sequencer/strips/clip_mask.html#bpy-types-movieclipsequence"), + ("bpy.types.movieclipsequence*", "video_editing/sequencer/strips/clip.html#bpy-types-movieclipsequence"), ("bpy.types.object.dimensions*", "scene_layout/object/properties/transforms.html#bpy-types-object-dimensions"), ("bpy.types.object.pass_index*", "scene_layout/object/properties/relations.html#bpy-types-object-pass-index"), ("bpy.types.object.track_axis*", "scene_layout/object/properties/relations.html#bpy-types-object-track-axis"), @@ -1515,6 +1582,8 @@ url_manual_mapping = ( ("bpy.ops.curve.make_segment*", "modeling/curves/editing/control_points.html#bpy-ops-curve-make-segment"), ("bpy.ops.graph.euler_filter*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-euler-filter"), ("bpy.ops.marker.camera_bind*", "animation/markers.html#bpy-ops-marker-camera-bind"), + ("bpy.ops.mask.select_circle*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-circle"), + ("bpy.ops.mask.select_linked*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-linked"), ("bpy.ops.mesh.beautify_fill*", "modeling/meshes/editing/face/beautify_faces.html#bpy-ops-mesh-beautify-fill"), ("bpy.ops.mesh.colors_rotate*", "modeling/meshes/editing/face/face_data.html#bpy-ops-mesh-colors-rotate"), ("bpy.ops.mesh.edge_collapse*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-edge-collapse"), @@ -1550,6 +1619,8 @@ url_manual_mapping = ( ("bpy.types.addonpreferences*", "editors/preferences/addons.html#bpy-types-addonpreferences"), ("bpy.types.arealight.spread*", "render/lights/light_object.html#bpy-types-arealight-spread"), ("bpy.types.armaturemodifier*", "modeling/modifiers/deform/armature.html#bpy-types-armaturemodifier"), + ("bpy.types.bone.head_radius*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-head-radius"), + ("bpy.types.bone.tail_radius*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-tail-radius"), ("bpy.types.brush.cloth_mass*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-mass"), ("bpy.types.brushtextureslot*", "sculpt_paint/brush/texture.html#bpy-types-brushtextureslot"), ("bpy.types.colormixsequence*", "video_editing/sequencer/strips/effects/color_mix.html#bpy-types-colormixsequence"), @@ -1560,8 +1631,11 @@ url_manual_mapping = ( ("bpy.types.decimatemodifier*", "modeling/modifiers/generate/decimate.html#bpy-types-decimatemodifier"), ("bpy.types.displacemodifier*", "modeling/modifiers/deform/displace.html#bpy-types-displacemodifier"), ("bpy.types.displaysafeareas*", "render/cameras.html#bpy-types-displaysafeareas"), + ("bpy.types.editbone.bbone_x*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-x"), + ("bpy.types.editbone.bbone_z*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-z"), ("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierstepped"), ("bpy.types.freestylelineset*", "render/freestyle/parameter_editor/line_set.html#bpy-types-freestylelineset"), + ("bpy.types.mask.frame_start*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-start"), ("bpy.types.mesh.*customdata*", "modeling/meshes/properties/custom_data.html#bpy-types-mesh-customdata"), ("bpy.types.multicamsequence*", "video_editing/sequencer/strips/effects/multicam.html#bpy-types-multicamsequence"), ("bpy.types.multiplysequence*", "video_editing/sequencer/strips/effects/multiply.html#bpy-types-multiplysequence"), @@ -1612,6 +1686,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.reproject*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-reproject"), ("bpy.ops.graph.easing_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-easing-type"), ("bpy.ops.graph.handle_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-handle-type"), + ("bpy.ops.mask.parent_clear*", "movie_clip/masking/editing.html#bpy-ops-mask-parent-clear"), ("bpy.ops.mesh.bevel.vertex*", "modeling/meshes/editing/vertex/bevel_vertices.html#bpy-ops-mesh-bevel-vertex"), ("bpy.ops.mesh.delete_loose*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-delete-loose"), ("bpy.ops.mesh.face_shading*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-face-shading"), @@ -1640,11 +1715,14 @@ url_manual_mapping = ( ("bpy.ops.wm.previews_clear*", "files/blend/previews.html#bpy-ops-wm-previews-clear"), ("bpy.types.armature.layers*", "animation/armatures/properties/skeleton.html#bpy-types-armature-layers"), ("bpy.types.armature.rigify*", "addons/rigging/rigify/basics.html#bpy-types-armature-rigify"), + ("bpy.types.bone.use_deform*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-use-deform"), ("bpy.types.booleanmodifier*", "modeling/modifiers/generate/booleans.html#bpy-types-booleanmodifier"), ("bpy.types.brush.mask_tool*", "sculpt_paint/sculpting/tools/mask.html#bpy-types-brush-mask-tool"), ("bpy.types.constraint.mute*", "animation/constraints/interface/header.html#bpy-types-constraint-mute"), ("bpy.types.curve.eval_time*", "modeling/curves/properties/path_animation.html#bpy-types-curve-eval-time"), ("bpy.types.curve.fill_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-fill-mode"), + ("bpy.types.editbone.layers*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-layers"), + ("bpy.types.editbone.parent*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-parent"), ("bpy.types.explodemodifier*", "modeling/modifiers/physics/explode.html#bpy-types-explodemodifier"), ("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fcurvemodifiers"), ("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"), @@ -1685,6 +1763,8 @@ url_manual_mapping = ( ("bpy.ops.gpencil.dissolve*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-dissolve"), ("bpy.ops.graph.frame_jump*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-frame-jump"), ("bpy.ops.graph.sound_bake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-sound-bake"), + ("bpy.ops.mask.select_less*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-less"), + ("bpy.ops.mask.select_more*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-more"), ("bpy.ops.mesh.convex_hull*", "modeling/meshes/editing/mesh/convex_hull.html#bpy-ops-mesh-convex-hull"), ("bpy.ops.mesh.edge_rotate*", "modeling/meshes/editing/edge/rotate_edge.html#bpy-ops-mesh-edge-rotate"), ("bpy.ops.mesh.unsubdivide*", "modeling/meshes/editing/edge/unsubdivide.html#bpy-ops-mesh-unsubdivide"), @@ -1718,6 +1798,7 @@ url_manual_mapping = ( ("bpy.types.curvesmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-curvesmodifier"), ("bpy.types.ffmpegsettings*", "render/output/properties/output.html#bpy-types-ffmpegsettings"), ("bpy.types.fmodifiernoise*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifiernoise"), + ("bpy.types.mask.frame_end*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-end"), ("bpy.types.material.paint*", "sculpt_paint/texture_paint/index.html#bpy-types-material-paint"), ("bpy.types.mirrormodifier*", "modeling/modifiers/generate/mirror.html#bpy-types-mirrormodifier"), ("bpy.types.movieclipproxy*", "editors/clip/sidebar.html#bpy-types-movieclipproxy"), @@ -1757,6 +1838,9 @@ url_manual_mapping = ( ("bpy.ops.fluid.free_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-ops-fluid-free-mesh"), ("bpy.ops.font.select_all*", "modeling/texts/selecting.html#bpy-ops-font-select-all"), ("bpy.ops.gpencil.convert*", "grease_pencil/modes/object/convert_to_geometry.html#bpy-ops-gpencil-convert"), + ("bpy.ops.mask.parent_set*", "movie_clip/masking/editing.html#bpy-ops-mask-parent-set"), + ("bpy.ops.mask.select_all*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-all"), + ("bpy.ops.mask.select_box*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-box"), ("bpy.ops.mesh.customdata*", "modeling/meshes/properties/custom_data.html#bpy-ops-mesh-customdata"), ("bpy.ops.mesh.edge_split*", "modeling/meshes/editing/mesh/split.html#bpy-ops-mesh-edge-split"), ("bpy.ops.mesh.fill_holes*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-fill-holes"), @@ -1800,11 +1884,15 @@ url_manual_mapping = ( ("bpy.types.crosssequence*", "video_editing/sequencer/strips/transitions/cross.html#bpy-types-crosssequence"), ("bpy.types.curve.extrude*", "modeling/curves/properties/geometry.html#bpy-types-curve-extrude"), ("bpy.types.curvemodifier*", "modeling/modifiers/deform/curve.html#bpy-types-curvemodifier"), + ("bpy.types.editbone.head*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-head"), + ("bpy.types.editbone.lock*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-lock"), + ("bpy.types.editbone.roll*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-roll"), + ("bpy.types.editbone.tail*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-tail"), ("bpy.types.fieldsettings*", "physics/forces/force_fields/index.html#bpy-types-fieldsettings"), - ("bpy.types.imagesequence*", "video_editing/sequencer/strips/movie_image.html#bpy-types-imagesequence"), + ("bpy.types.imagesequence*", "video_editing/sequencer/strips/image.html#bpy-types-imagesequence"), ("bpy.types.marbletexture*", "render/materials/legacy_textures/types/marble.html#bpy-types-marbletexture"), ("bpy.types.modifier.show*", "modeling/modifiers/introduction.html#bpy-types-modifier-show"), - ("bpy.types.moviesequence*", "video_editing/sequencer/strips/movie_image.html#bpy-types-moviesequence"), + ("bpy.types.moviesequence*", "video_editing/sequencer/strips/movie.html#bpy-types-moviesequence"), ("bpy.types.movietracking*", "movie_clip/tracking/index.html#bpy-types-movietracking"), ("bpy.types.nlastrip.mute*", "editors/nla/sidebar.html#bpy-types-nlastrip-mute"), ("bpy.types.nlastrip.name*", "editors/nla/sidebar.html#bpy-types-nlastrip-name"), @@ -1880,7 +1968,7 @@ url_manual_mapping = ( ("bpy.types.latticepoint*", "animation/lattice.html#bpy-types-latticepoint"), ("bpy.types.magictexture*", "render/materials/legacy_textures/types/magic.html#bpy-types-magictexture"), ("bpy.types.maskmodifier*", "modeling/modifiers/generate/mask.html#bpy-types-maskmodifier"), - ("bpy.types.masksequence*", "video_editing/sequencer/strips/clip_mask.html#bpy-types-masksequence"), + ("bpy.types.masksequence*", "video_editing/sequencer/strips/mask.html#bpy-types-masksequence"), ("bpy.types.materialslot*", "render/materials/assignment.html#bpy-types-materialslot"), ("bpy.types.metasequence*", "video_editing/sequencer/meta.html#bpy-types-metasequence"), ("bpy.types.object.color*", "scene_layout/object/properties/display.html#bpy-types-object-color"), @@ -1982,6 +2070,8 @@ url_manual_mapping = ( ("bpy.types.constraint*", "animation/constraints/index.html#bpy-types-constraint"), ("bpy.types.imagepaint*", "sculpt_paint/texture_paint/index.html#bpy-types-imagepaint"), ("bpy.types.lightprobe*", "render/eevee/light_probes/index.html#bpy-types-lightprobe"), + ("bpy.types.maskparent*", "movie_clip/masking/sidebar.html#bpy-types-maskparent"), + ("bpy.types.maskspline*", "movie_clip/masking/sidebar.html#bpy-types-maskspline"), ("bpy.types.node.color*", "interface/controls/nodes/sidebar.html#bpy-types-node-color"), ("bpy.types.node.label*", "interface/controls/nodes/sidebar.html#bpy-types-node-label"), ("bpy.types.nodesocket*", "interface/controls/nodes/parts.html#bpy-types-nodesocket"), @@ -2017,6 +2107,7 @@ url_manual_mapping = ( ("bpy.types.dopesheet*", "editors/dope_sheet/index.html#bpy-types-dopesheet"), ("bpy.types.fmodifier*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifier"), ("bpy.types.freestyle*", "render/freestyle/index.html#bpy-types-freestyle"), + ("bpy.types.masklayer*", "movie_clip/masking/sidebar.html#bpy-types-masklayer"), ("bpy.types.movieclip*", "movie_clip/index.html#bpy-types-movieclip"), ("bpy.types.node.name*", "interface/controls/nodes/sidebar.html#bpy-types-node-name"), ("bpy.types.nodeframe*", "interface/controls/nodes/frame.html#bpy-types-nodeframe"), diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index dbc7ce650a3..bd14b2c532c 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3010,6 +3010,22 @@ def km_clip_dopesheet_editor(_params): return keymap +def km_spreadsheet_generic(_params): + items = [] + keymap = ( + "Spreadsheet Generic", + {"space_type": 'SPREADSHEET', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + *_template_space_region_type_toggle( + sidebar_key={"type": 'N', "value": 'PRESS'}, + ), + ]) + + return keymap + # ------------------------------------------------------------------------------ # Animation @@ -4090,7 +4106,7 @@ def km_pose(params): ("pose.bone_layers", {"type": 'M', "value": 'PRESS'}, None), ("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None), ("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None), - ("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None), + ("anim.keyframe_delete", {"type": 'I', "value": 'PRESS', "alt": True}, None), ("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), ("poselib.browse_interactive", {"type": 'L', "value": 'PRESS', "alt": True}, None), ("poselib.pose_add", {"type": 'L', "value": 'PRESS', "shift": True}, None), @@ -4162,7 +4178,7 @@ def km_object_mode(params): ("wm.context_toggle", {"type": 'PERIOD', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'tool_settings.use_transform_data_origin')]}), ("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None), - ("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None), + ("anim.keyframe_delete", {"type": 'I', "value": 'PRESS', "alt": True}, None), ("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), ("collection.create", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("collection.objects_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None), @@ -7067,6 +7083,7 @@ def generate_keymaps(params=None): km_image(params), km_node_generic(params), km_node_editor(params), + km_spreadsheet_generic(params), km_info(params), km_file_browser(params), km_file_browser_main(params), 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 dbf583149e3..e56783fcc21 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -3000,7 +3000,7 @@ def km_pose(params): ("anim.keyframe_insert_by_name", {"type": 'R', "value": 'PRESS', "shift": True}, {"properties": [("type", 'Scaling')]}), - ("anim.keyframe_delete_v3d", {"type": 'S', "value": 'PRESS', "alt": True}, None), + ("anim.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None), ("anim.keying_set_active_set", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), *_template_items_context_menu("VIEW3D_MT_pose_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Tools @@ -3071,7 +3071,7 @@ def km_object_mode(params): {"properties": [("type", 'Rotation')]}), ("anim.keyframe_insert_by_name", {"type": 'R', "value": 'PRESS', "shift": True}, {"properties": [("type", 'Scaling')]}), - ("anim.keyframe_delete_v3d", {"type": 'S', "value": 'PRESS', "alt": True}, None), + ("anim.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None), ("anim.keying_set_active_set", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), *_template_items_context_menu("VIEW3D_MT_object_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), diff --git a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py index 192ba2cf5b5..40dd0729fec 100644 --- a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py +++ b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py @@ -23,7 +23,7 @@ from bpy.app.handlers import persistent @persistent -def load_handler(dummy): +def load_handler(_): import bpy # 2D Animation diff --git a/release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py b/release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py index 4d1361ed22a..ef085bcd688 100644 --- a/release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py +++ b/release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py @@ -21,7 +21,7 @@ from bpy.app.handlers import persistent @persistent -def load_handler(dummy): +def load_handler(_): import bpy # Apply subdivision modifier on startup bpy.ops.object.mode_set(mode='OBJECT') diff --git a/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py b/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py index c97c3466085..ae3fdee56ac 100644 --- a/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py +++ b/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py @@ -21,7 +21,7 @@ from bpy.app.handlers import persistent @persistent -def load_handler(dummy): +def load_handler(_): from bpy import context screen = context.screen for area in screen.areas: diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index 6150789ea10..3cefaf6929b 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -331,14 +331,15 @@ class NODE_OT_active_preview_toggle(Operator): active_node = ntree.nodes.active if active_node.active_preview: - self.disable_preview(context, ntree, active_node) + self._disable_preview(context, active_node) else: - self.enable_preview(context, node_editor, ntree, active_node) + self._enable_preview(context, node_editor, ntree, active_node) return {'FINISHED'} - def enable_preview(self, context, node_editor, ntree, active_node): - spreadsheets = self.find_unpinned_spreadsheets(context) + @classmethod + def _enable_preview(cls, context, node_editor, ntree, active_node): + spreadsheets = cls._find_unpinned_spreadsheets(context) for spreadsheet in spreadsheets: spreadsheet.set_geometry_node_context(node_editor, active_node) @@ -347,14 +348,16 @@ class NODE_OT_active_preview_toggle(Operator): node.active_preview = False active_node.active_preview = True - def disable_preview(self, context, ntree, active_node): - spreadsheets = self.find_unpinned_spreadsheets(context) + @classmethod + def _disable_preview(cls, context, active_node): + spreadsheets = cls._find_unpinned_spreadsheets(context) for spreadsheet in spreadsheets: spreadsheet.context_path.clear() active_node.active_preview = False - def find_unpinned_spreadsheets(self, context): + @staticmethod + def _find_unpinned_spreadsheets(context): spreadsheets = [] for window in context.window_manager.windows: for area in window.screen.areas: diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index 6452ad8465b..daf64642f68 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -156,11 +156,11 @@ class BONE_PT_curved(BoneButtonsPanel, Panel): col = topcol.column(align=True) col.prop(bbone, "bbone_curveinx", text="Curve In X") - col.prop(bbone, "bbone_curveiny", text="In Y") + col.prop(bbone, "bbone_curveinz", text="Z") col = topcol.column(align=True) col.prop(bbone, "bbone_curveoutx", text="Curve Out X") - col.prop(bbone, "bbone_curveouty", text="Out Y") + col.prop(bbone, "bbone_curveoutz", text="Z") col = topcol.column(align=True) col.prop(bbone, "bbone_rollin", text="Roll In") @@ -168,30 +168,55 @@ class BONE_PT_curved(BoneButtonsPanel, Panel): col.prop(bone, "use_endroll_as_inroll") col = topcol.column(align=True) - col.prop(bbone, "bbone_scaleinx", text="Scale In X") - col.prop(bbone, "bbone_scaleiny", text="In Y") + col.prop(bbone, "bbone_scalein", text="Scale In") col = topcol.column(align=True) - col.prop(bbone, "bbone_scaleoutx", text="Scale Out X") - col.prop(bbone, "bbone_scaleouty", text="Out Y") + col.prop(bbone, "bbone_scaleout", text="Scale Out") col = topcol.column(align=True) col.prop(bbone, "bbone_easein", text="Ease In") col.prop(bbone, "bbone_easeout", text="Out") + col.prop(bone, "use_scale_easing") col = topcol.column(align=True) col.prop(bone, "bbone_handle_type_start", text="Start Handle") - col = col.column(align=True) - col.active = (bone.bbone_handle_type_start != 'AUTO') - col.prop_search(bone, "bbone_custom_handle_start", arm, bone_list, text="Custom") + col2 = col.column(align=True) + col2.active = (bone.bbone_handle_type_start != 'AUTO') + col2.prop_search(bone, "bbone_custom_handle_start", arm, bone_list, text="Custom") + + row = col.row(align=True) + row.use_property_split = False + split = row.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Scale") + split2 = split.split(factor=0.7) + row2 = split2.row(align=True) + row2.prop(bone, "bbone_handle_use_scale_start", index=0, text="X", toggle=True) + row2.prop(bone, "bbone_handle_use_scale_start", index=1, text="Y", toggle=True) + row2.prop(bone, "bbone_handle_use_scale_start", index=2, text="Z", toggle=True) + split2.prop(bone, "bbone_handle_use_ease_start", text="Ease", toggle=True) + row.label(icon="BLANK1") col = topcol.column(align=True) col.prop(bone, "bbone_handle_type_end", text="End Handle") - col = col.column(align=True) - col.active = (bone.bbone_handle_type_end != 'AUTO') - col.prop_search(bone, "bbone_custom_handle_end", arm, bone_list, text="Custom") + col2 = col.column(align=True) + col2.active = (bone.bbone_handle_type_end != 'AUTO') + col2.prop_search(bone, "bbone_custom_handle_end", arm, bone_list, text="Custom") + + row = col.row(align=True) + row.use_property_split = False + split = row.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Scale") + split2 = split.split(factor=0.7) + row2 = split2.row(align=True) + row2.prop(bone, "bbone_handle_use_scale_end", index=0, text="X", toggle=True) + row2.prop(bone, "bbone_handle_use_scale_end", index=1, text="Y", toggle=True) + row2.prop(bone, "bbone_handle_use_scale_end", index=2, text="Z", toggle=True) + split2.prop(bone, "bbone_handle_use_ease_end", text="Ease", toggle=True) + row.label(icon="BLANK1") class BONE_PT_relations(BoneButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index a9d7b8d71f3..b0e466d3e51 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -517,7 +517,6 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel): col.prop(mesh, "remesh_voxel_size") col.prop(mesh, "remesh_voxel_adaptivity") col.prop(mesh, "use_remesh_fix_poles") - col.prop(mesh, "use_remesh_smooth_normals") col = layout.column(heading="Preserve") col.prop(mesh, "use_remesh_preserve_volume", text="Volume") diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index e7893b8c448..ffb7b9e5c20 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -594,9 +594,9 @@ class DOPESHEET_MT_delete(Menu): class DOPESHEET_MT_context_menu(Menu): bl_label = "Dope Sheet Context Menu" - def draw(self, _context): + def draw(self, context): layout = self.layout - st = _context.space_data + st = context.space_data layout.operator_context = 'INVOKE_DEFAULT' diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index b236b2ee7cf..1ad88744b16 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -44,6 +44,10 @@ class FILEBROWSER_HT_header(Header): layout.separator_spacer() + layout.prop(params, "import_type", text="") + + layout.separator_spacer() + # Uses prop_with_popover() as popover() only adds the triangle icon in headers. layout.prop_with_popover( params, diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 11eaf55a98b..3fafa328289 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -914,6 +914,12 @@ class IMAGE_PT_active_mask_point(MASK_PT_point, Panel): bl_category = "Mask" +class IMAGE_PT_mask_display(MASK_PT_display, Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'UI' + bl_category = "Mask" + + # --- end mask --- class IMAGE_PT_snapping(Panel): @@ -1616,6 +1622,7 @@ classes = ( IMAGE_PT_active_tool, IMAGE_PT_mask, IMAGE_PT_mask_layers, + IMAGE_PT_mask_display, IMAGE_PT_active_mask_spline, IMAGE_PT_active_mask_point, IMAGE_PT_snapping, diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 07d9b0ee1f8..13a8e46e2b8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -392,6 +392,11 @@ class SEQUENCER_MT_view(Menu): layout.menu("SEQUENCER_MT_proxy") layout.operator_context = 'INVOKE_DEFAULT' + + layout.separator() + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("sequencer.refresh_all", icon='FILE_REFRESH', text="Refresh All") + layout.operator_context = 'INVOKE_DEFAULT' if is_sequencer_view: layout.separator() @@ -401,12 +406,6 @@ class SEQUENCER_MT_view(Menu): layout.menu("SEQUENCER_MT_range") layout.separator() - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("sequencer.refresh_all", icon='FILE_REFRESH', text="Refresh All") - - layout.separator() - layout.operator_context = 'INVOKE_DEFAULT' - layout.prop(st, "show_locked_time") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_spreadsheet.py b/release/scripts/startup/bl_ui/space_spreadsheet.py index 13e435a7350..360849a0c7a 100644 --- a/release/scripts/startup/bl_ui/space_spreadsheet.py +++ b/release/scripts/startup/bl_ui/space_spreadsheet.py @@ -59,9 +59,12 @@ class SPREADSHEET_HT_header(bpy.types.Header): layout.operator("spreadsheet.toggle_pin", text="", icon=pin_icon, emboss=False) layout.separator_spacer() - - if isinstance(obj, bpy.types.Object) and obj.mode == 'EDIT': - layout.prop(space, "show_only_selected", text="Selected Only") + + row = layout.row(align=True) + sub = row.row(align=True) + sub.active = self.selection_filter_available(space) + sub.prop(space, "show_only_selected", text="") + row.prop(space, "use_filter", toggle=True, icon='FILTER', icon_only=True) def draw_without_context_path(self, layout): layout.label(text="No active context") @@ -102,6 +105,17 @@ class SPREADSHEET_HT_header(bpy.types.Header): def draw_spreadsheet_context_path_icon(self, layout, space, icon='RIGHTARROW_THIN'): layout.prop(space, "display_context_path_collapsed", icon_only=True, emboss=False, icon=icon) + def selection_filter_available(self, space): + root_context = space.context_path[0] + if root_context.type != 'OBJECT': + return False + obj = root_context.object + if obj is None: + return False + if obj.type != 'MESH' or obj.mode != 'EDIT': + return False + return True + classes = ( SPREADSHEET_HT_header, ) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 26ad22d3ac2..f9359a8b4a0 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -605,6 +605,23 @@ class USERPREF_PT_system_cycles_devices(SystemPanel, CenterAlignMixIn, Panel): # col.row().prop(system, "opensubdiv_compute_type", text="") +class USERPREF_PT_system_os_settings(SystemPanel, CenterAlignMixIn, Panel): + bl_label = "Operating System Settings" + + @classmethod + def poll(cls, _context): + # Only for Windows so far + import sys + return sys.platform[:3] == "win" + + def draw_centered(self, _context, layout): + layout.label(text="Make this installation your default Blender") + split = layout.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="") + split.operator("file.associate_blend", text="Make Default") + + class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel): bl_label = "Memory & Limits" @@ -2324,6 +2341,7 @@ classes = ( USERPREF_PT_animation_fcurves, USERPREF_PT_system_cycles_devices, + USERPREF_PT_system_os_settings, USERPREF_PT_system_memory, USERPREF_PT_system_video_sequencer, USERPREF_PT_system_sound, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 5f47aa90026..80cede9ee5a 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2530,7 +2530,7 @@ class VIEW3D_MT_object_context_menu(Menu): layout.operator_menu_enum("gpencil.convert", "type", text="Convert To") if ( - obj.type in {'MESH', 'CURVE', 'SURFACE', 'GPENCIL', 'LATTICE', 'ARMATURE', 'META'} or + obj.type in {'MESH', 'CURVE', 'SURFACE', 'GPENCIL', 'LATTICE', 'ARMATURE', 'META', 'FONT'} or (obj.type == 'EMPTY' and obj.instance_collection is not None) ): layout.operator_context = 'INVOKE_REGION_WIN' @@ -3727,6 +3727,8 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu): layout = self.layout + with_freestyle = bpy.app.build_options.freestyle + layout.operator_context = 'INVOKE_REGION_WIN' # If nothing is selected @@ -3844,10 +3846,11 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu): col.operator("mesh.mark_sharp") col.operator("mesh.mark_sharp", text="Clear Sharp").clear = True - col.separator() + if with_freestyle: + col.separator() - col.operator("mesh.mark_freestyle_edge").clear = False - col.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True + col.operator("mesh.mark_freestyle_edge").clear = False + col.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True col.separator() @@ -4014,38 +4017,6 @@ class VIEW3D_MT_edit_mesh_vertices(Menu): layout.operator("object.vertex_parent_set") -class VIEW3D_MT_edit_mesh_edges_data(Menu): - bl_label = "Edge Data" - - def draw(self, context): - layout = self.layout - - layout.operator_context = 'INVOKE_REGION_WIN' - - layout.operator("transform.edge_crease") - layout.operator("transform.edge_bevelweight") - - layout.separator() - - layout.operator("mesh.mark_seam").clear = False - layout.operator("mesh.mark_seam", text="Clear Seam").clear = True - - layout.separator() - - layout.operator("mesh.mark_sharp") - layout.operator("mesh.mark_sharp", text="Clear Sharp").clear = True - - layout.operator("mesh.mark_sharp", text="Mark Sharp from Vertices").use_verts = True - props = layout.operator("mesh.mark_sharp", text="Clear Sharp from Vertices") - props.use_verts = True - props.clear = True - - layout.separator() - - layout.operator("mesh.mark_freestyle_edge").clear = False - layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True - - class VIEW3D_MT_edit_mesh_edges(Menu): bl_label = "Edge" @@ -6186,9 +6157,6 @@ class VIEW3D_PT_overlay_geometry(Panel): sub.active = overlay.show_fade_inactive sub.prop(overlay, "fade_inactive_alpha", text="Fade Inactive Geometry") - row = col.row(align=True) - row.prop(overlay, "show_mode_transfer", text="Flash on Mode Transfer") - col = layout.column(align=True) col.active = display_all @@ -7609,7 +7577,6 @@ classes = ( VIEW3D_MT_edit_mesh_extrude, VIEW3D_MT_edit_mesh_vertices, VIEW3D_MT_edit_mesh_edges, - VIEW3D_MT_edit_mesh_edges_data, VIEW3D_MT_edit_mesh_faces, VIEW3D_MT_edit_mesh_faces_data, VIEW3D_MT_edit_mesh_normals, diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 89bb2005f53..bc385faf378 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -832,7 +832,6 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): props.mode = 'VOXEL' col.prop(mesh, "remesh_voxel_adaptivity") col.prop(mesh, "use_remesh_fix_poles") - col.prop(mesh, "use_remesh_smooth_normals") col = layout.column(heading="Preserve", align=True) col.prop(mesh, "use_remesh_preserve_volume", text="Volume") diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py index 83151a3480c..2fc2d966ea9 100644 --- a/release/scripts/startup/keyingsets_builtins.py +++ b/release/scripts/startup/keyingsets_builtins.py @@ -532,7 +532,7 @@ class WholeCharacterMixin: prop_path = '["%s"]' % bpy.utils.escape_identifier(prop) try: rna_property = bone.path_resolve(prop_path, False) - except ValueError as ex: + except ValueError: # This happens when a custom property is set to None. In that case it cannot # be converted to an FCurve-compatible value, so we can't keyframe it anyway. continue @@ -582,7 +582,7 @@ class BUILTIN_KSI_DeltaLocation(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(self, context, ks, data): + def generate(self, _context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) @@ -608,7 +608,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(self, context, ks, data): + def generate(self, _context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) @@ -642,7 +642,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(self, context, ks, data): + def generate(self, _context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 9db2670d7c0..005e282fc0f 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -66,7 +66,7 @@ class GeometryNodeCategory(SortedNodeCategory): # menu entry for node group tools -def group_tools_draw(self, layout, context): +def group_tools_draw(self, layout, _context): layout.operator("node.group_make") layout.operator("node.group_ungroup") layout.separator() @@ -119,8 +119,8 @@ def node_group_items(context): if group.name.startswith('.'): continue yield NodeItem(node_tree_group_type[group.bl_idname], - group.name, - {"node_tree": "bpy.data.node_groups[%r]" % group.name}) + label=group.name, + settings={"node_tree": "bpy.data.node_groups[%r]" % group.name}) # only show input/output nodes inside node groups @@ -501,11 +501,14 @@ geometry_node_categories = [ NodeItem("ShaderNodeCombineRGB"), ]), GeometryNodeCategory("GEO_CURVE", "Curve", items=[ + NodeItem("GeometryNodeCurveSubdivide"), NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), NodeItem("GeometryNodeCurveDeform"), NodeItem("GeometryNodeMeshToCurve"), + NodeItem("GeometryNodeCurveToPoints"), NodeItem("GeometryNodeCurveLength"), + NodeItem("GeometryNodeCurveReverse"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ NodeItem("GeometryNodeBoundBox"), @@ -513,6 +516,8 @@ geometry_node_categories = [ NodeItem("GeometryNodeDeleteGeometry"), NodeItem("GeometryNodeTransform"), NodeItem("GeometryNodeJoinGeometry"), + NodeItem("GeometryNodeSeparateComponents"), + NodeItem("GeometryNodeRaycast"), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ NodeItem("GeometryNodeObjectInfo"), diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 092eec578c9..7d553b68185 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -345,7 +345,7 @@ void DM_interp_vert_data(struct DerivedMesh *source, void mesh_get_mapped_verts_coords(struct Mesh *me_eval, float (*r_cos)[3], const int totcos); -/* same as above but wont use render settings */ +/* same as above but won't use render settings */ struct Mesh *editbmesh_get_eval_cage(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *, diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 3002a9cc10d..112b8bf3ad4 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -80,11 +80,10 @@ typedef struct EditBone { /* Bendy-Bone parameters */ short segments; float roll1, roll2; - float curve_in_x, curve_in_y; - float curve_out_x, curve_out_y; + float curve_in_x, curve_in_z; + float curve_out_x, curve_out_z; float ease1, ease2; - float scale_in_x, scale_in_y; - float scale_out_x, scale_out_y; + float scale_in[3], scale_out[3]; /** for envelope scaling */ float oldlength; @@ -92,6 +91,10 @@ typedef struct EditBone { /** Type of next/prev bone handles */ char bbone_prev_type; char bbone_next_type; + /** B-Bone flags. */ + int bbone_flag; + short bbone_prev_flag; + short bbone_next_flag; /** Next/prev bones to use as handle references when calculating bbones (optional) */ struct EditBone *bbone_prev; struct EditBone *bbone_next; @@ -298,8 +301,8 @@ typedef struct BBoneSplineParameters { /* Control values. */ float ease1, ease2; float roll1, roll2; - float scale_in_x, scale_in_y, scale_out_x, scale_out_y; - float curve_in_x, curve_in_y, curve_out_x, curve_out_y; + float scale_in[3], scale_out[3]; + float curve_in_x, curve_in_z, curve_out_x, curve_out_z; } BBoneSplineParameters; void BKE_pchan_bbone_handles_get(struct bPoseChannel *pchan, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 6ad910ff8ab..1767077fa45 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 3 +#define BLENDER_FILE_SUBVERSION 4 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h index 429e294a337..3e0a343a766 100644 --- a/source/blender/blenkernel/BKE_blendfile.h +++ b/source/blender/blenkernel/BKE_blendfile.h @@ -25,6 +25,7 @@ extern "C" { struct BlendFileData; struct BlendFileReadParams; +struct BlendFileReadReport; struct ID; struct Main; struct MemFile; @@ -35,7 +36,7 @@ struct bContext; void BKE_blendfile_read_setup_ex(struct bContext *C, struct BlendFileData *bfd, const struct BlendFileReadParams *params, - struct ReportList *reports, + struct BlendFileReadReport *reports, /* Extra args. */ const bool startup_update_defaults, const char *startup_app_template); @@ -43,11 +44,11 @@ void BKE_blendfile_read_setup_ex(struct bContext *C, void BKE_blendfile_read_setup(struct bContext *C, struct BlendFileData *bfd, const struct BlendFileReadParams *params, - struct ReportList *reports); + struct BlendFileReadReport *reports); struct BlendFileData *BKE_blendfile_read(const char *filepath, const struct BlendFileReadParams *params, - struct ReportList *reports); + struct BlendFileReadReport *reports); struct BlendFileData *BKE_blendfile_read_from_memory(const void *filebuf, int filelength, diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index eb9c68f80ec..2687a5ea16c 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -156,7 +156,7 @@ struct ListBase *BKE_curve_editNurbs_get(struct Curve *cu); void BKE_curve_bevelList_free(struct ListBase *bev); void BKE_curve_bevelList_make(struct Object *ob, struct ListBase *nurbs, bool for_render); -ListBase BKE_curve_bevel_make(const struct Curve *ob); +ListBase BKE_curve_bevel_make(const struct Curve *curve); void BKE_curve_forward_diff_bezier( float q0, float q1, float q2, float q3, float *p, int it, int stride); diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index ffa0b4d0091..a2d9bbcd011 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -93,8 +93,8 @@ void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph, const struct Scene *scene, struct Object *ob, struct ListBase *dispbase, - struct Mesh **r_final, - const bool for_orco); + const bool for_orco, + struct Mesh **r_final); void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 3a1eedfd807..075a9bc0eac 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -34,6 +34,7 @@ extern "C" { struct BMLoop; struct BMesh; struct BMPartialUpdate; +struct BMeshCalcTessellation_Params; struct BoundBox; struct Depsgraph; struct Mesh; @@ -85,8 +86,17 @@ typedef struct BMEditMesh { } BMEditMesh; /* editmesh.c */ +void BKE_editmesh_looptri_calc_ex(BMEditMesh *em, + const struct BMeshCalcTessellation_Params *params); void BKE_editmesh_looptri_calc(BMEditMesh *em); +void BKE_editmesh_looptri_calc_with_partial_ex(BMEditMesh *em, + struct BMPartialUpdate *bmpinfo, + const struct BMeshCalcTessellation_Params *params); void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo); +void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, + struct BMPartialUpdate *bmpinfo); + +void BKE_editmesh_looptri_and_normals_calc(BMEditMesh *em); BMEditMesh *BKE_editmesh_create(BMesh *bm, const bool do_tessellate); BMEditMesh *BKE_editmesh_copy(BMEditMesh *em); diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index bb145580928..d0a1f102a43 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -119,6 +119,7 @@ struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src, struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src, const bool dup_frames, const bool dup_strokes); +void BKE_gpencil_layer_copy_settings(const struct bGPDlayer *gpl_src, struct bGPDlayer *gpl_dst); void BKE_gpencil_frame_copy_strokes(struct bGPDframe *gpf_src, struct bGPDframe *gpf_dst); void BKE_gpencil_frame_selected_hash(struct bGPdata *gpd, struct GHash *r_list); diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h index c6406c8478c..8fbc2112c77 100644 --- a/source/blender/blenkernel/BKE_gpencil_modifier.h +++ b/source/blender/blenkernel/BKE_gpencil_modifier.h @@ -294,6 +294,22 @@ bool BKE_gpencil_has_geometry_modifiers(struct Object *ob); bool BKE_gpencil_has_time_modifiers(struct Object *ob); bool BKE_gpencil_has_transform_modifiers(struct Object *ob); +/* Stores the maximum calculation range in the whole modifier stack for line art so the cache can + * cover everything that will be visible. */ +typedef struct GpencilLineartLimitInfo { + char min_level; + char max_level; + short edge_types; +} GpencilLineartLimitInfo; + +GpencilLineartLimitInfo BKE_gpencil_get_lineart_modifier_limits(const struct Object *ob); + +void BKE_gpencil_set_lineart_modifier_limits(struct GpencilModifierData *md, + const struct GpencilLineartLimitInfo *info, + const bool is_first_lineart); +bool BKE_gpencil_is_first_lineart_in_stack(const struct Object *ob, + const struct GpencilModifierData *md); + void BKE_gpencil_lattice_init(struct Object *ob); void BKE_gpencil_lattice_clear(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 4dc99e64cf2..3eb0ff44129 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -43,6 +43,7 @@ extern "C" { #endif struct Collection; +struct BlendFileReadReport; struct ID; struct IDOverrideLibrary; struct IDOverrideLibraryProperty; @@ -90,11 +91,11 @@ bool BKE_lib_override_library_resync(struct Main *bmain, struct Collection *override_resync_residual_storage, const bool do_hierarchy_enforce, const bool do_post_process, - struct ReportList *reports); + struct BlendFileReadReport *reports); void BKE_lib_override_library_main_resync(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, - struct ReportList *reports); + struct BlendFileReadReport *reports); void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 62837c4f2a7..8ddfb0c8eb2 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -98,7 +98,8 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh, void BKE_mesh_free(struct Mesh *me); void BKE_mesh_clear_geometry(struct Mesh *me); struct Mesh *BKE_mesh_add(struct Main *bmain, const char *name); -void BKE_mesh_copy_settings(struct Mesh *me_dst, const struct Mesh *me_src); +void BKE_mesh_copy_parameters_for_eval(struct Mesh *me_dst, const struct Mesh *me_src); +void BKE_mesh_copy_parameters(struct Mesh *me_dst, const struct Mesh *me_src); void BKE_mesh_update_customdata_pointers(struct Mesh *me, const bool do_ensure_tess_cd); void BKE_mesh_ensure_skin_customdata(struct Mesh *me); @@ -134,7 +135,10 @@ bool BKE_mesh_clear_facemap_customdata(struct Mesh *me); float (*BKE_mesh_orco_verts_get(struct Object *ob))[3]; void BKE_mesh_orco_verts_transform(struct Mesh *me, float (*orco)[3], int totvert, int invert); -int test_index_face(struct MFace *mface, struct CustomData *mfdata, int mfindex, int nr); +int BKE_mesh_mface_index_validate(struct MFace *mface, + struct CustomData *mfdata, + int mfindex, + int nr); struct Mesh *BKE_mesh_from_object(struct Object *ob); void BKE_mesh_assign_object(struct Main *bmain, struct Object *ob, struct Mesh *me); void BKE_mesh_from_metaball(struct ListBase *lb, struct Mesh *me); @@ -246,7 +250,6 @@ bool BKE_mesh_minmax(const struct Mesh *me, float r_min[3], float r_max[3]); void BKE_mesh_transform(struct Mesh *me, const float mat[4][4], bool do_keys); void BKE_mesh_translate(struct Mesh *me, const float offset[3], const bool do_keys); -void BKE_mesh_tessface_calc(struct Mesh *mesh); void BKE_mesh_tessface_ensure(struct Mesh *mesh); void BKE_mesh_tessface_clear(struct Mesh *mesh); @@ -269,6 +272,32 @@ void BKE_mesh_vert_coords_apply_with_mat4(struct Mesh *mesh, void BKE_mesh_vert_coords_apply(struct Mesh *mesh, const float (*vert_coords)[3]); void BKE_mesh_vert_normals_apply(struct Mesh *mesh, const short (*vert_normals)[3]); +/* *** mesh_tessellate.c *** */ + +int BKE_mesh_tessface_calc_ex(struct CustomData *fdata, + struct CustomData *ldata, + struct CustomData *pdata, + struct MVert *mvert, + int totface, + int totloop, + int totpoly, + const bool do_face_nor_copy); +void BKE_mesh_tessface_calc(struct Mesh *mesh); + +void BKE_mesh_recalc_looptri(const struct MLoop *mloop, + const struct MPoly *mpoly, + const struct MVert *mvert, + int totloop, + int totpoly, + struct MLoopTri *mlooptri); +void BKE_mesh_recalc_looptri_with_normals(const struct MLoop *mloop, + const struct MPoly *mpoly, + const struct MVert *mvert, + int totloop, + int totpoly, + struct MLoopTri *mlooptri, + const float (*poly_normals)[3]); + /* *** mesh_evaluate.c *** */ void BKE_mesh_calc_normals_mapping_simple(struct Mesh *me); @@ -508,45 +537,6 @@ void BKE_mesh_calc_volume(const struct MVert *mverts, float r_center[3]); /* tessface */ -void BKE_mesh_loops_to_mface_corners(struct CustomData *fdata, - struct CustomData *ldata, - struct CustomData *pdata, - unsigned int lindex[4], - int findex, - const int polyindex, - const int mf_len, - const int numUV, - const int numCol, - const bool hasPCol, - const bool hasOrigSpace, - const bool hasLNor); -void BKE_mesh_loops_to_tessdata(struct CustomData *fdata, - struct CustomData *ldata, - struct MFace *mface, - const int *polyindices, - unsigned int (*loopindices)[4], - const int num_faces); -void BKE_mesh_tangent_loops_to_tessdata(struct CustomData *fdata, - struct CustomData *ldata, - struct MFace *mface, - const int *polyindices, - unsigned int (*loopindices)[4], - const int num_faces, - const char *layer_name); -int BKE_mesh_tessface_calc_ex(struct CustomData *fdata, - struct CustomData *ldata, - struct CustomData *pdata, - struct MVert *mvert, - int totface, - int totloop, - int totpoly, - const bool do_face_nor_copy); -void BKE_mesh_recalc_looptri(const struct MLoop *mloop, - const struct MPoly *mpoly, - const struct MVert *mvert, - int totloop, - int totpoly, - struct MLoopTri *mlooptri); void BKE_mesh_convert_mfaces_to_mpolys(struct Mesh *mesh); void BKE_mesh_do_versions_convert_mfaces_to_mpolys(struct Mesh *mesh); void BKE_mesh_convert_mfaces_to_mpolys_ex(struct ID *id, diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index f504650e349..dc747ba5b42 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -28,6 +28,11 @@ struct Mesh; +namespace blender::bke { +struct ReadAttributeLookup; +class OutputAttribute; +} // namespace blender::bke + namespace blender::bke::mesh_surface_sample { using fn::CPPType; @@ -35,6 +40,8 @@ using fn::GMutableSpan; using fn::GSpan; using fn::GVArray; +Span<MLoopTri> get_mesh_looptris(const Mesh &mesh); + void sample_point_attribute(const Mesh &mesh, Span<int> looptri_indices, Span<float3> bary_coords, @@ -52,4 +59,39 @@ void sample_face_attribute(const Mesh &mesh, const GVArray &data_in, GMutableSpan data_out); +enum class eAttributeMapMode { + INTERPOLATED, + NEAREST, +}; + +/** + * A utility class that performs attribute interpolation from a source mesh. + * + * The interpolator is only valid as long as the mesh is valid. + * Barycentric weights are needed when interpolating point or corner domain attributes, + * these are computed lazily when needed and re-used. + */ +class MeshAttributeInterpolator { + private: + const Mesh *mesh_; + const Span<float3> positions_; + const Span<int> looptri_indices_; + + Array<float3> bary_coords_; + Array<float3> nearest_weights_; + + public: + MeshAttributeInterpolator(const Mesh *mesh, + const Span<float3> positions, + const Span<int> looptri_indices); + + void sample_attribute(const ReadAttributeLookup &src_attribute, + OutputAttribute &dst_attribute, + eAttributeMapMode mode); + + protected: + Span<float3> ensure_barycentric_coords(); + Span<float3> ensure_nearest_weights(); +}; + } // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 48b4540e3d9..0b4e1191956 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -324,7 +324,7 @@ typedef struct ModifierTypeInfo { /** * True when a deform modifier uses normals, the requiredDataMask - * cant be used here because that refers to a normal layer whereas + * can't be used here because that refers to a normal layer whereas * in this case we need to know if the deform modifier uses normals. * * this is needed because applying 2 deform modifiers will give the diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 5fa7d2b2a95..87140964ccb 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1434,7 +1434,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_LENGTH 1054 #define GEO_NODE_SELECT_BY_MATERIAL 1055 #define GEO_NODE_CONVEX_HULL 1056 -#define GEO_NODE_CURVE_DEFORM 1057 +#define GEO_NODE_CURVE_TO_POINTS 1057 +#define GEO_NODE_CURVE_REVERSE 1058 +#define GEO_NODE_SEPARATE_COMPONENTS 1059 +#define GEO_NODE_CURVE_SUBDIVIDE 1060 +#define GEO_NODE_RAYCAST 1061 +#define GEO_NODE_CURVE_DEFORM 1062 /** \} */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index f3a5c794de8..b0b1657c162 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -337,9 +337,9 @@ bool BKE_object_obdata_texspace_get(struct Object *ob, float **r_loc, float **r_size); -struct Mesh *BKE_object_get_evaluated_mesh(struct Object *object); -struct Mesh *BKE_object_get_pre_modified_mesh(struct Object *object); -struct Mesh *BKE_object_get_original_mesh(struct Object *object); +struct Mesh *BKE_object_get_evaluated_mesh(const struct Object *object); +struct Mesh *BKE_object_get_pre_modified_mesh(const struct Object *object); +struct Mesh *BKE_object_get_original_mesh(const struct Object *object); /* Lattice accessors. * These functions return either the regular lattice, or the edit-mode lattice, @@ -377,6 +377,7 @@ void BKE_object_runtime_reset_on_copy(struct Object *object, const int flag); void BKE_object_runtime_free_data(struct Object *object); void BKE_object_batch_cache_dirty_tag(struct Object *ob); +void BKE_object_data_batch_cache_dirty_tag(struct ID *object_data); /* this function returns a superset of the scenes selection based on relationships */ diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index 170eb4ba662..8731162b720 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -33,7 +33,7 @@ extern "C" { #endif /* Point cache clearing option, for BKE_ptcache_id_clear, before - * and after are non inclusive (they wont remove the cfra) */ + * and after are non-inclusive (they won't remove the cfra) */ #define PTCACHE_CLEAR_ALL 0 #define PTCACHE_CLEAR_FRAME 1 #define PTCACHE_CLEAR_BEFORE 2 diff --git a/source/blender/blenkernel/BKE_shader_fx.h b/source/blender/blenkernel/BKE_shader_fx.h index e385f77565b..7e3783a3ee9 100644 --- a/source/blender/blenkernel/BKE_shader_fx.h +++ b/source/blender/blenkernel/BKE_shader_fx.h @@ -160,6 +160,8 @@ void BKE_shaderfx_free_ex(struct ShaderFxData *fx, const int flag); void BKE_shaderfx_free(struct ShaderFxData *fx); bool BKE_shaderfx_unique_name(struct ListBase *shaderfx, struct ShaderFxData *fx); bool BKE_shaderfx_depends_ontime(struct ShaderFxData *fx); +bool BKE_shaderfx_is_nonlocal_in_liboverride(const struct Object *ob, + const struct ShaderFxData *shaderfx); struct ShaderFxData *BKE_shaderfx_findby_type(struct Object *ob, ShaderFxType type); struct ShaderFxData *BKE_shaderfx_findby_name(struct Object *ob, const char *name); void BKE_shaderfx_copydata_generic(const struct ShaderFxData *fx_src, struct ShaderFxData *fx_dst); diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 60a9b9bb89a..8b8de115330 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -49,7 +49,7 @@ using SplinePtr = std::unique_ptr<Spline>; * evaluation happens in a layer on top of the evaluated points generated by the derived types. * * There are a few methods to evaluate a spline: - * 1. #evaluated_positions and #interpolate_to_evaluated_points give data for the initial + * 1. #evaluated_positions and #interpolate_to_evaluated give data for the initial * evaluated points, depending on the resolution. * 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups * along the length of a curve. @@ -74,8 +74,7 @@ class Spline { Minimum, Tangent, }; - /* Only #Zup is supported at the moment. */ - NormalCalculationMode normal_mode; + NormalCalculationMode normal_mode = Minimum; blender::bke::CustomDataAttributes attributes; @@ -108,9 +107,9 @@ class Spline { copy_base_settings(other, *this); } - virtual SplinePtr copy() const = 0; - /** Return a new spline with the same type and settings like "cyclic", but without any data. */ - virtual SplinePtr copy_settings() const = 0; + SplinePtr copy() const; + SplinePtr copy_only_settings() const; + SplinePtr copy_without_attributes() const; Spline::Type type() const; @@ -177,23 +176,23 @@ class Spline { LookupResult lookup_data_from_index_factor(const float index_factor) const; - void sample_based_on_index_factors(const blender::fn::GVArray &src, - blender::Span<float> index_factors, - blender::fn::GMutableSpan dst) const; + void sample_with_index_factors(const blender::fn::GVArray &src, + blender::Span<float> index_factors, + blender::fn::GMutableSpan dst) const; template<typename T> - void sample_based_on_index_factors(const blender::VArray<T> &src, - blender::Span<float> index_factors, - blender::MutableSpan<T> dst) const + void sample_with_index_factors(const blender::VArray<T> &src, + blender::Span<float> index_factors, + blender::MutableSpan<T> dst) const { - this->sample_based_on_index_factors( + this->sample_with_index_factors( blender::fn::GVArray_For_VArray(src), index_factors, blender::fn::GMutableSpan(dst)); } template<typename T> - void sample_based_on_index_factors(blender::Span<T> src, - blender::Span<float> index_factors, - blender::MutableSpan<T> dst) const + void sample_with_index_factors(blender::Span<T> src, + blender::Span<float> index_factors, + blender::MutableSpan<T> dst) const { - this->sample_based_on_index_factors(blender::VArray_For_Span(src), index_factors, dst); + this->sample_with_index_factors(blender::VArray_For_Span(src), index_factors, dst); } /** @@ -201,24 +200,21 @@ class Spline { * evaluated points. For poly splines, the lifetime of the returned virtual array must not * exceed the lifetime of the input data. */ - virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points( - const blender::fn::GVArray &source_data) const = 0; - blender::fn::GVArrayPtr interpolate_to_evaluated_points(blender::fn::GSpan data) const; + virtual blender::fn::GVArrayPtr interpolate_to_evaluated( + const blender::fn::GVArray &src) const = 0; + blender::fn::GVArrayPtr interpolate_to_evaluated(blender::fn::GSpan data) const; template<typename T> - blender::fn::GVArray_Typed<T> interpolate_to_evaluated_points(blender::Span<T> data) const + blender::fn::GVArray_Typed<T> interpolate_to_evaluated(blender::Span<T> data) const { - return blender::fn::GVArray_Typed<T>( - this->interpolate_to_evaluated_points(blender::fn::GSpan(data))); + return blender::fn::GVArray_Typed<T>(this->interpolate_to_evaluated(blender::fn::GSpan(data))); } protected: virtual void correct_end_tangents() const = 0; - /** Copy settings stored in the base spline class. */ - static void copy_base_settings(const Spline &src, Spline &dst) - { - dst.normal_mode = src.normal_mode; - dst.is_cyclic_ = src.is_cyclic_; - } + virtual void copy_settings(Spline &dst) const = 0; + virtual void copy_data(Spline &dst) const = 0; + + static void copy_base_settings(const Spline &src, Spline &dst); }; /** @@ -271,8 +267,6 @@ class BezierSpline final : public Spline { mutable bool mapping_cache_dirty_ = true; public: - virtual SplinePtr copy() const final; - SplinePtr copy_settings() const final; BezierSpline() : Spline(Type::Bezier) { } @@ -339,16 +333,19 @@ class BezierSpline final : public Spline { }; InterpolationData interpolation_data_from_index_factor(const float index_factor) const; - virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points( - const blender::fn::GVArray &source_data) const override; + virtual blender::fn::GVArrayPtr interpolate_to_evaluated(const blender::fn::GVArray &src) const; + + void evaluate_segment(const int index, + const int next_index, + blender::MutableSpan<blender::float3> positions) const; + bool segment_is_vector(const int start_index) const; private: - void ensure_auto_handles() const; void correct_end_tangents() const final; - bool segment_is_vector(const int start_index) const; - void evaluate_bezier_segment(const int index, - const int next_index, - blender::MutableSpan<blender::float3> positions) const; + void copy_settings(Spline &dst) const final; + void copy_data(Spline &dst) const final; + + void ensure_auto_handles() const; }; /** @@ -413,8 +410,6 @@ class NURBSpline final : public Spline { mutable bool position_cache_dirty_ = true; public: - SplinePtr copy() const final; - SplinePtr copy_settings() const final; NURBSpline() : Spline(Type::NURBS) { } @@ -461,13 +456,15 @@ class NURBSpline final : public Spline { blender::Span<blender::float3> evaluated_positions() const final; - blender::fn::GVArrayPtr interpolate_to_evaluated_points( - const blender::fn::GVArray &source_data) const final; + blender::fn::GVArrayPtr interpolate_to_evaluated(const blender::fn::GVArray &src) const final; protected: void correct_end_tangents() const final; + void copy_settings(Spline &dst) const final; + void copy_data(Spline &dst) const final; + void calculate_knots() const; - void calculate_basis_cache() const; + blender::Span<BasisCache> calculate_basis_cache() const; }; /** @@ -481,8 +478,6 @@ class PolySpline final : public Spline { blender::Vector<float> tilts_; public: - SplinePtr copy() const final; - SplinePtr copy_settings() const final; PolySpline() : Spline(Type::Poly) { } @@ -511,11 +506,12 @@ class PolySpline final : public Spline { blender::Span<blender::float3> evaluated_positions() const final; - blender::fn::GVArrayPtr interpolate_to_evaluated_points( - const blender::fn::GVArray &source_data) const final; + blender::fn::GVArrayPtr interpolate_to_evaluated(const blender::fn::GVArray &src) const final; protected: void correct_end_tangents() const final; + void copy_settings(Spline &dst) const final; + void copy_data(Spline &dst) const final; }; /** @@ -540,6 +536,7 @@ struct CurveEval { blender::Span<SplinePtr> splines() const; blender::MutableSpan<SplinePtr> splines(); + void resize(const int size); void add_spline(SplinePtr spline); void remove_splines(blender::IndexMask mask); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 021d7e15814..0f36887b234 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -194,6 +194,7 @@ set(SRC intern/mesh_runtime.c intern/mesh_sample.cc intern/mesh_tangent.c + intern/mesh_tessellate.c intern/mesh_validate.c intern/mesh_validate.cc intern/mesh_wrapper.c diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index d4dd7e248d5..6caed3936d4 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -42,6 +42,7 @@ #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_task.h" +#include "BLI_task.hh" #include "BLI_utildefines.h" #include "BLI_vector.hh" @@ -962,7 +963,7 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md, /* Return an empty mesh instead of null. */ if (mesh_output == nullptr) { mesh_output = BKE_mesh_new_nomain(0, 0, 0, 0, 0); - BKE_mesh_copy_settings(mesh_output, input_mesh); + BKE_mesh_copy_parameters_for_eval(mesh_output, input_mesh); } } @@ -1463,10 +1464,14 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, BLI_assert(runtime->eval_mutex != nullptr); BLI_mutex_lock((ThreadMutex *)runtime->eval_mutex); if (runtime->mesh_eval == nullptr) { - mesh_final = BKE_mesh_copy_for_eval(mesh_input, true); - mesh_calc_modifier_final_normals(mesh_input, &final_datamask, sculpt_dyntopo, mesh_final); - mesh_calc_finalize(mesh_input, mesh_final); - runtime->mesh_eval = mesh_final; + /* Isolate since computing normals is multithreaded and we are holding a lock. */ + blender::threading::isolate_task([&] { + mesh_final = BKE_mesh_copy_for_eval(mesh_input, true); + mesh_calc_modifier_final_normals( + mesh_input, &final_datamask, sculpt_dyntopo, mesh_final); + mesh_calc_finalize(mesh_input, mesh_final); + runtime->mesh_eval = mesh_final; + }); } BLI_mutex_unlock((ThreadMutex *)runtime->eval_mutex); } @@ -1567,7 +1572,7 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, /* BMESH_ONLY, ensure tessface's used for drawing, * but don't recalculate if the last modifier in the stack gives us tessfaces * check if the derived meshes are DM_TYPE_EDITBMESH before calling, this isn't essential - * but quiets annoying error messages since tessfaces wont be created. */ + * but quiets annoying error messages since tessfaces won't be created. */ if (final_datamask->fmask & CD_MASK_MFACE) { if (mesh_final->edit_mesh == nullptr) { BKE_mesh_tessface_ensure(mesh_final); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index a7e36b09516..71d242e9c79 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -665,8 +665,8 @@ bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name) unit_axis_angle(chan->rotAxis, &chan->rotAngle); chan->size[0] = chan->size[1] = chan->size[2] = 1.0f; - chan->scale_in_x = chan->scale_in_y = 1.0f; - chan->scale_out_x = chan->scale_out_y = 1.0f; + copy_v3_fl(chan->scale_in, 1.0f); + copy_v3_fl(chan->scale_out, 1.0f); chan->limitmin[0] = chan->limitmin[1] = chan->limitmin[2] = -M_PI; chan->limitmax[0] = chan->limitmax[1] = chan->limitmax[2] = M_PI; @@ -846,8 +846,9 @@ void BKE_pose_copy_data_ex(bPose **dst, } if (copy_constraints) { - BKE_constraints_copy_ex( - &listb, &pchan->constraints, flag, true); /* BKE_constraints_copy NULLs listb */ + /* #BKE_constraints_copy NULL's `listb` */ + BKE_constraints_copy_ex(&listb, &pchan->constraints, flag, true); + pchan->constraints = listb; /* XXX: This is needed for motionpath drawing to work. @@ -1659,11 +1660,12 @@ void BKE_pose_rest(bPose *pose, bool selected_bones_only) pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f; pchan->roll1 = pchan->roll2 = 0.0f; - pchan->curve_in_x = pchan->curve_in_y = 0.0f; - pchan->curve_out_x = pchan->curve_out_y = 0.0f; + pchan->curve_in_x = pchan->curve_in_z = 0.0f; + pchan->curve_out_x = pchan->curve_out_z = 0.0f; pchan->ease1 = pchan->ease2 = 0.0f; - pchan->scale_in_x = pchan->scale_in_y = 1.0f; - pchan->scale_out_x = pchan->scale_out_y = 1.0f; + + copy_v3_fl(pchan->scale_in, 1.0f); + copy_v3_fl(pchan->scale_out, 1.0f); pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); } @@ -1686,15 +1688,14 @@ void BKE_pose_copy_pchan_result(bPoseChannel *pchanto, const bPoseChannel *pchan pchanto->roll1 = pchanfrom->roll1; pchanto->roll2 = pchanfrom->roll2; pchanto->curve_in_x = pchanfrom->curve_in_x; - pchanto->curve_in_y = pchanfrom->curve_in_y; + pchanto->curve_in_z = pchanfrom->curve_in_z; pchanto->curve_out_x = pchanfrom->curve_out_x; - pchanto->curve_out_y = pchanfrom->curve_out_y; + pchanto->curve_out_z = pchanfrom->curve_out_z; pchanto->ease1 = pchanfrom->ease1; pchanto->ease2 = pchanfrom->ease2; - pchanto->scale_in_x = pchanfrom->scale_in_x; - pchanto->scale_in_y = pchanfrom->scale_in_y; - pchanto->scale_out_x = pchanfrom->scale_out_x; - pchanto->scale_out_y = pchanfrom->scale_out_y; + + copy_v3_v3(pchanto->scale_in, pchanfrom->scale_in); + copy_v3_v3(pchanto->scale_out, pchanfrom->scale_out); pchanto->rotmode = pchanfrom->rotmode; pchanto->flag = pchanfrom->flag; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 4ea71922df5..a57e1d6b2dd 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -853,6 +853,7 @@ bool bone_autoside_name( static void equalize_cubic_bezier(const float control[4][3], int temp_segments, int final_segments, + const float *segment_scales, float *r_t_points) { float(*coords)[3] = BLI_array_alloca(coords, temp_segments + 1); @@ -877,12 +878,19 @@ static void equalize_cubic_bezier(const float control[4][3], } /* Go over distances and calculate new parameter values. */ - float dist_step = pdist[temp_segments] / final_segments; + float dist_step = pdist[temp_segments]; + float dist = 0, sum = 0; + + for (int i = 0; i < final_segments; i++) { + sum += segment_scales[i]; + } + + dist_step /= sum; r_t_points[0] = 0.0f; for (int i = 1, nr = 1; i <= final_segments; i++) { - float dist = i * dist_step; + dist += segment_scales[i - 1] * dist_step; /* We're looking for location (distance) 'dist' in the array. */ while ((nr < temp_segments) && (dist >= pdist[nr])) { @@ -951,7 +959,7 @@ void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, { bPoseChannel *next, *prev; Bone *bone = pchan->bone; - float imat[4][4], posemat[4][4]; + float imat[4][4], posemat[4][4], tmpmat[4][4]; float delta[3]; memset(param, 0, sizeof(*param)); @@ -988,6 +996,11 @@ void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, invert_m4_m4(imat, pchan->pose_mat); } + float prev_scale[3], next_scale[3]; + + copy_v3_fl(prev_scale, 1.0f); + copy_v3_fl(next_scale, 1.0f); + if (prev) { float h1[3]; bool done = false; @@ -1033,6 +1046,12 @@ void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, if (!param->prev_bbone) { /* Find the previous roll to interpolate. */ mul_m4_m4m4(param->prev_mat, imat, rest ? prev->bone->arm_mat : prev->pose_mat); + + /* Retrieve the local scale of the bone if necessary. */ + if ((bone->bbone_prev_flag & BBONE_HANDLE_SCALE_ANY) && !rest) { + BKE_armature_mat_pose_to_bone(prev, prev->pose_mat, tmpmat); + mat4_to_size(prev_scale, tmpmat); + } } } @@ -1080,6 +1099,12 @@ void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, /* Find the next roll to interpolate as well. */ mul_m4_m4m4(param->next_mat, imat, rest ? next->bone->arm_mat : next->pose_mat); + + /* Retrieve the local scale of the bone if necessary. */ + if ((bone->bbone_next_flag & BBONE_HANDLE_SCALE_ANY) && !rest) { + BKE_armature_mat_pose_to_bone(next, next->pose_mat, tmpmat); + mat4_to_size(next_scale, tmpmat); + } } /* Add effects from bbone properties over the top @@ -1103,7 +1128,7 @@ void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, param->roll1 = bone->roll1 + (!rest ? pchan->roll1 : 0.0f); param->roll2 = bone->roll2 + (!rest ? pchan->roll2 : 0.0f); - if (bone->flag & BONE_ADD_PARENT_END_ROLL) { + if (bone->bbone_flag & BBONE_ADD_PARENT_END_ROLL) { if (prev) { if (prev->bone) { param->roll1 += prev->bone->roll2; @@ -1115,17 +1140,61 @@ void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, } } - param->scale_in_x = bone->scale_in_x * (!rest ? pchan->scale_in_x : 1.0f); - param->scale_in_y = bone->scale_in_y * (!rest ? pchan->scale_in_y : 1.0f); - param->scale_out_x = bone->scale_out_x * (!rest ? pchan->scale_out_x : 1.0f); - param->scale_out_y = bone->scale_out_y * (!rest ? pchan->scale_out_y : 1.0f); + copy_v3_v3(param->scale_in, bone->scale_in); + copy_v3_v3(param->scale_out, bone->scale_out); - /* Extra curve x / y */ + if (!rest) { + mul_v3_v3(param->scale_in, pchan->scale_in); + mul_v3_v3(param->scale_out, pchan->scale_out); + } + + /* Extra curve x / z */ param->curve_in_x = bone->curve_in_x + (!rest ? pchan->curve_in_x : 0.0f); - param->curve_in_y = bone->curve_in_y + (!rest ? pchan->curve_in_y : 0.0f); + param->curve_in_z = bone->curve_in_z + (!rest ? pchan->curve_in_z : 0.0f); param->curve_out_x = bone->curve_out_x + (!rest ? pchan->curve_out_x : 0.0f); - param->curve_out_y = bone->curve_out_y + (!rest ? pchan->curve_out_y : 0.0f); + param->curve_out_z = bone->curve_out_z + (!rest ? pchan->curve_out_z : 0.0f); + + if (bone->bbone_flag & BBONE_SCALE_EASING) { + param->ease1 *= param->scale_in[1]; + param->curve_in_x *= param->scale_in[1]; + param->curve_in_z *= param->scale_in[1]; + + param->ease2 *= param->scale_out[1]; + param->curve_out_x *= param->scale_out[1]; + param->curve_out_z *= param->scale_out[1]; + } + + /* Custom handle scale. */ + if (bone->bbone_prev_flag & BBONE_HANDLE_SCALE_X) { + param->scale_in[0] *= prev_scale[0]; + } + if (bone->bbone_prev_flag & BBONE_HANDLE_SCALE_Y) { + param->scale_in[1] *= prev_scale[1]; + } + if (bone->bbone_prev_flag & BBONE_HANDLE_SCALE_Z) { + param->scale_in[2] *= prev_scale[2]; + } + if (bone->bbone_prev_flag & BBONE_HANDLE_SCALE_EASE) { + param->ease1 *= prev_scale[1]; + param->curve_in_x *= prev_scale[1]; + param->curve_in_z *= prev_scale[1]; + } + + if (bone->bbone_next_flag & BBONE_HANDLE_SCALE_X) { + param->scale_out[0] *= next_scale[0]; + } + if (bone->bbone_next_flag & BBONE_HANDLE_SCALE_Y) { + param->scale_out[1] *= next_scale[1]; + } + if (bone->bbone_next_flag & BBONE_HANDLE_SCALE_Z) { + param->scale_out[2] *= next_scale[2]; + } + if (bone->bbone_next_flag & BBONE_HANDLE_SCALE_EASE) { + param->ease2 *= next_scale[1]; + param->curve_out_x *= next_scale[1]; + param->curve_out_z *= next_scale[1]; + } } } @@ -1250,13 +1319,13 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, * in the bone length getting scaled up too (from 1 to 8), causing the curve to flatten out. */ const float xscale_correction = (param->do_scale) ? param->scale[0] : 1.0f; - const float yscale_correction = (param->do_scale) ? param->scale[2] : 1.0f; + const float zscale_correction = (param->do_scale) ? param->scale[2] : 1.0f; h1[0] += param->curve_in_x * xscale_correction; - h1[2] += param->curve_in_y * yscale_correction; + h1[2] += param->curve_in_z * zscale_correction; h2[0] += param->curve_out_x * xscale_correction; - h2[2] += param->curve_out_y * yscale_correction; + h2[2] += param->curve_out_z * zscale_correction; } } @@ -1266,7 +1335,7 @@ static void make_bbone_spline_matrix(BBoneSplineParameters *param, const float axis[3], float roll, float scalex, - float scaley, + float scalez, float result[4][4]) { float mat3[3][3]; @@ -1283,7 +1352,7 @@ static void make_bbone_spline_matrix(BBoneSplineParameters *param, /* BBone scale... */ mul_v3_fl(result[0], scalex); - mul_v3_fl(result[2], scaley); + mul_v3_fl(result[2], scalez); } /* Fade from first to second derivative when the handle is very short. */ @@ -1329,9 +1398,25 @@ int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, copy_v3_v3(bezt_controls[1], h1); zero_v3(bezt_controls[0]); + /* Compute lengthwise segment scale. */ + float segment_scales[MAX_BBONE_SUBDIV]; + + CLAMP_MIN(param->scale_in[1], 0.0001f); + CLAMP_MIN(param->scale_out[1], 0.0001f); + + const float log_scale_in_len = logf(param->scale_in[1]); + const float log_scale_out_len = logf(param->scale_out[1]); + + for (int i = 0; i < param->segments; i++) { + const float fac = ((float)i) / (param->segments - 1); + segment_scales[i] = expf(interpf(log_scale_out_len, log_scale_in_len, fac)); + } + + /* Compute segment vertex offsets along the curve length. */ float bezt_points[MAX_BBONE_SUBDIV + 1]; - equalize_cubic_bezier(bezt_controls, MAX_BBONE_SUBDIV, param->segments, bezt_points); + equalize_cubic_bezier( + bezt_controls, MAX_BBONE_SUBDIV, param->segments, segment_scales, bezt_points); /* Deformation uses N+1 matrices computed at points between the segments. */ if (for_deform) { @@ -1352,8 +1437,8 @@ int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, bezt_controls[0], axis, roll1, - param->scale_in_x, - param->scale_in_y, + param->scale_in[0], + param->scale_in[2], result_array[0].mat); for (int a = 1; a < param->segments; a++) { @@ -1361,11 +1446,11 @@ int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, float fac = ((float)a) / param->segments; float roll = interpf(roll2, roll1, fac); - float scalex = interpf(param->scale_out_x, param->scale_in_x, fac); - float scaley = interpf(param->scale_out_y, param->scale_in_y, fac); + float scalex = interpf(param->scale_out[0], param->scale_in[0], fac); + float scalez = interpf(param->scale_out[2], param->scale_in[2], fac); make_bbone_spline_matrix( - param, scalemats, cur, axis, roll, scalex, scaley, result_array[a].mat); + param, scalemats, cur, axis, roll, scalex, scalez, result_array[a].mat); } negate_v3(bezt_deriv2[1]); @@ -1375,8 +1460,8 @@ int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, bezt_controls[3], axis, roll2, - param->scale_out_x, - param->scale_out_y, + param->scale_out[0], + param->scale_out[2], result_array[param->segments].mat); } /* Other code (e.g. display) uses matrices for the segments themselves. */ @@ -1390,11 +1475,11 @@ int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, float fac = (a + 0.5f) / param->segments; float roll = interpf(roll2, roll1, fac); - float scalex = interpf(param->scale_out_x, param->scale_in_x, fac); - float scaley = interpf(param->scale_out_y, param->scale_in_y, fac); + float scalex = interpf(param->scale_out[0], param->scale_in[0], fac); + float scalez = interpf(param->scale_out[2], param->scale_in[2], fac); make_bbone_spline_matrix( - param, scalemats, prev, axis, roll, scalex, scaley, result_array[a].mat); + param, scalemats, prev, axis, roll, scalex, scalez, result_array[a].mat); copy_v3_v3(prev, cur); } } @@ -2296,7 +2381,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected) return; } - /* in some cases when rigs change, we cant synchronize + /* in some cases when rigs change, we can't synchronize * to avoid crashing check for possible errors here */ for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { if (pchan->bone->layer & layer_protected) { diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c index ec8962d5f6d..c8fedc086b8 100644 --- a/source/blender/blenkernel/intern/blender_copybuffer.c +++ b/source/blender/blenkernel/intern/blender_copybuffer.c @@ -87,7 +87,7 @@ bool BKE_copybuffer_read(Main *bmain_dst, ReportList *reports, const uint64_t id_types_mask) { - BlendHandle *bh = BLO_blendhandle_from_file(libname, reports); + BlendHandle *bh = BLO_blendhandle_from_file(libname, &(BlendFileReadReport){.reports = reports}); if (bh == NULL) { /* Error reports will have been made by BLO_blendhandle_from_file(). */ return false; @@ -106,7 +106,7 @@ bool BKE_copybuffer_read(Main *bmain_dst, /* Append, rather than linking. */ Library *lib = BLI_findstring(&bmain_dst->libraries, libname, offsetof(Library, filepath_abs)); BKE_library_make_local(bmain_dst, lib, NULL, true, false); - /* Important we unset, otherwise these object wont + /* Important we unset, otherwise these object won't * link into other scenes from this blend file. */ BKE_main_id_tag_all(bmain_dst, LIB_TAG_PRE_EXISTING, false); @@ -133,7 +133,7 @@ int BKE_copybuffer_paste(bContext *C, BlendHandle *bh; const int id_tag_extra = 0; - bh = BLO_blendhandle_from_file(libname, reports); + bh = BLO_blendhandle_from_file(libname, &(BlendFileReadReport){.reports = reports}); if (bh == NULL) { /* error reports will have been made by BLO_blendhandle_from_file() */ @@ -166,7 +166,7 @@ int BKE_copybuffer_paste(bContext *C, lib = BLI_findstring(&bmain->libraries, libname, offsetof(Library, filepath_abs)); BKE_library_make_local(bmain, lib, NULL, true, false); - /* important we unset, otherwise these object wont + /* important we unset, otherwise these object won't * link into other scenes from this blend file */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index 02076823675..6f47cd1336e 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -78,9 +78,10 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu, if (UNDO_DISK) { const struct BlendFileReadParams params = {0}; - struct BlendFileData *bfd = BKE_blendfile_read(mfu->filename, ¶ms, NULL); + struct BlendFileData *bfd = BKE_blendfile_read( + mfu->filename, ¶ms, &(BlendFileReadReport){NULL}); if (bfd != NULL) { - BKE_blendfile_read_setup(C, bfd, ¶ms, NULL); + BKE_blendfile_read_setup(C, bfd, ¶ms, &(BlendFileReadReport){NULL}); success = true; } } @@ -93,7 +94,7 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu, struct BlendFileData *bfd = BKE_blendfile_read_from_memfile( bmain, &mfu->memfile, ¶ms, NULL); if (bfd != NULL) { - BKE_blendfile_read_setup(C, bfd, ¶ms, NULL); + BKE_blendfile_read_setup(C, bfd, ¶ms, &(BlendFileReadReport){NULL}); success = true; } } @@ -105,7 +106,7 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu, if (success) { /* important not to update time here, else non keyed transforms are lost */ - DEG_on_visible_update(bmain, false); + DEG_tag_on_visible_update(bmain, false); } return success; diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 54fd3f55c31..9ea1c8fedda 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -36,6 +36,8 @@ #include "BLI_system.h" #include "BLI_utildefines.h" +#include "PIL_time.h" + #include "IMB_colormanagement.h" #include "BKE_addon.h" @@ -136,7 +138,7 @@ static void setup_app_userdef(BlendFileData *bfd) static void setup_app_data(bContext *C, BlendFileData *bfd, const struct BlendFileReadParams *params, - ReportList *reports) + BlendFileReadReport *reports) { Main *bmain = G_MAIN; Scene *curscene = NULL; @@ -155,7 +157,7 @@ static void setup_app_data(bContext *C, /* may happen with library files - UNDO file should never have NULL curscene (but may have a * NULL curscreen)... */ else if (ELEM(NULL, bfd->curscreen, bfd->curscene)) { - BKE_report(reports, RPT_WARNING, "Library file, loading empty scene"); + BKE_report(reports->reports, RPT_WARNING, "Library file, loading empty scene"); mode = LOAD_UI_OFF; } else if (G.fileflags & G_FILE_NO_UI) { @@ -269,7 +271,7 @@ static void setup_app_data(bContext *C, } /* We need to tag this here because events may be handled immediately after. - * only the current screen is important because we wont have to handle + * only the current screen is important because we won't have to handle * events from multiple screens at once.*/ if (curscreen) { BKE_screen_gizmo_tag_refresh(curscreen); @@ -396,11 +398,17 @@ static void setup_app_data(bContext *C, } if (mode != LOAD_UNDO && !USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) { + reports->duration.lib_overrides_resync = PIL_check_seconds_timer(); + BKE_lib_override_library_main_resync( bmain, curscene, bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene), reports); + + reports->duration.lib_overrides_resync = PIL_check_seconds_timer() - + reports->duration.lib_overrides_resync; + /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */ BKE_lib_override_library_main_operations_create(bmain, true); } @@ -409,7 +417,7 @@ static void setup_app_data(bContext *C, static void setup_app_blend_file_data(bContext *C, BlendFileData *bfd, const struct BlendFileReadParams *params, - ReportList *reports) + BlendFileReadReport *reports) { if ((params->skip_flags & BLO_READ_SKIP_USERDEF) == 0) { setup_app_userdef(bfd); @@ -419,12 +427,12 @@ static void setup_app_blend_file_data(bContext *C, } } -static void handle_subversion_warning(Main *main, ReportList *reports) +static void handle_subversion_warning(Main *main, BlendFileReadReport *reports) { if (main->minversionfile > BLENDER_FILE_VERSION || (main->minversionfile == BLENDER_FILE_VERSION && main->minsubversionfile > BLENDER_FILE_SUBVERSION)) { - BKE_reportf(reports, + BKE_reportf(reports->reports, RPT_ERROR, "File written by newer Blender binary (%d.%d), expect loss of data!", main->minversionfile, @@ -443,7 +451,7 @@ static void handle_subversion_warning(Main *main, ReportList *reports) void BKE_blendfile_read_setup_ex(bContext *C, BlendFileData *bfd, const struct BlendFileReadParams *params, - ReportList *reports, + BlendFileReadReport *reports, /* Extra args. */ const bool startup_update_defaults, const char *startup_app_template) @@ -460,7 +468,7 @@ void BKE_blendfile_read_setup_ex(bContext *C, void BKE_blendfile_read_setup(bContext *C, BlendFileData *bfd, const struct BlendFileReadParams *params, - ReportList *reports) + BlendFileReadReport *reports) { BKE_blendfile_read_setup_ex(C, bfd, params, reports, false, NULL); } @@ -470,7 +478,7 @@ void BKE_blendfile_read_setup(bContext *C, */ struct BlendFileData *BKE_blendfile_read(const char *filepath, const struct BlendFileReadParams *params, - ReportList *reports) + BlendFileReadReport *reports) { /* Don't print startup file loading. */ if (params->is_startup == false) { @@ -482,7 +490,7 @@ struct BlendFileData *BKE_blendfile_read(const char *filepath, handle_subversion_warning(bfd->main, reports); } else { - BKE_reports_prependf(reports, "Loading '%s' failed: ", filepath); + BKE_reports_prependf(reports->reports, "Loading '%s' failed: ", filepath); } return bfd; } @@ -559,7 +567,9 @@ UserDef *BKE_blendfile_userdef_read(const char *filepath, ReportList *reports) BlendFileData *bfd; UserDef *userdef = NULL; - bfd = BLO_read_from_file(filepath, BLO_READ_SKIP_ALL & ~BLO_READ_SKIP_USERDEF, reports); + bfd = BLO_read_from_file(filepath, + BLO_READ_SKIP_ALL & ~BLO_READ_SKIP_USERDEF, + &(struct BlendFileReadReport){.reports = reports}); if (bfd) { if (bfd->user) { userdef = bfd->user; @@ -770,7 +780,8 @@ WorkspaceConfigFileData *BKE_blendfile_workspace_config_read(const char *filepat WorkspaceConfigFileData *workspace_config = NULL; if (filepath) { - bfd = BLO_read_from_file(filepath, BLO_READ_SKIP_USERDEF, reports); + bfd = BLO_read_from_file( + filepath, BLO_READ_SKIP_USERDEF, &(struct BlendFileReadReport){.reports = reports}); } else { bfd = BLO_read_from_memory(filebuf, filelength, BLO_READ_SKIP_USERDEF, reports); diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 47427beccba..f26a9f06697 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -345,7 +345,7 @@ static bool missing_files_find__recursive(char *filename_new, BLI_join_dirfile(path, sizeof(path), dirname, de->d_name); if (BLI_stat(path, &status) == -1) { - continue; /* cant stat, don't bother with this file, could print debug info here */ + continue; /* can't stat, don't bother with this file, could print debug info here */ } if (S_ISREG(status.st_mode)) { /* is file */ @@ -812,7 +812,7 @@ bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *pa if (BLI_path_abs(filepath, base_old)) { /* Path was relative and is now absolute. Remap. * Important BLI_path_normalize runs before the path is made relative - * because it wont work for paths that start with "//../" */ + * because it won't work for paths that start with "//../" */ BLI_path_normalize(base_new, filepath); BLI_path_rel(filepath, base_new); BLI_strncpy(path_dst, filepath, FILE_MAX); @@ -850,7 +850,7 @@ static bool bpath_list_append(void *userdata, char *UNUSED(path_dst), const char static bool bpath_list_restore(void *userdata, char *path_dst, const char *path_src) { - /* assume ls->first wont be NULL because the number of paths can't change! + /* assume ls->first won't be NULL because the number of paths can't change! * (if they do caller is wrong) */ ListBase *ls = userdata; struct PathStore *path_store = ls->first; diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c index bc63e423c09..116e6279657 100644 --- a/source/blender/blenkernel/intern/bvhutils.c +++ b/source/blender/blenkernel/intern/bvhutils.c @@ -31,6 +31,7 @@ #include "BLI_linklist.h" #include "BLI_math.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -160,6 +161,26 @@ void bvhcache_free(BVHCache *bvh_cache) MEM_freeN(bvh_cache); } +/* BVH tree balancing inside a mutex lock must be run in isolation. Balancing + * is multithreaded, and we do not want the current thread to start another task + * that may involve acquiring the same mutex lock that it is waiting for. */ +static void bvhtree_balance_isolated(void *userdata) +{ + BLI_bvhtree_balance((BVHTree *)userdata); +} + +static void bvhtree_balance(BVHTree *tree, const bool isolate) +{ + if (tree) { + if (isolate) { + BLI_task_isolate(bvhtree_balance_isolated, tree); + } + else { + BLI_bvhtree_balance(tree); + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name Local Callbacks @@ -566,7 +587,6 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree(float epsilon, BLI_bvhtree_insert(tree, i, eve->co, 1); } BLI_assert(BLI_bvhtree_get_len(tree) == verts_num_active); - BLI_bvhtree_balance(tree); } return tree; @@ -600,7 +620,6 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon, BLI_bvhtree_insert(tree, i, vert[i].co, 1); } BLI_assert(BLI_bvhtree_get_len(tree) == verts_num_active); - BLI_bvhtree_balance(tree); } } @@ -649,6 +668,7 @@ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data, if (data->cached == false) { tree = bvhtree_from_editmesh_verts_create_tree( epsilon, tree_type, axis, em, verts_mask, verts_num_active); + bvhtree_balance(tree, true); /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ @@ -660,6 +680,7 @@ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data, else { tree = bvhtree_from_editmesh_verts_create_tree( epsilon, tree_type, axis, em, verts_mask, verts_num_active); + bvhtree_balance(tree, false); } if (tree) { @@ -711,6 +732,7 @@ BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data, if (in_cache == false) { tree = bvhtree_from_mesh_verts_create_tree( epsilon, tree_type, axis, vert, verts_num, verts_mask, verts_num_active); + bvhtree_balance(tree, bvh_cache_p != NULL); if (bvh_cache_p) { /* Save on cache for later use */ @@ -771,7 +793,6 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon, BLI_bvhtree_insert(tree, i, co[0], 2); } BLI_assert(BLI_bvhtree_get_len(tree) == edges_num_active); - BLI_bvhtree_balance(tree); } return tree; @@ -809,7 +830,6 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const MVert *vert, BLI_bvhtree_insert(tree, i, co[0], 2); } - BLI_bvhtree_balance(tree); } } @@ -861,7 +881,7 @@ BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data, if (data->cached == false) { tree = bvhtree_from_editmesh_edges_create_tree( epsilon, tree_type, axis, em, edges_mask, edges_num_active); - + bvhtree_balance(tree, true); /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ bvhcache_insert(bvh_cache, tree, bvh_cache_type); @@ -872,6 +892,7 @@ BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data, else { tree = bvhtree_from_editmesh_edges_create_tree( epsilon, tree_type, axis, em, edges_mask, edges_num_active); + bvhtree_balance(tree, false); } if (tree) { @@ -928,12 +949,17 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data, vert, edge, edges_num, edges_mask, edges_num_active, epsilon, tree_type, axis); if (bvh_cache_p) { + bvhtree_balance(tree, true); + BVHCache *bvh_cache = *bvh_cache_p; /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ bvhcache_insert(bvh_cache, tree, bvh_cache_type); in_cache = true; } + else { + bvhtree_balance(tree, false); + } } if (bvh_cache_p) { @@ -994,7 +1020,6 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon, } } BLI_assert(BLI_bvhtree_get_len(tree) == faces_num_active); - BLI_bvhtree_balance(tree); } } @@ -1057,6 +1082,7 @@ BVHTree *bvhtree_from_mesh_faces_ex(BVHTreeFromMesh *data, if (in_cache == false) { tree = bvhtree_from_mesh_faces_create_tree( epsilon, tree_type, axis, vert, face, numFaces, faces_mask, faces_num_active); + bvhtree_balance(tree, bvh_cache_p != NULL); if (bvh_cache_p) { /* Save on cache for later use */ @@ -1127,7 +1153,6 @@ static BVHTree *bvhtree_from_editmesh_looptri_create_tree(float epsilon, } } BLI_assert(BLI_bvhtree_get_len(tree) == looptri_num_active); - BLI_bvhtree_balance(tree); } } @@ -1173,7 +1198,6 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon, } } BLI_assert(BLI_bvhtree_get_len(tree) == looptri_num_active); - BLI_bvhtree_balance(tree); } } @@ -1229,6 +1253,7 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, bool in_cache = bvhcache_find( bvh_cache_p, bvh_cache_type, &tree, &lock_started, mesh_eval_mutex); BVHCache *bvh_cache = *bvh_cache_p; + bvhtree_balance(tree, true); if (in_cache == false) { tree = bvhtree_from_editmesh_looptri_create_tree( @@ -1243,6 +1268,7 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, else { tree = bvhtree_from_editmesh_looptri_create_tree( epsilon, tree_type, axis, em, looptri_mask, looptri_num_active); + bvhtree_balance(tree, false); } if (tree) { @@ -1303,6 +1329,8 @@ BVHTree *bvhtree_from_mesh_looptri_ex(BVHTreeFromMesh *data, looptri_mask, looptri_num_active); + bvhtree_balance(tree, bvh_cache_p != NULL); + if (bvh_cache_p) { BVHCache *bvh_cache = *bvh_cache_p; bvhcache_insert(bvh_cache, tree, bvh_cache_type); @@ -1742,7 +1770,7 @@ BVHTree *BKE_bvhtree_from_pointcloud_get(BVHTreeFromPointCloud *data, BLI_bvhtree_insert(tree, i, pointcloud->co[i], 1); } BLI_assert(BLI_bvhtree_get_len(tree) == pointcloud->totpoint); - BLI_bvhtree_balance(tree); + bvhtree_balance(tree, false); data->coords = pointcloud->co; data->tree = tree; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index feae033337d..30e9ae39b67 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -198,6 +198,9 @@ void BKE_cachefile_reader_open(CacheFile *cache_file, void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reader) { #ifdef WITH_ALEMBIC + /* Multiple modifiers and constraints can call this function concurrently, and + * cachefile_handle_free() can also be called at the same time. */ + BLI_spin_lock(&spin); if (*reader != NULL) { if (cache_file) { BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE); @@ -206,13 +209,11 @@ void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reade CacheReader_free(*reader); *reader = NULL; - /* Multiple modifiers and constraints can call this function concurrently. */ - BLI_spin_lock(&spin); if (cache_file && cache_file->handle_readers) { BLI_gset_remove(cache_file->handle_readers, reader, NULL); } - BLI_spin_unlock(&spin); } + BLI_spin_unlock(&spin); #else UNUSED_VARS(cache_file, reader); #endif diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c index 10c6d2213ff..7deac4e4f19 100644 --- a/source/blender/blenkernel/intern/curve_deform.c +++ b/source/blender/blenkernel/intern/curve_deform.c @@ -173,12 +173,10 @@ static bool calc_curve_deform( copy_qt_qt(quat, new_quat); copy_v3_v3(cent, co); - /* zero the axis which is not used, - * the big block of text above now applies to these 3 lines */ - quat_apply_track(quat, - axis, - (ELEM(axis, 0, 2)) ? 1 : - 0); /* up flag is a dummy, set so no rotation is done */ + /* Zero the axis which is not used, + * the big block of text above now applies to these 3 lines. + * The `upflag` argument may be a dummy, set so no rotation is done. */ + quat_apply_track(quat, axis, (ELEM(axis, 0, 2)) ? 1 : 0); vec_apply_track(cent, axis); cent[index] = 0.0f; diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 9cafe1124b1..0a6e4458a35 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -15,10 +15,13 @@ */ #include "BLI_array.hh" +#include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_span.hh" #include "BLI_string_ref.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" #include "DNA_curve_types.h" @@ -28,9 +31,12 @@ using blender::Array; using blender::float3; using blender::float4x4; +using blender::IndexRange; using blender::Map; +using blender::MutableSpan; using blender::Span; using blender::StringRefNull; +using blender::Vector; blender::Span<SplinePtr> CurveEval::splines() const { @@ -42,6 +48,12 @@ blender::MutableSpan<SplinePtr> CurveEval::splines() return splines_; } +void CurveEval::resize(const int size) +{ + splines_.resize(size); + attributes.reallocate(size); +} + /** * \warning Call #reallocate on the spline's attributes after adding all splines. */ @@ -162,70 +174,118 @@ static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag) return NURBSpline::KnotsMode::Normal; } +static SplinePtr spline_from_dna_bezier(const Nurb &nurb) +{ + std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); + spline->set_resolution(nurb.resolu); + spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); + + Span<const BezTriple> src_points{nurb.bezt, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan<float3> positions = spline->positions(); + MutableSpan<float3> handle_positions_left = spline->handle_positions_left(); + MutableSpan<float3> handle_positions_right = spline->handle_positions_right(); + MutableSpan<BezierSpline::HandleType> handle_types_left = spline->handle_types_left(); + MutableSpan<BezierSpline::HandleType> handle_types_right = spline->handle_types_right(); + MutableSpan<float> radii = spline->radii(); + MutableSpan<float> tilts = spline->tilts(); + + blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + const BezTriple &bezt = src_points[i]; + positions[i] = bezt.vec[1]; + handle_positions_left[i] = bezt.vec[0]; + handle_types_left[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1); + handle_positions_right[i] = bezt.vec[2]; + handle_types_right[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2); + radii[i] = bezt.radius; + tilts[i] = bezt.tilt; + } + }); + + return spline; +} + +static SplinePtr spline_from_dna_nurbs(const Nurb &nurb) +{ + std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); + spline->set_resolution(nurb.resolu); + spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); + spline->set_order(nurb.orderu); + spline->knots_mode = knots_mode_from_dna_nurb(nurb.flagu); + + Span<const BPoint> src_points{nurb.bp, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan<float3> positions = spline->positions(); + MutableSpan<float> weights = spline->weights(); + MutableSpan<float> radii = spline->radii(); + MutableSpan<float> tilts = spline->tilts(); + + blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + const BPoint &bp = src_points[i]; + positions[i] = bp.vec; + weights[i] = bp.vec[3]; + radii[i] = bp.radius; + tilts[i] = bp.tilt; + } + }); + + return spline; +} + +static SplinePtr spline_from_dna_poly(const Nurb &nurb) +{ + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); + + Span<const BPoint> src_points{nurb.bp, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan<float3> positions = spline->positions(); + MutableSpan<float> radii = spline->radii(); + MutableSpan<float> tilts = spline->tilts(); + + blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + const BPoint &bp = src_points[i]; + positions[i] = bp.vec; + radii[i] = bp.radius; + tilts[i] = bp.tilt; + } + }); + + return spline; +} + std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve) { + Vector<const Nurb *> nurbs(*BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve))); + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + curve->resize(nurbs.size()); + MutableSpan<SplinePtr> splines = curve->splines(); - const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve)); - - /* TODO: Optimize by reserving the correct points size. */ - LISTBASE_FOREACH (const Nurb *, nurb, nurbs) { - switch (nurb->type) { - case CU_BEZIER: { - std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); - spline->set_resolution(nurb->resolu); - spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); - - for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) { - spline->add_point(bezt.vec[1], - handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1), - bezt.vec[0], - handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2), - bezt.vec[2], - bezt.radius, - bezt.tilt); - } - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - break; - } - case CU_NURBS: { - std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); - spline->set_resolution(nurb->resolu); - spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); - spline->set_order(nurb->orderu); - spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu); - - for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) { - spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]); - } - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - break; - } - case CU_POLY: { - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); - - for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) { - spline->add_point(bp.vec, bp.radius, bp.tilt); - } - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - break; - } - default: { - BLI_assert_unreachable(); - break; + blender::threading::parallel_for(nurbs.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + switch (nurbs[i]->type) { + case CU_BEZIER: + splines[i] = spline_from_dna_bezier(*nurbs[i]); + break; + case CU_NURBS: + splines[i] = spline_from_dna_nurbs(*nurbs[i]); + break; + case CU_POLY: + splines[i] = spline_from_dna_poly(*nurbs[i]); + break; + default: + BLI_assert_unreachable(); + break; } } - } - - /* Though the curve has no attributes, this is necessary to properly set the custom data size. */ - curve->attributes.reallocate(curve->splines().size()); + }); - /* Note: Normal mode is stored separately in each spline to facilitate combining splines - * from multiple curve objects, where the value may be different. */ + /* Normal mode is stored separately in each spline to facilitate combining + * splines from multiple curve objects, where the value may be different. */ const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve( dna_curve.twist_mode); for (SplinePtr &spline : curve->splines()) { diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 710dfdaf137..b1bb8b9715e 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1970,7 +1970,7 @@ const CustomData_MeshMasks CD_MASK_BMESH = { CD_MASK_SCULPT_FACE_SETS), }; /** - * cover values copied by #BKE_mesh_loops_to_tessdata + * cover values copied by #mesh_loops_to_tessdata */ const CustomData_MeshMasks CD_MASK_FACECORNERS = { .vmask = 0, @@ -2020,35 +2020,35 @@ static const char *layerType_getName(int type) void customData_mask_layers__print(const CustomData_MeshMasks *mask) { - printf("verts mask=0x%lx:\n", (long unsigned int)mask->vmask); + printf("verts mask=0x%" PRIx64 ":\n", mask->vmask); for (int i = 0; i < CD_NUMTYPES; i++) { if (mask->vmask & CD_TYPE_AS_MASK(i)) { printf(" %s\n", layerType_getName(i)); } } - printf("edges mask=0x%lx:\n", (long unsigned int)mask->emask); + printf("edges mask=0x%" PRIx64 ":\n", mask->emask); for (int i = 0; i < CD_NUMTYPES; i++) { if (mask->emask & CD_TYPE_AS_MASK(i)) { printf(" %s\n", layerType_getName(i)); } } - printf("faces mask=0x%lx:\n", (long unsigned int)mask->fmask); + printf("faces mask=0x%" PRIx64 ":\n", mask->fmask); for (int i = 0; i < CD_NUMTYPES; i++) { if (mask->fmask & CD_TYPE_AS_MASK(i)) { printf(" %s\n", layerType_getName(i)); } } - printf("loops mask=0x%lx:\n", (long unsigned int)mask->lmask); + printf("loops mask=0x%" PRIx64 ":\n", mask->lmask); for (int i = 0; i < CD_NUMTYPES; i++) { if (mask->lmask & CD_TYPE_AS_MASK(i)) { printf(" %s\n", layerType_getName(i)); } } - printf("polys mask=0x%lx:\n", (long unsigned int)mask->pmask); + printf("polys mask=0x%" PRIx64 ":\n", mask->pmask); for (int i = 0; i < CD_NUMTYPES; i++) { if (mask->pmask & CD_TYPE_AS_MASK(i)) { printf(" %s\n", layerType_getName(i)); diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 8661941bd4c..70355f3883d 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -1420,237 +1420,237 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, if (ob->type == OB_SURF) { displist_make_surf(depsgraph, scene, ob, dispbase, r_final, for_render, for_orco); + return; } - else if (ELEM(ob->type, OB_CURVE, OB_FONT)) { - ListBase nubase = {nullptr, nullptr}; - bool force_mesh_conversion = false; - BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); + ListBase nubase = {nullptr, nullptr}; + bool force_mesh_conversion = false; - /* We only re-evaluate path if evaluation is not happening for orco. - * If the calculation happens for orco, we should never free data which - * was needed before and only not needed for orco calculation. */ - if (!for_orco) { - if (ob->runtime.curve_cache->anim_path_accum_length) { - MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length); - } - ob->runtime.curve_cache->anim_path_accum_length = nullptr; - } + BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); - if (ob->type == OB_FONT) { - BKE_vfont_to_curve_nubase(ob, FO_EDIT, &nubase); - } - else { - BKE_nurbList_duplicate(&nubase, BKE_curve_nurbs_get(const_cast<Curve *>(cu))); + /* We only re-evaluate path if evaluation is not happening for orco. + * If the calculation happens for orco, we should never free data which + * was needed before and only not needed for orco calculation. */ + if (!for_orco) { + if (ob->runtime.curve_cache->anim_path_accum_length) { + MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length); } + ob->runtime.curve_cache->anim_path_accum_length = nullptr; + } - if (!for_orco) { - force_mesh_conversion = BKE_curve_calc_modifiers_pre( - depsgraph, scene, ob, &nubase, &nubase, for_render); - } + if (ob->type == OB_FONT) { + BKE_vfont_to_curve_nubase(ob, FO_EDIT, &nubase); + } + else { + BKE_nurbList_duplicate(&nubase, BKE_curve_nurbs_get(const_cast<Curve *>(cu))); + } - BKE_curve_bevelList_make(ob, &nubase, for_render); + if (!for_orco) { + force_mesh_conversion = BKE_curve_calc_modifiers_pre( + depsgraph, scene, ob, &nubase, &nubase, for_render); + } - /* If curve has no bevel will return nothing */ - ListBase dlbev = BKE_curve_bevel_make(cu); + BKE_curve_bevelList_make(ob, &nubase, for_render); - /* no bevel or extrude, and no width correction? */ - if (BLI_listbase_is_empty(&dlbev) && cu->width == 1.0f) { - curve_to_displist(cu, &nubase, for_render, dispbase); - } - else { - const float widfac = cu->width - 1.0f; + /* If curve has no bevel will return nothing */ + ListBase dlbev = BKE_curve_bevel_make(cu); - BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first; - Nurb *nu = (Nurb *)nubase.first; - for (; bl && nu; bl = bl->next, nu = nu->next) { - float *data; + /* no bevel or extrude, and no width correction? */ + if (BLI_listbase_is_empty(&dlbev) && cu->width == 1.0f) { + curve_to_displist(cu, &nubase, for_render, dispbase); + } + else { + const float widfac = cu->width - 1.0f; - if (bl->nr == 0) { /* blank bevel lists can happen */ - continue; - } + BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first; + Nurb *nu = (Nurb *)nubase.first; + for (; bl && nu; bl = bl->next, nu = nu->next) { + float *data; - /* exception handling; curve without bevel or extrude, with width correction */ - if (BLI_listbase_is_empty(&dlbev)) { - DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev"); - dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts"); - BLI_addtail(dispbase, dl); + if (bl->nr == 0) { /* blank bevel lists can happen */ + continue; + } - if (bl->poly != -1) { - dl->type = DL_POLY; - } - else { - dl->type = DL_SEGM; - dl->flag = (DL_FRONT_CURVE | DL_BACK_CURVE); - } + /* exception handling; curve without bevel or extrude, with width correction */ + if (BLI_listbase_is_empty(&dlbev)) { + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev"); + dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts"); + BLI_addtail(dispbase, dl); - dl->parts = 1; - dl->nr = bl->nr; - dl->col = nu->mat_nr; - dl->charidx = nu->charidx; - dl->rt = nu->flag; + if (bl->poly != -1) { + dl->type = DL_POLY; + } + else { + dl->type = DL_SEGM; + dl->flag = (DL_FRONT_CURVE | DL_BACK_CURVE); + } - int a = dl->nr; - BevPoint *bevp = bl->bevpoints; - data = dl->verts; - while (a--) { - data[0] = bevp->vec[0] + widfac * bevp->sina; - data[1] = bevp->vec[1] + widfac * bevp->cosa; - data[2] = bevp->vec[2]; - bevp++; - data += 3; - } + dl->parts = 1; + dl->nr = bl->nr; + dl->col = nu->mat_nr; + dl->charidx = nu->charidx; + dl->rt = nu->flag; + + int a = dl->nr; + BevPoint *bevp = bl->bevpoints; + data = dl->verts; + while (a--) { + data[0] = bevp->vec[0] + widfac * bevp->sina; + data[1] = bevp->vec[1] + widfac * bevp->cosa; + data[2] = bevp->vec[2]; + bevp++; + data += 3; + } + } + else { + ListBase bottom_capbase = {nullptr, nullptr}; + ListBase top_capbase = {nullptr, nullptr}; + float bottom_no[3] = {0.0f}; + float top_no[3] = {0.0f}; + float first_blend = 0.0f, last_blend = 0.0f; + int start, steps = 0; + + if (nu->flagu & CU_NURB_CYCLIC) { + calc_bevfac_mapping_default(bl, &start, &first_blend, &steps, &last_blend); } else { - ListBase bottom_capbase = {nullptr, nullptr}; - ListBase top_capbase = {nullptr, nullptr}; - float bottom_no[3] = {0.0f}; - float top_no[3] = {0.0f}; - float first_blend = 0.0f, last_blend = 0.0f; - int start, steps = 0; - - if (nu->flagu & CU_NURB_CYCLIC) { - calc_bevfac_mapping_default(bl, &start, &first_blend, &steps, &last_blend); + if (fabsf(cu->bevfac2 - cu->bevfac1) < FLT_EPSILON) { + continue; } - else { - if (fabsf(cu->bevfac2 - cu->bevfac1) < FLT_EPSILON) { - continue; - } - calc_bevfac_mapping(cu, bl, nu, &start, &first_blend, &steps, &last_blend); + calc_bevfac_mapping(cu, bl, nu, &start, &first_blend, &steps, &last_blend); + } + + LISTBASE_FOREACH (DispList *, dlb, &dlbev) { + /* for each part of the bevel use a separate displblock */ + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1"); + dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts"); + BLI_addtail(dispbase, dl); + + dl->type = DL_SURF; + + dl->flag = dlb->flag & (DL_FRONT_CURVE | DL_BACK_CURVE); + if (dlb->type == DL_POLY) { + dl->flag |= DL_CYCL_U; + } + if ((bl->poly >= 0) && (steps > 2)) { + dl->flag |= DL_CYCL_V; } - LISTBASE_FOREACH (DispList *, dlb, &dlbev) { - /* for each part of the bevel use a separate displblock */ - DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1"); - dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts"); - BLI_addtail(dispbase, dl); + dl->parts = steps; + dl->nr = dlb->nr; + dl->col = nu->mat_nr; + dl->charidx = nu->charidx; + dl->rt = nu->flag; - dl->type = DL_SURF; + /* for each point of poly make a bevel piece */ + BevPoint *bevp_first = bl->bevpoints; + BevPoint *bevp_last = &bl->bevpoints[bl->nr - 1]; + BevPoint *bevp = &bl->bevpoints[start]; + for (int i = start, a = 0; a < steps; i++, bevp++, a++) { + float radius_factor = 1.0; + float *cur_data = data; - dl->flag = dlb->flag & (DL_FRONT_CURVE | DL_BACK_CURVE); - if (dlb->type == DL_POLY) { - dl->flag |= DL_CYCL_U; - } - if ((bl->poly >= 0) && (steps > 2)) { - dl->flag |= DL_CYCL_V; + if (cu->taperobj == nullptr) { + radius_factor = bevp->radius; } + else { + float taper_factor; + if (cu->flag & CU_MAP_TAPER) { + float len = (steps - 3) + first_blend + last_blend; - dl->parts = steps; - dl->nr = dlb->nr; - dl->col = nu->mat_nr; - dl->charidx = nu->charidx; - dl->rt = nu->flag; - - /* for each point of poly make a bevel piece */ - BevPoint *bevp_first = bl->bevpoints; - BevPoint *bevp_last = &bl->bevpoints[bl->nr - 1]; - BevPoint *bevp = &bl->bevpoints[start]; - for (int i = start, a = 0; a < steps; i++, bevp++, a++) { - float radius_factor = 1.0; - float *cur_data = data; - - if (cu->taperobj == nullptr) { - radius_factor = bevp->radius; - } - else { - float taper_factor; - if (cu->flag & CU_MAP_TAPER) { - float len = (steps - 3) + first_blend + last_blend; - - if (a == 0) { - taper_factor = 0.0f; - } - else if (a == steps - 1) { - taper_factor = 1.0f; - } - else { - taper_factor = ((float)a - (1.0f - first_blend)) / len; - } + if (a == 0) { + taper_factor = 0.0f; + } + else if (a == steps - 1) { + taper_factor = 1.0f; } else { - float len = bl->nr - 1; - taper_factor = (float)i / len; - - if (a == 0) { - taper_factor += (1.0f - first_blend) / len; - } - else if (a == steps - 1) { - taper_factor -= (1.0f - last_blend) / len; - } + taper_factor = ((float)a - (1.0f - first_blend)) / len; } + } + else { + float len = bl->nr - 1; + taper_factor = (float)i / len; - radius_factor = displist_calc_taper(depsgraph, scene, cu->taperobj, taper_factor); - - if (cu->taper_radius_mode == CU_TAPER_RADIUS_MULTIPLY) { - radius_factor *= bevp->radius; + if (a == 0) { + taper_factor += (1.0f - first_blend) / len; } - else if (cu->taper_radius_mode == CU_TAPER_RADIUS_ADD) { - radius_factor += bevp->radius; + else if (a == steps - 1) { + taper_factor -= (1.0f - last_blend) / len; } } - /* rotate bevel piece and write in data */ - if ((a == 0) && (bevp != bevp_last)) { - rotateBevelPiece( - cu, bevp, bevp + 1, dlb, 1.0f - first_blend, widfac, radius_factor, &data); - } - else if ((a == steps - 1) && (bevp != bevp_first)) { - rotateBevelPiece( - cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data); + radius_factor = displist_calc_taper(depsgraph, scene, cu->taperobj, taper_factor); + + if (cu->taper_radius_mode == CU_TAPER_RADIUS_MULTIPLY) { + radius_factor *= bevp->radius; } - else { - rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data); + else if (cu->taper_radius_mode == CU_TAPER_RADIUS_ADD) { + radius_factor += bevp->radius; } + } - if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) { - if (a == 1) { - fillBevelCap(nu, dlb, cur_data - 3 * dlb->nr, &bottom_capbase); - copy_v3_v3(bottom_no, bevp->dir); - } - if (a == steps - 1) { - fillBevelCap(nu, dlb, cur_data, &top_capbase); - negate_v3_v3(top_no, bevp->dir); - } - } + /* rotate bevel piece and write in data */ + if ((a == 0) && (bevp != bevp_last)) { + rotateBevelPiece( + cu, bevp, bevp + 1, dlb, 1.0f - first_blend, widfac, radius_factor, &data); + } + else if ((a == steps - 1) && (bevp != bevp_first)) { + rotateBevelPiece( + cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data); + } + else { + rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data); } - /* gl array drawing: using indices */ - displist_surf_indices(dl); + if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) { + if (a == 1) { + fillBevelCap(nu, dlb, cur_data - 3 * dlb->nr, &bottom_capbase); + copy_v3_v3(bottom_no, bevp->dir); + } + if (a == steps - 1) { + fillBevelCap(nu, dlb, cur_data, &top_capbase); + negate_v3_v3(top_no, bevp->dir); + } + } } - if (bottom_capbase.first) { - BKE_displist_fill(&bottom_capbase, dispbase, bottom_no, false); - BKE_displist_fill(&top_capbase, dispbase, top_no, false); - BKE_displist_free(&bottom_capbase); - BKE_displist_free(&top_capbase); - } + /* gl array drawing: using indices */ + displist_surf_indices(dl); } - } - BKE_displist_free(&dlbev); - } - if (!(cu->flag & CU_DEFORM_FILL)) { - curve_to_filledpoly(cu, dispbase); + if (bottom_capbase.first) { + BKE_displist_fill(&bottom_capbase, dispbase, bottom_no, false); + BKE_displist_fill(&top_capbase, dispbase, top_no, false); + BKE_displist_free(&bottom_capbase); + BKE_displist_free(&top_capbase); + } + } } + BKE_displist_free(&dlbev); + } - if (!for_orco) { - if ((cu->flag & CU_PATH) || - DEG_get_eval_flags_for_id(depsgraph, &ob->id) & DAG_EVAL_NEED_CURVE_PATH) { - BKE_anim_path_calc_data(ob); - } + if (!(cu->flag & CU_DEFORM_FILL)) { + curve_to_filledpoly(cu, dispbase); + } - BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase); - curve_calc_modifiers_post( - depsgraph, scene, ob, dispbase, for_render, force_mesh_conversion, r_final); + if (!for_orco) { + if ((cu->flag & CU_PATH) || + DEG_get_eval_flags_for_id(depsgraph, &ob->id) & DAG_EVAL_NEED_CURVE_PATH) { + BKE_anim_path_calc_data(ob); } - if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) { - curve_to_filledpoly(cu, dispbase); - } + BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase); + curve_calc_modifiers_post( + depsgraph, scene, ob, dispbase, for_render, force_mesh_conversion, r_final); + } - BKE_nurbList_free(&nubase); + if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) { + curve_to_filledpoly(cu, dispbase); } + + BKE_nurbList_free(&nubase); } void BKE_displist_make_curveTypes(Depsgraph *depsgraph, @@ -1689,8 +1689,8 @@ void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph, const Scene *scene, Object *ob, ListBase *dispbase, - Mesh **r_final, - const bool for_orco) + const bool for_orco, + Mesh **r_final) { if (ob->runtime.curve_cache == nullptr) { ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 42af3a391ed..788a51257bf 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -5185,7 +5185,7 @@ static int dynamicPaint_prepareEffectStep(struct Depsgraph *depsgraph, } /* Get number of required steps using average point distance - * so that just a few ultra close pixels wont up substeps to max. */ + * so that just a few ultra close pixels won't increase substeps to max. */ /* adjust number of required substep by fastest active effect */ if (surface->effect & MOD_DPAINT_EFFECT_DO_SPREAD) { diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 472de1f3c77..b512309a773 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -96,7 +96,8 @@ BMEditMesh *BKE_editmesh_from_object(Object *ob) return ((Mesh *)ob->data)->edit_mesh; } -static void editmesh_tessface_calc_intern(BMEditMesh *em) +static void editmesh_tessface_calc_intern(BMEditMesh *em, + const struct BMeshCalcTessellation_Params *params) { /* allocating space before calculating the tessellation */ @@ -130,12 +131,13 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em) em->tottri = looptris_tot; /* after allocating the em->looptris, we're ready to tessellate */ - BM_mesh_calc_tessellation(em->bm, em->looptris); + BM_mesh_calc_tessellation_ex(em->bm, em->looptris, params); } -void BKE_editmesh_looptri_calc(BMEditMesh *em) +void BKE_editmesh_looptri_calc_ex(BMEditMesh *em, + const struct BMeshCalcTessellation_Params *params) { - editmesh_tessface_calc_intern(em); + editmesh_tessface_calc_intern(em, params); /* commented because editbmesh_build_data() ensures we get tessfaces */ #if 0 @@ -149,12 +151,62 @@ void BKE_editmesh_looptri_calc(BMEditMesh *em) #endif } -void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo) +void BKE_editmesh_looptri_calc(BMEditMesh *em) +{ + BKE_editmesh_looptri_calc_ex(em, + &(const struct BMeshCalcTessellation_Params){ + .face_normals = false, + }); +} + +/** + * Performing the face normal calculation at the same time as tessellation + * gives a reasonable performance boost (approx ~20% faster). + */ +void BKE_editmesh_looptri_and_normals_calc(BMEditMesh *em) +{ + BKE_editmesh_looptri_calc_ex(em, + &(const struct BMeshCalcTessellation_Params){ + .face_normals = true, + }); + BM_mesh_normals_update_ex(em->bm, + &(const struct BMeshNormalsUpdate_Params){ + .face_normals = false, + }); +} + +void BKE_editmesh_looptri_calc_with_partial_ex(BMEditMesh *em, + struct BMPartialUpdate *bmpinfo, + const struct BMeshCalcTessellation_Params *params) { BLI_assert(em->tottri == poly_to_tri_count(em->bm->totface, em->bm->totloop)); BLI_assert(em->looptris != NULL); - BM_mesh_calc_tessellation_with_partial(em->bm, em->looptris, bmpinfo); + BM_mesh_calc_tessellation_with_partial_ex(em->bm, em->looptris, bmpinfo, params); +} + +void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo) +{ + BKE_editmesh_looptri_calc_with_partial_ex(em, + bmpinfo, + &(const struct BMeshCalcTessellation_Params){ + .face_normals = false, + }); +} + +void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, + struct BMPartialUpdate *bmpinfo) +{ + BKE_editmesh_looptri_calc_with_partial_ex(em, + bmpinfo, + &(const struct BMeshCalcTessellation_Params){ + .face_normals = true, + }); + BM_mesh_normals_update_with_partial_ex(em->bm, + bmpinfo, + &(const struct BMeshNormalsUpdate_Params){ + .face_normals = false, + }); } void BKE_editmesh_free_derivedmesh(BMEditMesh *em) diff --git a/source/blender/blenkernel/intern/editmesh_bvh.c b/source/blender/blenkernel/intern/editmesh_bvh.c index c4f855dd8c2..9e0e1933a00 100644 --- a/source/blender/blenkernel/intern/editmesh_bvh.c +++ b/source/blender/blenkernel/intern/editmesh_bvh.c @@ -117,7 +117,7 @@ BMBVHTree *BKE_bmbvh_new_ex(BMesh *bm, for (int i = 0; i < looptris_tot; i++) { if (test_fn) { - /* note, the arrays wont align now! take care */ + /* Note: the arrays won't align now! Take care. */ f_test = looptris[i][0]->f; if (f_test != f_test_prev) { test_fn_ret = test_fn(f_test, user_data); diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c index 088a2087a96..d849f4ab37d 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.c +++ b/source/blender/blenkernel/intern/editmesh_tangent.c @@ -362,7 +362,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, /* Calculation */ if (em->tottri != 0) { TaskPool *task_pool; - task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW); tangent_mask_curr = 0; /* Calculate tangent layers */ diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 493a267c2f0..553575b5c7b 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1615,8 +1615,9 @@ static void emit_from_particles(Object *flow_ob, } } - state.time = BKE_scene_frame_get( - scene); /* DEG_get_ctime(depsgraph) does not give subframe time */ + /* `DEG_get_ctime(depsgraph)` does not give sub-frame time. */ + state.time = BKE_scene_frame_get(scene); + if (psys_get_particle_state(&sim, p, &state, 0) == 0) { continue; } @@ -4234,7 +4235,7 @@ struct Mesh *BKE_fluid_modifier_do( result = BKE_mesh_copy_for_eval(me, false); } else { - BKE_mesh_copy_settings(result, me); + BKE_mesh_copy_parameters_for_eval(result, me); } /* Liquid simulation has a texture space that based on the bounds of the fluid mesh. diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index de8dc355557..b5c49dbb8b2 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -240,14 +240,18 @@ static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVA * unless it is necessary (in that case the materialize functions will be called). */ template<typename T> class VArray_For_SplineToPoint final : public VArray<T> { + GVArrayPtr original_varray_; /* Store existing data materialized if it was not already a span. This is expected * to be worth it because a single spline's value will likely be accessed many times. */ - VArray_Span<T> original_data_; + fn::GVArray_Span<T> original_data_; Array<int> offsets_; public: - VArray_For_SplineToPoint(const VArray<T> &original_varray, Array<int> offsets) - : VArray<T>(offsets.last()), original_data_(original_varray), offsets_(std::move(offsets)) + VArray_For_SplineToPoint(GVArrayPtr original_varray, Array<int> offsets) + : VArray<T>(offsets.last()), + original_varray_(std::move(original_varray)), + original_data_(*original_varray_), + offsets_(std::move(offsets)) { } @@ -309,7 +313,7 @@ static GVArrayPtr adapt_curve_domain_spline_to_point(const CurveEval &curve, GVA Array<int> offsets = curve.control_point_offsets(); new_varray = std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplineToPoint<T>>>( - offsets.last(), *varray->typed<T>(), std::move(offsets)); + offsets.last(), std::move(varray), std::move(offsets)); }); return new_varray; } @@ -1007,9 +1011,10 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return false; } + /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ bool layer_freed = false; for (SplinePtr &spline : curve->splines()) { - spline->attributes.remove(attribute_name); + layer_freed = spline->attributes.remove(attribute_name); } return layer_freed; } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 69840ba1612..8cf08d05d9d 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -399,7 +399,7 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou const GeometrySet &set = set_group.geometry_set; if (set.has_mesh()) { const Mesh &mesh = *set.get_mesh_for_read(); - BKE_mesh_copy_settings(new_mesh, &mesh); + BKE_mesh_copy_parameters_for_eval(new_mesh, &mesh); break; } } @@ -472,6 +472,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou poly_offset += mesh.totpoly; } } + + const float3 point_normal{0.0f, 0.0f, 1.0f}; + short point_normal_short[3]; + normal_float_to_short_v3(point_normal_short, point_normal); + if (convert_points_to_vertices && set.has_pointcloud()) { const PointCloud &pointcloud = *set.get_pointcloud_for_read(); for (const float4x4 &transform : set_group.transforms) { @@ -480,6 +485,7 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou const float3 old_position = pointcloud.co[i]; const float3 new_position = transform * old_position; copy_v3_v3(new_vert.co, new_position); + memcpy(&new_vert.no, point_normal_short, sizeof(point_normal_short)); } vert_offset += pointcloud.totpoint; } @@ -544,7 +550,45 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, } } -static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups) +static PointCloud *join_pointcloud_position_attribute(Span<GeometryInstanceGroup> set_groups) +{ + /* Count the total number of points. */ + int totpoint = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has<PointCloudComponent>()) { + const PointCloudComponent &component = *set.get_component_for_read<PointCloudComponent>(); + totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT); + } + } + if (totpoint == 0) { + return nullptr; + } + + PointCloud *new_pointcloud = BKE_pointcloud_new_nomain(totpoint); + + /* Transform each instance's point locations into the new point cloud. */ + int offset = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const PointCloud *pointcloud = set.get_pointcloud_for_read(); + if (pointcloud == nullptr) { + continue; + } + for (const float4x4 &transform : set_group.transforms) { + for (const int i : IndexRange(pointcloud->totpoint)) { + const float3 old_position = pointcloud->co[i]; + const float3 new_position = transform * old_position; + copy_v3_v3(new_pointcloud->co[offset + i], new_position); + } + offset += pointcloud->totpoint; + } + } + + return new_pointcloud; +} + +static CurveEval *join_curve_splines_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups) { Vector<SplinePtr> new_splines; for (const GeometryInstanceGroup &set_group : set_groups) { @@ -556,7 +600,7 @@ static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups) const CurveEval &source_curve = *set.get_curve_for_read(); for (const SplinePtr &source_spline : source_curve.splines()) { for (const float4x4 &transform : set_group.transforms) { - SplinePtr new_spline = source_spline->copy(); + SplinePtr new_spline = source_spline->copy_without_attributes(); new_spline->transform(transform); new_splines.append(std::move(new_spline)); } @@ -571,15 +615,6 @@ static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups) new_curve->add_spline(std::move(new_spline)); } - for (SplinePtr &spline : new_curve->splines()) { - /* Spline instances should have no custom attributes, since they always come - * from original objects which currently do not support custom attributes. - * - * This is only true as long as a #GeometrySet cannot be instanced directly. */ - BLI_assert(spline->attributes.data.totlayer == 0); - UNUSED_VARS_NDEBUG(spline); - } - new_curve->attributes.reallocate(new_curve->splines().size()); return new_curve; } @@ -617,24 +652,17 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) { - int totpoint = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - if (set.has<PointCloudComponent>()) { - const PointCloudComponent &component = *set.get_component_for_read<PointCloudComponent>(); - totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT); - } - } - if (totpoint == 0) { + PointCloud *new_pointcloud = join_pointcloud_position_attribute(set_groups); + if (new_pointcloud == nullptr) { return; } PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>(); - PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint); - dst_component.replace(pointcloud); + dst_component.replace(new_pointcloud); + Map<std::string, AttributeKind> attributes; geometry_set_gather_instances_attribute_info( - set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {}, attributes); + set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {"position"}, attributes); join_attributes(set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, attributes, @@ -644,19 +672,38 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) { - /* Not yet supported. Joining volume grids with the same name requires resampling of at least - * one of the grids. The cell size of the resulting volume has to be determined somehow. */ - UNUSED_VARS(set_groups, result); + /* Not yet supported; for now only return the first volume. Joining volume grids with the same + * name requires resampling of at least one of the grids. The cell size of the resulting volume + * has to be determined somehow. */ + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has<VolumeComponent>()) { + result.add(*set.get_component_for_read<VolumeComponent>()); + return; + } + } } static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) { - CurveEval *curve = join_curve_splines(set_groups); + CurveEval *curve = join_curve_splines_and_builtin_attributes(set_groups); if (curve == nullptr) { return; } + CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); dst_component.replace(curve); + + Map<std::string, AttributeKind> attributes; + geometry_set_gather_instances_attribute_info( + set_groups, + {GEO_COMPONENT_TYPE_CURVE}, + {"position", "radius", "tilt", "cyclic", "resolution"}, + attributes); + join_attributes(set_groups, + {GEO_COMPONENT_TYPE_CURVE}, + attributes, + static_cast<GeometryComponent &>(dst_component)); } GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 6d1476485ca..a66c3cf3573 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -1103,6 +1103,35 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src, } /** + * Make a copy of a given gpencil layer settings. + */ +void BKE_gpencil_layer_copy_settings(const bGPDlayer *gpl_src, bGPDlayer *gpl_dst) +{ + gpl_dst->line_change = gpl_src->line_change; + copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor); + gpl_dst->opacity = gpl_src->opacity; + gpl_dst->vertex_paint_opacity = gpl_src->vertex_paint_opacity; + gpl_dst->pass_index = gpl_src->pass_index; + gpl_dst->parent = gpl_src->parent; + copy_m4_m4(gpl_dst->inverse, gpl_src->inverse); + BLI_strncpy(gpl_dst->parsubstr, gpl_src->parsubstr, 64); + gpl_dst->partype = gpl_src->partype; + BLI_strncpy(gpl_dst->viewlayername, gpl_src->viewlayername, 64); + copy_v3_v3(gpl_dst->location, gpl_src->location); + copy_v3_v3(gpl_dst->rotation, gpl_src->rotation); + copy_v3_v3(gpl_dst->scale, gpl_src->scale); + copy_m4_m4(gpl_dst->layer_mat, gpl_src->layer_mat); + copy_m4_m4(gpl_dst->layer_invmat, gpl_src->layer_invmat); + /* Use Lights flag. */ + if (gpl_src->flag & GP_LAYER_USE_LIGHTS) { + gpl_dst->flag |= GP_LAYER_USE_LIGHTS; + } + else { + gpl_dst->flag &= ~GP_LAYER_USE_LIGHTS; + } +} + +/** * Make a copy of a given gpencil data-block. * * XXX: Should this be deprecated? @@ -2980,8 +3009,8 @@ void BKE_gpencil_update_layer_transforms(const Depsgraph *depsgraph, Object *ob) bGPdata *gpd = (bGPdata *)ob->data; float cur_mat[4][4]; - bool changed = false; LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bool changed = false; unit_m4(cur_mat); if (gpl->actframe != NULL) { if (gpl->parent != NULL) { diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 7f839650f33..36b8b5e52f0 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -793,6 +793,7 @@ bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf) { bGPDspoint *pt = &gps->points[i]; float sco[3] = {0.0f}; + const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; /* Do nothing if not enough points to smooth out */ if (gps->totpoints <= 2) { @@ -802,7 +803,7 @@ bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf) /* Only affect endpoints by a fraction of the normal strength, * to prevent the stroke from shrinking too much */ - if (ELEM(i, 0, gps->totpoints - 1)) { + if (!is_cyclic && ELEM(i, 0, gps->totpoints - 1)) { inf *= 0.1f; } @@ -828,8 +829,22 @@ bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf) int before = i - step; int after = i + step; - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); + if (is_cyclic) { + if (before < 0) { + /* Sub to end point (before is already negative). */ + before = gps->totpoints + before; + CLAMP(before, 0, gps->totpoints - 1); + } + if (after > gps->totpoints - 1) { + /* Add to start point. */ + after = after - gps->totpoints; + CLAMP(after, 0, gps->totpoints - 1); + } + } + else { + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + } pt1 = &gps->points[before]; pt2 = &gps->points[after]; @@ -855,6 +870,7 @@ bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf) bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence) { bGPDspoint *ptb = &gps->points[point_index]; + const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; /* Do nothing if not enough points */ if ((gps->totpoints <= 2) || (point_index < 1)) { @@ -862,7 +878,7 @@ bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float } /* Only affect endpoints by a fraction of the normal influence */ float inf = influence; - if (ELEM(point_index, 0, gps->totpoints - 1)) { + if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) { inf *= 0.01f; } /* Limit max influence to reduce pop effect. */ @@ -884,9 +900,22 @@ bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float int before = point_index - step; int after = point_index + step; - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); - + if (is_cyclic) { + if (before < 0) { + /* Sub to end point (before is already negative). */ + before = gps->totpoints + before; + CLAMP(before, 0, gps->totpoints - 1); + } + if (after > gps->totpoints - 1) { + /* Add to start point. */ + after = after - gps->totpoints; + CLAMP(after, 0, gps->totpoints - 1); + } + } + else { + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + } pt1 = &gps->points[before]; pt2 = &gps->points[after]; @@ -919,6 +948,7 @@ bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence) { bGPDspoint *ptb = &gps->points[point_index]; + const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; /* Do nothing if not enough points */ if ((gps->totpoints <= 2) || (point_index < 1)) { @@ -926,7 +956,7 @@ bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float } /* Only affect endpoints by a fraction of the normal influence */ float inf = influence; - if (ELEM(point_index, 0, gps->totpoints - 1)) { + if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) { inf *= 0.01f; } /* Limit max influence to reduce pop effect. */ @@ -948,9 +978,22 @@ bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float int before = point_index - step; int after = point_index + step; - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); - + if (is_cyclic) { + if (before < 0) { + /* Sub to end point (before is already negative). */ + before = gps->totpoints + before; + CLAMP(before, 0, gps->totpoints - 1); + } + if (after > gps->totpoints - 1) { + /* Add to start point. */ + after = after - gps->totpoints; + CLAMP(after, 0, gps->totpoints - 1); + } + } + else { + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + } pt1 = &gps->points[before]; pt2 = &gps->points[after]; @@ -982,6 +1025,7 @@ bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence) { bGPDspoint *ptb = &gps->points[point_index]; + const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; /* Do nothing if not enough points */ if (gps->totpoints <= 2) { @@ -993,9 +1037,22 @@ bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influe int before = point_index - 1; int after = point_index + 1; - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); - + if (is_cyclic) { + if (before < 0) { + /* Sub to end point (before is already negative). */ + before = gps->totpoints + before; + CLAMP(before, 0, gps->totpoints - 1); + } + if (after > gps->totpoints - 1) { + /* Add to start point. */ + after = after - gps->totpoints; + CLAMP(after, 0, gps->totpoints - 1); + } + } + else { + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + } pta = &gps->points[before]; ptc = &gps->points[after]; @@ -2984,6 +3041,9 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd, /* Add new stroke to the frame or delete if below limit */ if ((limit > 0) && (new_stroke->totpoints <= limit)) { + if (gps_first == new_stroke) { + gps_first = NULL; + } BKE_gpencil_free_stroke(new_stroke); } else { diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 16386cac029..9f5f70ab2ba 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -55,6 +55,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "MOD_gpencil_lineart.h" #include "MOD_gpencil_modifiertypes.h" #include "BLO_read_write.h" @@ -202,6 +203,60 @@ bool BKE_gpencil_has_transform_modifiers(Object *ob) return false; } +GpencilLineartLimitInfo BKE_gpencil_get_lineart_modifier_limits(const Object *ob) +{ + GpencilLineartLimitInfo info = {0}; + bool is_first = true; + LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { + if (md->type == eGpencilModifierType_Lineart) { + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; + if (is_first || (lmd->flags & LRT_GPENCIL_USE_CACHE)) { + info.min_level = MIN2(info.min_level, lmd->level_start); + info.max_level = MAX2(info.max_level, + (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start)); + info.edge_types |= lmd->edge_types; + } + } + } + return info; +} + +void BKE_gpencil_set_lineart_modifier_limits(GpencilModifierData *md, + const GpencilLineartLimitInfo *info, + const bool is_first_lineart) +{ + BLI_assert(md->type == eGpencilModifierType_Lineart); + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; + if (is_first_lineart || lmd->flags & LRT_GPENCIL_USE_CACHE) { + lmd->level_start_override = info->min_level; + lmd->level_end_override = info->max_level; + lmd->edge_types_override = info->edge_types; + } + else { + lmd->level_start_override = lmd->level_start; + lmd->level_end_override = lmd->level_end; + lmd->edge_types_override = lmd->edge_types; + } +} + +bool BKE_gpencil_is_first_lineart_in_stack(const Object *ob, const GpencilModifierData *md) +{ + if (md->type != eGpencilModifierType_Lineart) { + return false; + } + LISTBASE_FOREACH (GpencilModifierData *, gmd, &ob->greasepencil_modifiers) { + if (gmd->type == eGpencilModifierType_Lineart) { + if (gmd == md) { + return true; + } + return false; + } + } + /* If we reach here it means md is not in ob's modifier stack. */ + BLI_assert(false); + return false; +} + /* apply time modifiers */ static int gpencil_time_modifier( Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl, int cfra, bool is_render) @@ -771,6 +826,8 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) BKE_gpencil_lattice_init(ob); const bool time_remap = BKE_gpencil_has_time_modifiers(ob); + bool is_first_lineart = true; + GpencilLineartLimitInfo info = BKE_gpencil_get_lineart_modifier_limits(ob); LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { @@ -781,6 +838,11 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) continue; } + if (md->type == eGpencilModifierType_Lineart) { + BKE_gpencil_set_lineart_modifier_limits(md, &info, is_first_lineart); + is_first_lineart = false; + } + /* Apply geometry modifiers (add new geometry). */ if (mti && mti->generateStrokes) { mti->generateStrokes(md, depsgraph, ob); @@ -806,6 +868,8 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) /* Clear any lattice data. */ BKE_gpencil_lattice_clear(ob); + + MOD_lineart_clear_cache(&gpd->runtime.lineart_cache); } void BKE_gpencil_modifier_blend_write(BlendWriter *writer, ListBase *modbase) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 2f7e2b41a73..7a3619154a6 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -68,6 +68,7 @@ #include "BLI_math_vector.h" #include "BLI_mempool.h" #include "BLI_system.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_timecode.h" /* For stamp time-code format. */ #include "BLI_utildefines.h" @@ -882,6 +883,39 @@ Image *BKE_image_load_exists(Main *bmain, const char *filepath) return BKE_image_load_exists_ex(bmain, filepath, NULL); } +typedef struct ImageFillData { + short gen_type; + uint width; + uint height; + unsigned char *rect; + float *rect_float; + float fill_color[4]; +} ImageFillData; + +static void image_buf_fill_isolated(void *usersata_v) +{ + ImageFillData *usersata = usersata_v; + + const short gen_type = usersata->gen_type; + const uint width = usersata->width; + const uint height = usersata->height; + + unsigned char *rect = usersata->rect; + float *rect_float = usersata->rect_float; + + switch (gen_type) { + case IMA_GENTYPE_GRID: + BKE_image_buf_fill_checker(rect, rect_float, width, height); + break; + case IMA_GENTYPE_GRID_COLOR: + BKE_image_buf_fill_checker_color(rect, rect_float, width, height); + break; + default: + BKE_image_buf_fill_color(rect, rect_float, width, height, usersata->fill_color); + break; + } +} + static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char *name, @@ -944,17 +978,16 @@ static ImBuf *add_ibuf_size(unsigned int width, STRNCPY(ibuf->name, name); - switch (gen_type) { - case IMA_GENTYPE_GRID: - BKE_image_buf_fill_checker(rect, rect_float, width, height); - break; - case IMA_GENTYPE_GRID_COLOR: - BKE_image_buf_fill_checker_color(rect, rect_float, width, height); - break; - default: - BKE_image_buf_fill_color(rect, rect_float, width, height, fill_color); - break; - } + ImageFillData data; + + data.gen_type = gen_type; + data.width = width; + data.height = height; + data.rect = rect; + data.rect_float = rect_float; + copy_v4_v4(data.fill_color, fill_color); + + BLI_task_isolate(image_buf_fill_isolated, &data); return ibuf; } @@ -5326,7 +5359,7 @@ int BKE_image_user_frame_get(const ImageUser *iuser, int cfra, bool *r_is_in_ran } } - /* important to apply after else we cant loop on frames 100 - 110 for eg. */ + /* important to apply after else we can't loop on frames 100 - 110 for eg. */ framenr += iuser->offset; return framenr; diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c index ceb13c4955e..1a0cc8c2924 100644 --- a/source/blender/blenkernel/intern/image_gen.c +++ b/source/blender/blenkernel/intern/image_gen.c @@ -69,10 +69,11 @@ static void image_buf_fill_color_slice( } } -static void image_buf_fill_color_thread_do(void *data_v, int start_scanline, int num_scanlines) +static void image_buf_fill_color_thread_do(void *data_v, int scanline) { FillColorThreadData *data = (FillColorThreadData *)data_v; - size_t offset = ((size_t)start_scanline) * data->width * 4; + const int num_scanlines = 1; + size_t offset = ((size_t)scanline) * data->width * 4; unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; image_buf_fill_color_slice(rect, rect_float, data->width, num_scanlines, data->color); @@ -197,13 +198,14 @@ typedef struct FillCheckerThreadData { int width; } FillCheckerThreadData; -static void image_buf_fill_checker_thread_do(void *data_v, int start_scanline, int num_scanlines) +static void image_buf_fill_checker_thread_do(void *data_v, int scanline) { FillCheckerThreadData *data = (FillCheckerThreadData *)data_v; - size_t offset = ((size_t)start_scanline) * data->width * 4; + size_t offset = ((size_t)scanline) * data->width * 4; + const int num_scanlines = 1; unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; - image_buf_fill_checker_slice(rect, rect_float, data->width, num_scanlines, start_scanline); + image_buf_fill_checker_slice(rect, rect_float, data->width, num_scanlines, scanline); } void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int width, int height) @@ -444,16 +446,15 @@ typedef struct FillCheckerColorThreadData { int width, height; } FillCheckerColorThreadData; -static void checker_board_color_prepare_thread_do(void *data_v, - int start_scanline, - int num_scanlines) +static void checker_board_color_prepare_thread_do(void *data_v, int scanline) { FillCheckerColorThreadData *data = (FillCheckerColorThreadData *)data_v; - size_t offset = ((size_t)data->width) * start_scanline * 4; + const int num_scanlines = 1; + size_t offset = ((size_t)data->width) * scanline * 4; unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; checker_board_color_prepare_slice( - rect, rect_float, data->width, num_scanlines, start_scanline, data->height); + rect, rect_float, data->width, num_scanlines, scanline, data->height); } void BKE_image_buf_fill_checker_color(unsigned char *rect, diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 2ac10586fd9..fb9c38f51b7 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1000,7 +1000,7 @@ void BKE_main_collection_sync_remap(const Main *bmain) /* On remapping of object or collection pointers free caches. */ /* TODO: try to make this faster */ - for (const Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { MEM_SAFE_FREE(view_layer->object_bases_array); @@ -1009,6 +1009,10 @@ void BKE_main_collection_sync_remap(const Main *bmain) view_layer->object_bases_hash = NULL; } } + + BKE_collection_object_cache_free(scene->master_collection); + DEG_id_tag_update_ex((Main *)bmain, &scene->master_collection->id, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update_ex((Main *)bmain, &scene->id, ID_RECALC_COPY_ON_WRITE); } for (Collection *collection = bmain->collections.first; collection; diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index f26b85338f0..b7cacba20b3 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1952,7 +1952,7 @@ void BKE_library_make_local(Main *bmain, } /* The check on the fourth line (LIB_TAG_PRE_EXISTING) is done so it's possible to tag data * you don't want to be made local, used for appending data, - * so any libdata already linked wont become local (very nasty + * so any libdata already linked won't become local (very nasty * to discover all your links are lost after appending). * Also, never ever make proxified objects local, would not make any sense. */ /* Some more notes: diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index 8e21ae88aa6..f6988a2d71f 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -123,7 +123,7 @@ TEST(lib_id_main_unique_name, local_ids_1) test_lib_id_main_sort_check_order({id_a, id_b, id_c}); BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false); + BKE_id_new_name_validate(&ctx.bmain->objects, id_c, nullptr, false); EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); EXPECT_TRUE(ctx.bmain->objects.first == id_a); @@ -150,7 +150,7 @@ TEST(lib_id_main_unique_name, linked_ids_1) id_b->lib = lib_a; id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, nullptr, true); EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); EXPECT_TRUE(ctx.bmain->objects.first == id_c); @@ -160,7 +160,7 @@ TEST(lib_id_main_unique_name, linked_ids_1) id_b->lib = lib_b; id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, nullptr, true); EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); EXPECT_TRUE(ctx.bmain->objects.first == id_c); diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 6b2ffa3b944..9a45f484581 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -52,12 +52,17 @@ #include "BKE_report.h" #include "BKE_scene.h" +#include "BLO_readfile.h" + #include "BLI_ghash.h" +#include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_task.h" #include "BLI_utildefines.h" +#include "PIL_time.h" + #include "RNA_access.h" #include "RNA_types.h" @@ -466,11 +471,14 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, typedef struct LibOverrideGroupTagData { Main *bmain; + Scene *scene; ID *id_root; uint tag; uint missing_tag; /* Whether we are looping on override data, or their references (linked) one. */ bool is_override; + /* Whether we are creating new override, or resyncing existing one. */ + bool is_resync; } LibOverrideGroupTagData; /* Tag all IDs in dependency relationships within an override hierarchy/group. @@ -591,7 +599,9 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; + Scene *scene = data->scene; ID *id_root = data->id_root; + const bool is_resync = data->is_resync; BLI_assert(!data->is_override); if ((id_root->tag & LIB_TAG_MISSING)) { @@ -616,6 +626,43 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) } } } + + /* For each object tagged for override, ensure we get at least one local or liboverride + * collection to host it. Avoids getting a bunch of random object in the scene's master + * collection when all objects' dependencies are not properly 'packed' into a single root + * collection. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ID_IS_LINKED(ob) && (ob->id.tag & data->tag) != 0) { + Collection *instantiating_collection = NULL; + Collection *instantiating_collection_override_candidate = NULL; + /* Loop over all collections instantiating the object, if we already have a 'locale' one we + * have nothing to do, otherwise try to find a 'linked' one that we can override too. */ + while ((instantiating_collection = BKE_collection_object_find( + bmain, scene, instantiating_collection, ob)) != NULL) { + /* In (recursive) resync case, if a collection of a 'parent' lib instantiates the linked + * object, it is also fine. */ + if (!ID_IS_LINKED(instantiating_collection) || + (is_resync && ID_IS_LINKED(id_root) && + instantiating_collection->id.lib->temp_index < id_root->lib->temp_index)) { + break; + } + else if (ID_IS_LINKED(instantiating_collection) && + (!is_resync || instantiating_collection->id.lib == id_root->lib)) { + instantiating_collection_override_candidate = instantiating_collection; + } + } + + if (instantiating_collection == NULL && + instantiating_collection_override_candidate != NULL) { + if ((instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING)) { + instantiating_collection_override_candidate->id.tag |= data->missing_tag; + } + else { + instantiating_collection_override_candidate->id.tag |= data->tag; + } + } + } + } } } @@ -694,14 +741,16 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) lib_override_overrides_group_tag_recursive(data); } -static bool lib_override_library_create_do(Main *bmain, ID *id_root) +static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_root) { BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, + .scene = scene, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, - .is_override = false}; + .is_override = false, + .is_resync = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -862,7 +911,7 @@ bool BKE_lib_override_library_create(Main *bmain, *r_id_root_override = NULL; } - const bool success = lib_override_library_create_do(bmain, id_root); + const bool success = lib_override_library_create_do(bmain, scene, id_root); if (!success) { return success; @@ -958,7 +1007,7 @@ bool BKE_lib_override_library_resync(Main *bmain, Collection *override_resync_residual_storage, const bool do_hierarchy_enforce, const bool do_post_process, - ReportList *reports) + BlendFileReadReport *reports) { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); @@ -966,10 +1015,12 @@ bool BKE_lib_override_library_resync(Main *bmain, BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, + .scene = scene, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, - .is_override = true}; + .is_override = true, + .is_resync = true}; lib_override_overrides_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -1286,7 +1337,7 @@ bool BKE_lib_override_library_resync(Main *bmain, id_root = id_root_reference->newid; if (user_edited_overrides_deletion_count > 0) { - BKE_reportf(reports, + BKE_reportf(reports != NULL ? reports->reports : NULL, RPT_WARNING, "During resync of data-block %s, %d obsolete overrides were deleted, that had " "local changes defined by user", @@ -1438,8 +1489,11 @@ static void lib_override_library_main_resync_on_library_indirect_level( ViewLayer *view_layer, Collection *override_resync_residual_storage, const int library_indirect_level, - ReportList *reports) + BlendFileReadReport *reports) { + const bool do_reports_recursive_resync_timing = (library_indirect_level != 0); + const double init_time = do_reports_recursive_resync_timing ? PIL_check_seconds_timer() : 0.0; + BKE_main_relations_create(bmain, 0); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); @@ -1460,10 +1514,12 @@ static void lib_override_library_main_resync_on_library_indirect_level( } LibOverrideGroupTagData data = {.bmain = bmain, + .scene = scene, .id_root = id->override_library->reference, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, - .is_override = false}; + .is_override = false, + .is_resync = true}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); @@ -1530,6 +1586,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } + Library *library = id->lib; int level = 0; /* In complex non-supported cases, with several different override hierarchies sharing @@ -1541,12 +1598,21 @@ static void lib_override_library_main_resync_on_library_indirect_level( id = lib_override_library_main_resync_find_root_recurse(id, &level); id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); + BLI_assert(id->lib == library); do_continue = true; - CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, id->lib); + CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, library); const bool success = BKE_lib_override_library_resync( bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports); CLOG_INFO(&LOG, 2, "\tSuccess: %d", success); + if (success) { + reports->count.resynced_lib_overrides++; + if (library_indirect_level > 0 && + BLI_linklist_index(reports->resynced_lib_overrides_libraries, library) < 0) { + BLI_linklist_prepend(&reports->resynced_lib_overrides_libraries, library); + reports->resynced_lib_overrides_libraries_count++; + } + } break; } FOREACH_MAIN_LISTBASE_ID_END; @@ -1556,6 +1622,10 @@ static void lib_override_library_main_resync_on_library_indirect_level( } FOREACH_MAIN_LISTBASE_END; } + + if (do_reports_recursive_resync_timing) { + reports->duration.lib_overrides_recursive_resync += PIL_check_seconds_timer() - init_time; + } } static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) @@ -1633,7 +1703,7 @@ static int lib_override_libraries_index_define(Main *bmain) void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer, - ReportList *reports) + BlendFileReadReport *reports) { /* We use a specific collection to gather/store all 'orphaned' override collections and objects * generated by re-sync-process. This avoids putting them in scene's master collection. */ @@ -1688,10 +1758,12 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) /* Tag all library overrides in the chains of dependencies from the given root one. */ BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, + .scene = NULL, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, - .is_override = true}; + .is_override = true, + .is_resync = false}; lib_override_overrides_group_tag(&data); BKE_main_relations_free(bmain); @@ -2335,8 +2407,7 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for } struct LibOverrideOpCreateData create_pool_data = {.bmain = bmain, .changed = false}; - TaskPool *task_pool = BLI_task_pool_create( - &create_pool_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH); FOREACH_MAIN_ID_BEGIN (bmain, id) { if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) && diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index ddbc7e7d1ef..08d3236f3a9 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -900,9 +900,11 @@ Mesh *BKE_mesh_new_nomain( return mesh; } -/* Copy user editable settings that we want to preserve through the modifier stack - * or operations where a mesh with new topology is created based on another mesh. */ -void BKE_mesh_copy_settings(Mesh *me_dst, const Mesh *me_src) +/** + * Copy user editable settings that we want to preserve + * when a new mesh is based on an existing mesh. + */ +void BKE_mesh_copy_parameters(Mesh *me_dst, const Mesh *me_src) { /* Copy general settings. */ me_dst->editflag = me_src->editflag; @@ -920,6 +922,20 @@ void BKE_mesh_copy_settings(Mesh *me_dst, const Mesh *me_src) me_dst->texflag = me_src->texflag; copy_v3_v3(me_dst->loc, me_src->loc); copy_v3_v3(me_dst->size, me_src->size); +} + +/** + * A version of #BKE_mesh_copy_parameters that is intended for evaluated output + * (the modifier stack for example). + * + * \warning User counts are not handled for ID's. + */ +void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src) +{ + /* User counts aren't handled, don't copy into a mesh from #G_MAIN. */ + BLI_assert(me_dst->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)); + + BKE_mesh_copy_parameters(me_dst, me_src); /* Copy materials. */ if (me_dst->mat != NULL) { @@ -951,7 +967,7 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src, me_dst->totpoly = polys_len; me_dst->cd_flag = me_src->cd_flag; - BKE_mesh_copy_settings(me_dst, me_src); + BKE_mesh_copy_parameters_for_eval(me_dst, me_src); CustomData_copy(&me_src->vdata, &me_dst->vdata, mask.vmask, CD_CALLOC, verts_len); CustomData_copy(&me_src->edata, &me_dst->edata, mask.emask, CD_CALLOC, edges_len); @@ -1038,7 +1054,7 @@ Mesh *BKE_mesh_from_bmesh_nomain(BMesh *bm, BLI_assert(params->calc_object_remap == false); Mesh *mesh = BKE_id_new_nomain(ID_ME, NULL); BM_mesh_bm_to_me(NULL, bm, mesh, params); - BKE_mesh_copy_settings(mesh, me_settings); + BKE_mesh_copy_parameters_for_eval(mesh, me_settings); return mesh; } @@ -1048,7 +1064,7 @@ Mesh *BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, { Mesh *mesh = BKE_id_new_nomain(ID_ME, NULL); BM_mesh_bm_to_me_for_eval(bm, mesh, cd_mask_extra); - BKE_mesh_copy_settings(mesh, me_settings); + BKE_mesh_copy_parameters_for_eval(mesh, me_settings); return mesh; } @@ -1198,9 +1214,11 @@ void BKE_mesh_orco_verts_transform(Mesh *me, float (*orco)[3], int totvert, int } } -/* rotates the vertices of a face in case v[2] or v[3] (vertex index) is = 0. - * this is necessary to make the if (mface->v4) check for quads work */ -int test_index_face(MFace *mface, CustomData *fdata, int mfindex, int nr) +/** + * Rotates the vertices of a face in case v[2] or v[3] (vertex index) is = 0. + * this is necessary to make the if #MFace.v4 check for quads work. + */ +int BKE_mesh_mface_index_validate(MFace *mface, CustomData *fdata, int mfindex, int nr) { /* first test if the face is legal */ if ((mface->v3 || nr == 4) && mface->v3 == mface->v4) { @@ -1244,8 +1262,8 @@ int test_index_face(MFace *mface, CustomData *fdata, int mfindex, int nr) if (mface->v3 == 0) { static int corner_indices[4] = {1, 2, 0, 3}; - SWAP(unsigned int, mface->v1, mface->v2); - SWAP(unsigned int, mface->v2, mface->v3); + SWAP(uint, mface->v1, mface->v2); + SWAP(uint, mface->v2, mface->v3); if (fdata) { CustomData_swap_corners(fdata, mfindex, corner_indices); @@ -1256,8 +1274,8 @@ int test_index_face(MFace *mface, CustomData *fdata, int mfindex, int nr) if (mface->v3 == 0 || mface->v4 == 0) { static int corner_indices[4] = {2, 3, 0, 1}; - SWAP(unsigned int, mface->v1, mface->v3); - SWAP(unsigned int, mface->v2, mface->v4); + SWAP(uint, mface->v1, mface->v3); + SWAP(uint, mface->v2, mface->v4); if (fdata) { CustomData_swap_corners(fdata, mfindex, corner_indices); @@ -1359,7 +1377,7 @@ void BKE_mesh_material_index_clear(Mesh *me) } } -void BKE_mesh_material_remap(Mesh *me, const unsigned int *remap, unsigned int remap_len) +void BKE_mesh_material_remap(Mesh *me, const uint *remap, uint remap_len) { const short remap_len_short = (short)remap_len; @@ -1423,10 +1441,7 @@ int poly_find_loop_from_vert(const MPoly *poly, const MLoop *loopstart, uint ver * vertex. Returns the index of the loop matching vertex, or -1 if the * vertex is not in \a poly */ -int poly_get_adj_loops_from_vert(const MPoly *poly, - const MLoop *mloop, - unsigned int vert, - unsigned int r_adj[2]) +int poly_get_adj_loops_from_vert(const MPoly *poly, const MLoop *mloop, uint vert, uint r_adj[2]) { int corner = poly_find_loop_from_vert(poly, &mloop[poly->loopstart], vert); @@ -1521,12 +1536,12 @@ void BKE_mesh_transform(Mesh *me, const float mat[4][4], bool do_keys) void BKE_mesh_translate(Mesh *me, const float offset[3], const bool do_keys) { - MVert *mvert = CustomData_duplicate_referenced_layer(&me->vdata, CD_MVERT, me->totvert); + CustomData_duplicate_referenced_layer(&me->vdata, CD_MVERT, me->totvert); /* If the referenced layer has been re-allocated need to update pointers stored in the mesh. */ BKE_mesh_update_customdata_pointers(me, false); int i = me->totvert; - for (mvert = me->mvert; i--; mvert++) { + for (MVert *mvert = me->mvert; i--; mvert++) { add_v3_v3(mvert->co, offset); } @@ -1541,22 +1556,6 @@ void BKE_mesh_translate(Mesh *me, const float offset[3], const bool do_keys) } } -void BKE_mesh_tessface_calc(Mesh *mesh) -{ - mesh->totface = BKE_mesh_tessface_calc_ex( - &mesh->fdata, - &mesh->ldata, - &mesh->pdata, - mesh->mvert, - mesh->totface, - mesh->totloop, - mesh->totpoly, - /* calc normals right after, don't copy from polys here */ - false); - - BKE_mesh_update_customdata_pointers(mesh, true); -} - void BKE_mesh_tessface_ensure(Mesh *mesh) { if (mesh->totpoly && mesh->totface == 0) { diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index e893a3983bd..934f9ce5018 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -148,7 +148,7 @@ static void make_edges_mdata_extend( int totedge = *r_totedge; int totedge_new; EdgeHash *eh; - unsigned int eh_reserve; + uint eh_reserve; const MPoly *mp; int i; @@ -174,7 +174,7 @@ static void make_edges_mdata_extend( if (totedge_new) { EdgeHashIterator *ehi; MEdge *medge; - unsigned int e_index = totedge; + uint e_index = totedge; *r_alledge = medge = (*r_alledge ? MEM_reallocN(*r_alledge, sizeof(MEdge) * (totedge + totedge_new)) : @@ -719,17 +719,17 @@ typedef struct EdgeLink { typedef struct VertLink { Link *next, *prev; - unsigned int index; + uint index; } VertLink; -static void prependPolyLineVert(ListBase *lb, unsigned int index) +static void prependPolyLineVert(ListBase *lb, uint index) { VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink"); vl->index = index; BLI_addhead(lb, vl); } -static void appendPolyLineVert(ListBase *lb, unsigned int index) +static void appendPolyLineVert(ListBase *lb, uint index) { VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink"); vl->index = index; @@ -784,8 +784,8 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed bool closed = false; int totpoly = 0; MEdge *med_current = ((EdgeLink *)edges.last)->edge; - unsigned int startVert = med_current->v1; - unsigned int endVert = med_current->v2; + uint startVert = med_current->v1; + uint endVert = med_current->v2; bool ok = true; appendPolyLineVert(&polyline, startVert); @@ -1113,7 +1113,7 @@ static void curve_to_mesh_eval_ensure(Object *object) * Brecht says hold off with that. */ Mesh *mesh_eval = NULL; BKE_displist_make_curveTypes_forRender( - NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval, false); + NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, false, &mesh_eval); /* Note: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a * real issue currently, code here is broken in more than one way, fix(es) will be done @@ -1704,7 +1704,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, tmp.texflag &= ~ME_AUTOSPACE_EVALUATED; /* Clear any run-time data. - * Even though this mesh wont typically have run-time data, the Python API can for e.g. + * Even though this mesh won't typically have run-time data, the Python API can for e.g. * create loop-triangle cache here, which is confusing when left in the mesh, see: T81136. */ BKE_mesh_runtime_clear_geometry(&tmp); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 345546bc9cf..2260ffc668a 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -40,7 +40,6 @@ #include "BLI_linklist_stack.h" #include "BLI_math.h" #include "BLI_memarena.h" -#include "BLI_polyfill_2d.h" #include "BLI_stack.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -478,7 +477,7 @@ void BKE_mesh_calc_normals_looptri(MVert *mverts, for (int i = 0; i < looptri_num; i++) { const MLoopTri *lt = &looptri[i]; float *f_no = fnors[i]; - const unsigned int vtri[3] = { + const uint vtri[3] = { mloop[lt->tri[0]].v, mloop[lt->tri[1]].v, mloop[lt->tri[2]].v, @@ -1058,7 +1057,7 @@ static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopS if (lnors_spacearr) { float vec_curr[3], vec_prev[3]; - const unsigned int mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ const MVert *mv_pivot = &mverts[mv_pivot_index]; const MEdge *me_curr = &medges[ml_curr->e]; const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : @@ -1117,7 +1116,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli * number of sharp edges per vertex, I doubt the additional memory usage would be worth it, * especially as it should not be a common case in real-life meshes anyway). */ - const unsigned int mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ const MVert *mv_pivot = &mverts[mv_pivot_index]; /* ml_curr would be mlfan_prev if we needed that one. */ @@ -1361,7 +1360,7 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops, const int ml_prev_index, const int mp_curr_index) { - const unsigned int mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ const int *e2lfan_curr; const MLoop *mlfan_curr; /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ @@ -1715,8 +1714,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts, loop_split_generator(NULL, &common_data); } else { - TaskPool *task_pool = BLI_task_pool_create( - &common_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH); loop_split_generator(task_pool, &common_data); @@ -2134,7 +2132,7 @@ void BKE_mesh_normals_loop_to_vertex(const int numVerts, int i; const MLoop *ml; for (i = 0, ml = mloops; i < numLoops; i++, ml++) { - const unsigned int v = ml->v; + const uint v = ml->v; add_v3_v3(r_vert_clnors[v], clnors[i]); vert_loops_nbr[v]++; @@ -2316,7 +2314,7 @@ float BKE_mesh_calc_poly_area(const MPoly *mpoly, const MLoop *loopstart, const } /* finally calculate the area */ - float area = area_poly_v3((const float(*)[3])vertexcos, (unsigned int)mpoly->totloop); + float area = area_poly_v3((const float(*)[3])vertexcos, (uint)mpoly->totloop); return area; } @@ -2519,9 +2517,7 @@ void BKE_mesh_poly_edgehash_insert(EdgeHash *ehash, const MPoly *mp, const MLoop } } -void BKE_mesh_poly_edgebitmap_insert(unsigned int *edge_bitmap, - const MPoly *mp, - const MLoop *mloop) +void BKE_mesh_poly_edgebitmap_insert(uint *edge_bitmap, const MPoly *mp, const MLoop *mloop) { const MLoop *ml; int i = mp->totloop; @@ -2567,7 +2563,7 @@ bool BKE_mesh_center_median_from_polys(const Mesh *me, float r_cent[3]) const MLoop *mloop = me->mloop; const MVert *mvert = me->mvert; zero_v3(r_cent); - for (mpoly = me->mpoly; i--; mpoly++) { + for (; i--; mpoly++) { int loopend = mpoly->loopstart + mpoly->totloop; for (int j = mpoly->loopstart; j < loopend; j++) { add_v3_v3(r_cent, mvert[mloop[j].v].co); @@ -2789,638 +2785,6 @@ void BKE_mesh_calc_volume(const MVert *mverts, /** \name NGon Tessellation (NGon/Tessface Conversion) * \{ */ -/** - * Convert a triangle or quadrangle of loop/poly data to tessface data - */ -void BKE_mesh_loops_to_mface_corners( - CustomData *fdata, - CustomData *ldata, - CustomData *UNUSED(pdata), - unsigned int lindex[4], - int findex, - const int UNUSED(polyindex), - const int mf_len, /* 3 or 4 */ - - /* cache values to avoid lookups every time */ - const int numUV, /* CustomData_number_of_layers(ldata, CD_MLOOPUV) */ - const int numCol, /* CustomData_number_of_layers(ldata, CD_MLOOPCOL) */ - const bool hasPCol, /* CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL) */ - const bool hasOrigSpace, /* CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP) */ - const bool hasLNor /* CustomData_has_layer(ldata, CD_NORMAL) */ -) -{ - MTFace *texface; - MCol *mcol; - MLoopCol *mloopcol; - MLoopUV *mloopuv; - int i, j; - - for (i = 0; i < numUV; i++) { - texface = CustomData_get_n(fdata, CD_MTFACE, findex, i); - - for (j = 0; j < mf_len; j++) { - mloopuv = CustomData_get_n(ldata, CD_MLOOPUV, (int)lindex[j], i); - copy_v2_v2(texface->uv[j], mloopuv->uv); - } - } - - for (i = 0; i < numCol; i++) { - mcol = CustomData_get_n(fdata, CD_MCOL, findex, i); - - for (j = 0; j < mf_len; j++) { - mloopcol = CustomData_get_n(ldata, CD_MLOOPCOL, (int)lindex[j], i); - MESH_MLOOPCOL_TO_MCOL(mloopcol, &mcol[j]); - } - } - - if (hasPCol) { - mcol = CustomData_get(fdata, findex, CD_PREVIEW_MCOL); - - for (j = 0; j < mf_len; j++) { - mloopcol = CustomData_get(ldata, (int)lindex[j], CD_PREVIEW_MLOOPCOL); - MESH_MLOOPCOL_TO_MCOL(mloopcol, &mcol[j]); - } - } - - if (hasOrigSpace) { - OrigSpaceFace *of = CustomData_get(fdata, findex, CD_ORIGSPACE); - OrigSpaceLoop *lof; - - for (j = 0; j < mf_len; j++) { - lof = CustomData_get(ldata, (int)lindex[j], CD_ORIGSPACE_MLOOP); - copy_v2_v2(of->uv[j], lof->uv); - } - } - - if (hasLNor) { - short(*tlnors)[3] = CustomData_get(fdata, findex, CD_TESSLOOPNORMAL); - - for (j = 0; j < mf_len; j++) { - normal_float_to_short_v3(tlnors[j], CustomData_get(ldata, (int)lindex[j], CD_NORMAL)); - } - } -} - -/** - * Convert all CD layers from loop/poly to tessface data. - * - * \param loopindices: is an array of an int[4] per tessface, - * mapping tessface's verts to loops indices. - * - * \note when mface is not NULL, mface[face_index].v4 - * is used to test quads, else, loopindices[face_index][3] is used. - */ -void BKE_mesh_loops_to_tessdata(CustomData *fdata, - CustomData *ldata, - MFace *mface, - const int *polyindices, - unsigned int (*loopindices)[4], - const int num_faces) -{ - /* Note: performances are sub-optimal when we get a NULL mface, - * we could be ~25% quicker with dedicated code... - * Issue is, unless having two different functions with nearly the same code, - * there's not much ways to solve this. Better imho to live with it for now. :/ --mont29 - */ - const int numUV = CustomData_number_of_layers(ldata, CD_MLOOPUV); - const int numCol = CustomData_number_of_layers(ldata, CD_MLOOPCOL); - const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL); - const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP); - const bool hasLoopNormal = CustomData_has_layer(ldata, CD_NORMAL); - const bool hasLoopTangent = CustomData_has_layer(ldata, CD_TANGENT); - int findex, i, j; - const int *pidx; - unsigned int(*lidx)[4]; - - for (i = 0; i < numUV; i++) { - MTFace *texface = CustomData_get_layer_n(fdata, CD_MTFACE, i); - MLoopUV *mloopuv = CustomData_get_layer_n(ldata, CD_MLOOPUV, i); - - for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; - pidx++, lidx++, findex++, texface++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - copy_v2_v2(texface->uv[j], mloopuv[(*lidx)[j]].uv); - } - } - } - - for (i = 0; i < numCol; i++) { - MCol(*mcol)[4] = CustomData_get_layer_n(fdata, CD_MCOL, i); - MLoopCol *mloopcol = CustomData_get_layer_n(ldata, CD_MLOOPCOL, i); - - for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); - } - } - } - - if (hasPCol) { - MCol(*mcol)[4] = CustomData_get_layer(fdata, CD_PREVIEW_MCOL); - MLoopCol *mloopcol = CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); - - for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); - } - } - } - - if (hasOrigSpace) { - OrigSpaceFace *of = CustomData_get_layer(fdata, CD_ORIGSPACE); - OrigSpaceLoop *lof = CustomData_get_layer(ldata, CD_ORIGSPACE_MLOOP); - - for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - copy_v2_v2(of->uv[j], lof[(*lidx)[j]].uv); - } - } - } - - if (hasLoopNormal) { - short(*fnors)[4][3] = CustomData_get_layer(fdata, CD_TESSLOOPNORMAL); - float(*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); - - for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, fnors++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - normal_float_to_short_v3((*fnors)[j], lnors[(*lidx)[j]]); - } - } - } - - if (hasLoopTangent) { - /* need to do for all uv maps at some point */ - float(*ftangents)[4] = CustomData_get_layer(fdata, CD_TANGENT); - float(*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); - - for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; - pidx++, lidx++, findex++) { - int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; - for (j = nverts; j--;) { - copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]); - } - } - } -} - -void BKE_mesh_tangent_loops_to_tessdata(CustomData *fdata, - CustomData *ldata, - MFace *mface, - const int *polyindices, - unsigned int (*loopindices)[4], - const int num_faces, - const char *layer_name) -{ - /* Note: performances are sub-optimal when we get a NULL mface, - * we could be ~25% quicker with dedicated code... - * Issue is, unless having two different functions with nearly the same code, - * there's not much ways to solve this. Better imho to live with it for now. :/ --mont29 - */ - - float(*ftangents)[4] = NULL; - float(*ltangents)[4] = NULL; - - int findex, j; - const int *pidx; - unsigned int(*lidx)[4]; - - if (layer_name) { - ltangents = CustomData_get_layer_named(ldata, CD_TANGENT, layer_name); - } - else { - ltangents = CustomData_get_layer(ldata, CD_TANGENT); - } - - if (ltangents) { - /* need to do for all uv maps at some point */ - if (layer_name) { - ftangents = CustomData_get_layer_named(fdata, CD_TANGENT, layer_name); - } - else { - ftangents = CustomData_get_layer(fdata, CD_TANGENT); - } - if (ftangents) { - for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; - pidx++, lidx++, findex++) { - int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; - for (j = nverts; j--;) { - copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]); - } - } - } - } -} - -/** - * Recreate tessellation. - * - * \param do_face_nor_copy: Controls whether the normals from the poly - * are copied to the tessellated faces. - * - * \return number of tessellation faces. - */ -int BKE_mesh_tessface_calc_ex(CustomData *fdata, - CustomData *ldata, - CustomData *pdata, - MVert *mvert, - int totface, - int totloop, - int totpoly, - const bool do_face_nor_copy) -{ - /* use this to avoid locking pthread for _every_ polygon - * and calling the fill function */ - -#define USE_TESSFACE_SPEEDUP -#define USE_TESSFACE_QUADS /* NEEDS FURTHER TESTING */ - -/* We abuse MFace->edcode to tag quad faces. See below for details. */ -#define TESSFACE_IS_QUAD 1 - - const int looptri_num = poly_to_tri_count(totpoly, totloop); - - MPoly *mp, *mpoly; - MLoop *ml, *mloop; - MFace *mface, *mf; - MemArena *arena = NULL; - int *mface_to_poly_map; - unsigned int(*lindices)[4]; - int poly_index, mface_index; - unsigned int j; - - mpoly = CustomData_get_layer(pdata, CD_MPOLY); - mloop = CustomData_get_layer(ldata, CD_MLOOP); - - /* allocate the length of totfaces, avoid many small reallocs, - * if all faces are tri's it will be correct, quads == 2x allocs */ - /* take care. we are _not_ calloc'ing so be sure to initialize each field */ - mface_to_poly_map = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface_to_poly_map), __func__); - mface = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface), __func__); - lindices = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*lindices), __func__); - - mface_index = 0; - mp = mpoly; - for (poly_index = 0; poly_index < totpoly; poly_index++, mp++) { - const unsigned int mp_loopstart = (unsigned int)mp->loopstart; - const unsigned int mp_totloop = (unsigned int)mp->totloop; - unsigned int l1, l2, l3, l4; - unsigned int *lidx; - if (mp_totloop < 3) { - /* do nothing */ - } - -#ifdef USE_TESSFACE_SPEEDUP - -# define ML_TO_MF(i1, i2, i3) \ - mface_to_poly_map[mface_index] = poly_index; \ - mf = &mface[mface_index]; \ - lidx = lindices[mface_index]; \ - /* set loop indices, transformed to vert indices later */ \ - l1 = mp_loopstart + i1; \ - l2 = mp_loopstart + i2; \ - l3 = mp_loopstart + i3; \ - mf->v1 = mloop[l1].v; \ - mf->v2 = mloop[l2].v; \ - mf->v3 = mloop[l3].v; \ - mf->v4 = 0; \ - lidx[0] = l1; \ - lidx[1] = l2; \ - lidx[2] = l3; \ - lidx[3] = 0; \ - mf->mat_nr = mp->mat_nr; \ - mf->flag = mp->flag; \ - mf->edcode = 0; \ - (void)0 - -/* ALMOST IDENTICAL TO DEFINE ABOVE (see EXCEPTION) */ -# define ML_TO_MF_QUAD() \ - mface_to_poly_map[mface_index] = poly_index; \ - mf = &mface[mface_index]; \ - lidx = lindices[mface_index]; \ - /* set loop indices, transformed to vert indices later */ \ - l1 = mp_loopstart + 0; /* EXCEPTION */ \ - l2 = mp_loopstart + 1; /* EXCEPTION */ \ - l3 = mp_loopstart + 2; /* EXCEPTION */ \ - l4 = mp_loopstart + 3; /* EXCEPTION */ \ - mf->v1 = mloop[l1].v; \ - mf->v2 = mloop[l2].v; \ - mf->v3 = mloop[l3].v; \ - mf->v4 = mloop[l4].v; \ - lidx[0] = l1; \ - lidx[1] = l2; \ - lidx[2] = l3; \ - lidx[3] = l4; \ - mf->mat_nr = mp->mat_nr; \ - mf->flag = mp->flag; \ - mf->edcode = TESSFACE_IS_QUAD; \ - (void)0 - - else if (mp_totloop == 3) { - ML_TO_MF(0, 1, 2); - mface_index++; - } - else if (mp_totloop == 4) { -# ifdef USE_TESSFACE_QUADS - ML_TO_MF_QUAD(); - mface_index++; -# else - ML_TO_MF(0, 1, 2); - mface_index++; - ML_TO_MF(0, 2, 3); - mface_index++; -# endif - } -#endif /* USE_TESSFACE_SPEEDUP */ - else { - const float *co_curr, *co_prev; - - float normal[3]; - - float axis_mat[3][3]; - float(*projverts)[2]; - unsigned int(*tris)[3]; - - const unsigned int totfilltri = mp_totloop - 2; - - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - - tris = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)totfilltri); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)mp_totloop); - - zero_v3(normal); - - /* calc normal, flipped: to get a positive 2d cross product */ - ml = mloop + mp_loopstart; - co_prev = mvert[ml[mp_totloop - 1].v].co; - for (j = 0; j < mp_totloop; j++, ml++) { - co_curr = mvert[ml->v].co; - add_newell_cross_v3_v3v3(normal, co_prev, co_curr); - co_prev = co_curr; - } - if (UNLIKELY(normalize_v3(normal) == 0.0f)) { - normal[2] = 1.0f; - } - - /* project verts to 2d */ - axis_dominant_v3_to_m3_negate(axis_mat, normal); - - ml = mloop + mp_loopstart; - for (j = 0; j < mp_totloop; j++, ml++) { - mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); - } - - BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, arena); - - /* apply fill */ - for (j = 0; j < totfilltri; j++) { - unsigned int *tri = tris[j]; - lidx = lindices[mface_index]; - - mface_to_poly_map[mface_index] = poly_index; - mf = &mface[mface_index]; - - /* set loop indices, transformed to vert indices later */ - l1 = mp_loopstart + tri[0]; - l2 = mp_loopstart + tri[1]; - l3 = mp_loopstart + tri[2]; - - mf->v1 = mloop[l1].v; - mf->v2 = mloop[l2].v; - mf->v3 = mloop[l3].v; - mf->v4 = 0; - - lidx[0] = l1; - lidx[1] = l2; - lidx[2] = l3; - lidx[3] = 0; - - mf->mat_nr = mp->mat_nr; - mf->flag = mp->flag; - mf->edcode = 0; - - mface_index++; - } - - BLI_memarena_clear(arena); - } - } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - CustomData_free(fdata, totface); - totface = mface_index; - - BLI_assert(totface <= looptri_num); - - /* not essential but without this we store over-alloc'd memory in the CustomData layers */ - if (LIKELY(looptri_num != totface)) { - mface = MEM_reallocN(mface, sizeof(*mface) * (size_t)totface); - mface_to_poly_map = MEM_reallocN(mface_to_poly_map, - sizeof(*mface_to_poly_map) * (size_t)totface); - } - - CustomData_add_layer(fdata, CD_MFACE, CD_ASSIGN, mface, totface); - - /* CD_ORIGINDEX will contain an array of indices from tessfaces to the polygons - * they are directly tessellated from */ - CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, mface_to_poly_map, totface); - CustomData_from_bmeshpoly(fdata, ldata, totface); - - if (do_face_nor_copy) { - /* If polys have a normals layer, copying that to faces can help - * avoid the need to recalculate normals later */ - if (CustomData_has_layer(pdata, CD_NORMAL)) { - float(*pnors)[3] = CustomData_get_layer(pdata, CD_NORMAL); - float(*fnors)[3] = CustomData_add_layer(fdata, CD_NORMAL, CD_CALLOC, NULL, totface); - for (mface_index = 0; mface_index < totface; mface_index++) { - copy_v3_v3(fnors[mface_index], pnors[mface_to_poly_map[mface_index]]); - } - } - } - - /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: - * Polygons take care of their loops ordering, hence not of their vertices ordering. - * Currently, our tfaces' fourth vertex index might be 0 even for a quad. However, - * we know our fourth loop index is never 0 for quads (because they are sorted for polygons, - * and our quads are still mere copies of their polygons). - * So we pass NULL as MFace pointer, and BKE_mesh_loops_to_tessdata - * will use the fourth loop index as quad test. - * ... - */ - BKE_mesh_loops_to_tessdata(fdata, ldata, NULL, mface_to_poly_map, lindices, totface); - - /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: - * ...However, most TFace code uses 'MFace->v4 == 0' test to check whether it is a tri or quad. - * test_index_face() will check this and rotate the tessellated face if needed. - */ -#ifdef USE_TESSFACE_QUADS - mf = mface; - for (mface_index = 0; mface_index < totface; mface_index++, mf++) { - if (mf->edcode == TESSFACE_IS_QUAD) { - test_index_face(mf, fdata, mface_index, 4); - mf->edcode = 0; - } - } -#endif - - MEM_freeN(lindices); - - return totface; - -#undef USE_TESSFACE_SPEEDUP -#undef USE_TESSFACE_QUADS - -#undef ML_TO_MF -#undef ML_TO_MF_QUAD -} - -/** - * Calculate tessellation into #MLoopTri which exist only for this purpose. - */ -void BKE_mesh_recalc_looptri(const MLoop *mloop, - const MPoly *mpoly, - const MVert *mvert, - int totloop, - int totpoly, - MLoopTri *mlooptri) -{ - /* use this to avoid locking pthread for _every_ polygon - * and calling the fill function */ - -#define USE_TESSFACE_SPEEDUP - - const MPoly *mp; - const MLoop *ml; - MLoopTri *mlt; - MemArena *arena = NULL; - int poly_index, mlooptri_index; - unsigned int j; - - mlooptri_index = 0; - mp = mpoly; - for (poly_index = 0; poly_index < totpoly; poly_index++, mp++) { - const unsigned int mp_loopstart = (unsigned int)mp->loopstart; - const unsigned int mp_totloop = (unsigned int)mp->totloop; - unsigned int l1, l2, l3; - if (mp_totloop < 3) { - /* do nothing */ - } - -#ifdef USE_TESSFACE_SPEEDUP - -# define ML_TO_MLT(i1, i2, i3) \ - { \ - mlt = &mlooptri[mlooptri_index]; \ - l1 = mp_loopstart + i1; \ - l2 = mp_loopstart + i2; \ - l3 = mp_loopstart + i3; \ - ARRAY_SET_ITEMS(mlt->tri, l1, l2, l3); \ - mlt->poly = (unsigned int)poly_index; \ - } \ - ((void)0) - - else if (mp_totloop == 3) { - ML_TO_MLT(0, 1, 2); - mlooptri_index++; - } - else if (mp_totloop == 4) { - ML_TO_MLT(0, 1, 2); - MLoopTri *mlt_a = mlt; - mlooptri_index++; - ML_TO_MLT(0, 2, 3); - MLoopTri *mlt_b = mlt; - mlooptri_index++; - - if (UNLIKELY(is_quad_flip_v3_first_third_fast(mvert[mloop[mlt_a->tri[0]].v].co, - mvert[mloop[mlt_a->tri[1]].v].co, - mvert[mloop[mlt_a->tri[2]].v].co, - mvert[mloop[mlt_b->tri[2]].v].co))) { - /* flip out of degenerate 0-2 state. */ - mlt_a->tri[2] = mlt_b->tri[2]; - mlt_b->tri[0] = mlt_a->tri[1]; - } - } -#endif /* USE_TESSFACE_SPEEDUP */ - else { - const float *co_curr, *co_prev; - - float normal[3]; - - float axis_mat[3][3]; - float(*projverts)[2]; - unsigned int(*tris)[3]; - - const unsigned int totfilltri = mp_totloop - 2; - - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - - tris = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)totfilltri); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)mp_totloop); - - zero_v3(normal); - - /* calc normal, flipped: to get a positive 2d cross product */ - ml = mloop + mp_loopstart; - co_prev = mvert[ml[mp_totloop - 1].v].co; - for (j = 0; j < mp_totloop; j++, ml++) { - co_curr = mvert[ml->v].co; - add_newell_cross_v3_v3v3(normal, co_prev, co_curr); - co_prev = co_curr; - } - if (UNLIKELY(normalize_v3(normal) == 0.0f)) { - normal[2] = 1.0f; - } - - /* project verts to 2d */ - axis_dominant_v3_to_m3_negate(axis_mat, normal); - - ml = mloop + mp_loopstart; - for (j = 0; j < mp_totloop; j++, ml++) { - mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); - } - - BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, arena); - - /* apply fill */ - for (j = 0; j < totfilltri; j++) { - unsigned int *tri = tris[j]; - - mlt = &mlooptri[mlooptri_index]; - - /* set loop indices, transformed to vert indices later */ - l1 = mp_loopstart + tri[0]; - l2 = mp_loopstart + tri[1]; - l3 = mp_loopstart + tri[2]; - - ARRAY_SET_ITEMS(mlt->tri, l1, l2, l3); - mlt->poly = (unsigned int)poly_index; - - mlooptri_index++; - } - - BLI_memarena_clear(arena); - } - } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - BLI_assert(mlooptri_index == poly_to_tri_count(totpoly, totloop)); - UNUSED_VARS_NDEBUG(totloop); - -#undef USE_TESSFACE_SPEEDUP -#undef ML_TO_MLT -} - static void bm_corners_to_loops_ex(ID *id, CustomData *fdata, CustomData *ldata, @@ -3630,7 +2994,7 @@ void BKE_mesh_convert_mfaces_to_mpolys_ex(ID *id, CustomData_external_read(fdata, id, CD_MASK_MDISPS, totface_i); } - eh = BLI_edgehash_new_ex(__func__, (unsigned int)totedge_i); + eh = BLI_edgehash_new_ex(__func__, (uint)totedge_i); /* build edge hash */ me = medge; @@ -3769,12 +3133,12 @@ void BKE_mesh_polygon_flip_ex(MPoly *mpoly, /* We also have to update loops edge * (they will get their original 'other edge', that is, * the original edge of their original previous loop)... */ - unsigned int prev_edge_index = mloop[loopstart].e; + uint prev_edge_index = mloop[loopstart].e; mloop[loopstart].e = mloop[loopend].e; for (loopstart++; loopend > loopstart; loopstart++, loopend--) { mloop[loopend].e = mloop[loopend - 1].e; - SWAP(unsigned int, mloop[loopstart].e, prev_edge_index); + SWAP(uint, mloop[loopstart].e, prev_edge_index); if (!loops_in_ldata) { SWAP(MLoop, mloop[loopstart], mloop[loopend]); @@ -4027,9 +3391,9 @@ void BKE_mesh_calc_relative_deform(const MPoly *mpoly, const MLoop *loopstart = mloop + mp->loopstart; for (int j = 0; j < mp->totloop; j++) { - unsigned int v_prev = loopstart[(mp->totloop + (j - 1)) % mp->totloop].v; - unsigned int v_curr = loopstart[j].v; - unsigned int v_next = loopstart[(j + 1) % mp->totloop].v; + uint v_prev = loopstart[(mp->totloop + (j - 1)) % mp->totloop].v; + uint v_curr = loopstart[j].v; + uint v_next = loopstart[(j + 1) % mp->totloop].v; float tvec[3]; diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index 2a364b183b2..6ac1aa9b2b9 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -65,7 +65,7 @@ class FairingContext { float r_adj_prev[3]) = 0; /* Get the other vertex index for a loop. */ - virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) = 0; + virtual int other_vertex_index_from_loop(const int loop, const uint v) = 0; int vertex_count_get() { @@ -257,7 +257,7 @@ class MeshFairingContext : public FairingContext { copy_v3_v3(r_adj_prev, co_[ME_POLY_LOOP_PREV(mloop_, p, corner)->v]); } - int other_vertex_index_from_loop(const int loop, const unsigned int v) override + int other_vertex_index_from_loop(const int loop, const uint v) override { MEdge *e = &medge_[mloop_[loop].e]; if (e->v1 == v) { @@ -332,7 +332,7 @@ class BMeshFairingContext : public FairingContext { copy_v3_v3(r_adj_prev, bmloop_[loop]->prev->v->co); } - int other_vertex_index_from_loop(const int loop, const unsigned int v) override + int other_vertex_index_from_loop(const int loop, const uint v) override { BMLoop *l = bmloop_[loop]; BMVert *bmvert = BM_vert_at_index(bm, v); diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index 81642b156d5..d4119f48193 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -48,8 +48,8 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, const MLoop *mloop, const MLoopUV *mloopuv, - unsigned int totpoly, - unsigned int totvert, + uint totpoly, + uint totvert, const float limit[2], const bool selected, const bool use_winding) @@ -57,7 +57,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, UvVertMap *vmap; UvMapVert *buf; const MPoly *mp; - unsigned int a; + uint a; int i, totuv, nverts; bool *winding = NULL; @@ -115,7 +115,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, } if (use_winding) { - winding[a] = cross_poly_v2(tf_uv, (unsigned int)nverts) > 0; + winding[a] = cross_poly_v2(tf_uv, (uint)nverts) > 0; } } } @@ -176,7 +176,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, return vmap; } -UvMapVert *BKE_mesh_uv_vert_map_get_vert(UvVertMap *vmap, unsigned int v) +UvMapVert *BKE_mesh_uv_vert_map_get_vert(UvVertMap *vmap, uint v) { return vmap->vert[v]; } @@ -239,7 +239,7 @@ static void mesh_vert_poly_or_loop_map_create(MeshElemMap **r_map, const MPoly *p = &mpoly[i]; for (j = 0; j < p->totloop; j++) { - unsigned int v = mloop[p->loopstart + j].v; + uint v = mloop[p->loopstart + j].v; map[v].indices[map[v].count] = do_loops ? p->loopstart + j : i; map[v].count++; @@ -362,7 +362,7 @@ void BKE_mesh_vert_edge_map_create( /* Find the users */ for (i = 0; i < totedge; i++) { - const unsigned int v[2] = {medge[i].v1, medge[i].v2}; + const uint v[2] = {medge[i].v1, medge[i].v2}; map[v[0]].indices[map[v[0]].count] = i; map[v[1]].indices[map[v[1]].count] = i; @@ -405,7 +405,7 @@ void BKE_mesh_vert_edge_vert_map_create( /* Find the users */ for (i = 0; i < totedge; i++) { - const unsigned int v[2] = {medge[i].v1, medge[i].v2}; + const uint v[2] = {medge[i].v1, medge[i].v2}; map[v[0]].indices[map[v[0]].count] = (int)v[1]; map[v[1]].indices[map[v[1]].count] = (int)v[0]; @@ -1025,8 +1025,8 @@ static bool mesh_check_island_boundary_uv(const MPoly *UNUSED(mp), BLI_assert(edge_to_loops->count >= 2 && (edge_to_loops->count % 2) == 0); - const unsigned int v1 = loops[edge_to_loops->indices[0]].v; - const unsigned int v2 = loops[edge_to_loops->indices[1]].v; + const uint v1 = loops[edge_to_loops->indices[0]].v; + const uint v2 = loops[edge_to_loops->indices[1]].v; const float *uvco_v1 = luvs[edge_to_loops->indices[0]].uv; const float *uvco_v2 = luvs[edge_to_loops->indices[1]].uv; for (int i = 2; i < edge_to_loops->count; i += 2) { diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c index e3b5e5ea434..fb73152cb8e 100644 --- a/source/blender/blenkernel/intern/mesh_merge.c +++ b/source/blender/blenkernel/intern/mesh_merge.c @@ -179,13 +179,13 @@ static int cddm_poly_compare(MLoop *mloop_array, /* Utility stuff for using GHash with polys, used by vertex merging. */ typedef struct PolyKey { - int poly_index; /* index of the MPoly within the derived mesh */ - int totloops; /* number of loops in the poly */ - unsigned int hash_sum; /* Sum of all vertices indices */ - unsigned int hash_xor; /* Xor of all vertices indices */ + int poly_index; /* index of the MPoly within the derived mesh */ + int totloops; /* number of loops in the poly */ + uint hash_sum; /* Sum of all vertices indices */ + uint hash_xor; /* Xor of all vertices indices */ } PolyKey; -static unsigned int poly_gset_hash_fn(const void *key) +static uint poly_gset_hash_fn(const void *key) { const PolyKey *pk = key; return pk->hash_sum; @@ -331,8 +331,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, med = mesh->medge; c = 0; for (i = 0; i < totedge; i++, med++) { - const unsigned int v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1; - const unsigned int v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2; + const uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1; + const uint v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2; if (LIKELY(v1 != v2)) { void **val_p; diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index 58d2a24f15b..b7cff624a04 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -787,7 +787,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, int j = 2; while (j--) { - const unsigned int vidx_dst = j ? e_dst->v1 : e_dst->v2; + const uint vidx_dst = j ? e_dst->v1 : e_dst->v2; /* Compute closest verts only once! */ if (v_dst_to_src_map[vidx_dst].hit_dist == -1.0f) { @@ -813,7 +813,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, /* Now, check all source edges of closest sources vertices, * and select the one giving the smallest total verts-to-verts distance. */ for (j = 2; j--;) { - const unsigned int vidx_dst = j ? e_dst->v1 : e_dst->v2; + const uint vidx_dst = j ? e_dst->v1 : e_dst->v2; const float first_dist = v_dst_to_src_map[vidx_dst].hit_dist; const int vidx_src = v_dst_to_src_map[vidx_dst].index; int *eidx_src, k; @@ -1542,7 +1542,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, mp_src = &polys_src[isld->indices[i]]; for (lidx_src = mp_src->loopstart; lidx_src < mp_src->loopstart + mp_src->totloop; lidx_src++) { - const unsigned int vidx_src = loops_src[lidx_src].v; + const uint vidx_src = loops_src[lidx_src].v; if (!BLI_BITMAP_TEST(verts_active, vidx_src)) { BLI_BITMAP_ENABLE(verts_active, loops_src[lidx_src].v); num_verts_active++; @@ -2418,8 +2418,7 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode, tri_vidx_2d[1][2] = 3; } else { - BLI_polyfill_calc( - poly_vcos_2d, (unsigned int)mp->totloop, -1, (unsigned int(*)[3])tri_vidx_2d); + BLI_polyfill_calc(poly_vcos_2d, (uint)mp->totloop, -1, (uint(*)[3])tri_vidx_2d); } for (j = 0; j < tris_num; j++) { diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c index 0a5aa360553..5a90d1f6ea5 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c @@ -67,20 +67,19 @@ struct OpenVDBLevelSet *BKE_mesh_remesh_voxel_ovdb_mesh_to_level_set_create( BKE_mesh_runtime_verttri_from_looptri( verttri, mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(mesh)); - unsigned int totfaces = BKE_mesh_runtime_looptri_len(mesh); - unsigned int totverts = mesh->totvert; + uint totfaces = BKE_mesh_runtime_looptri_len(mesh); + uint totverts = mesh->totvert; float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts"); - unsigned int *faces = (unsigned int *)MEM_malloc_arrayN( - totfaces * 3, sizeof(unsigned int), "remesh_input_faces"); + uint *faces = (uint *)MEM_malloc_arrayN(totfaces * 3, sizeof(uint), "remesh_input_faces"); - for (unsigned int i = 0; i < totverts; i++) { + for (uint i = 0; i < totverts; i++) { MVert *mvert = &mesh->mvert[i]; verts[i * 3] = mvert->co[0]; verts[i * 3 + 1] = mvert->co[1]; verts[i * 3 + 2] = mvert->co[2]; } - for (unsigned int i = 0; i < totfaces; i++) { + for (uint i = 0; i < totfaces; i++) { MVertTri *vt = &verttri[i]; faces[i * 3] = vt->tri[0]; faces[i * 3 + 1] = vt->tri[1]; @@ -171,20 +170,19 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh, BKE_mesh_runtime_verttri_from_looptri( verttri, input_mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(input_mesh)); - unsigned int totfaces = BKE_mesh_runtime_looptri_len(input_mesh); - unsigned int totverts = input_mesh->totvert; + uint totfaces = BKE_mesh_runtime_looptri_len(input_mesh); + uint totverts = input_mesh->totvert; float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts"); - unsigned int *faces = (unsigned int *)MEM_malloc_arrayN( - totfaces * 3, sizeof(unsigned int), "remesh_input_faces"); + uint *faces = (uint *)MEM_malloc_arrayN(totfaces * 3, sizeof(uint), "remesh_input_faces"); - for (unsigned int i = 0; i < totverts; i++) { + for (uint i = 0; i < totverts; i++) { MVert *mvert = &input_mesh->mvert[i]; verts[i * 3] = mvert->co[0]; verts[i * 3 + 1] = mvert->co[1]; verts[i * 3 + 2] = mvert->co[2]; } - for (unsigned int i = 0; i < totfaces; i++) { + for (uint i = 0; i < totfaces; i++) { MVertTri *vt = &verttri[i]; faces[i * 3] = vt->tri[0]; faces[i * 3 + 1] = vt->tri[1]; diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 22f29bef97e..011dd7e25ee 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -30,6 +30,7 @@ #include "DNA_object_types.h" #include "BLI_math_geom.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BKE_bvhutils.h" @@ -98,7 +99,7 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) */ static void mesh_ensure_looptri_data(Mesh *mesh) { - const unsigned int totpoly = mesh->totpoly; + const uint totpoly = mesh->totpoly; const int looptris_len = poly_to_tri_count(totpoly, mesh->totloop); BLI_assert(mesh->runtime.looptris.array_wip == NULL); @@ -151,6 +152,12 @@ int BKE_mesh_runtime_looptri_len(const Mesh *mesh) return looptri_len; } +static void mesh_runtime_looptri_recalc_isolated(void *userdata) +{ + Mesh *mesh = userdata; + BKE_mesh_runtime_looptri_recalc(mesh); +} + /* This is a ported copy of dm_getLoopTriArray(dm). */ const MLoopTri *BKE_mesh_runtime_looptri_ensure(Mesh *mesh) { @@ -163,7 +170,8 @@ const MLoopTri *BKE_mesh_runtime_looptri_ensure(Mesh *mesh) BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime.looptris.len); } else { - BKE_mesh_runtime_looptri_recalc(mesh); + /* Must isolate multithreaded tasks while holding a mutex lock. */ + BLI_task_isolate(mesh_runtime_looptri_recalc_isolated, mesh); looptri = mesh->runtime.looptris.array; } diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index 91c9951ae89..bd46407d060 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -14,6 +14,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" @@ -23,7 +24,7 @@ namespace blender::bke::mesh_surface_sample { -static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) +Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) { /* This only updates a cache and can be considered to be logically const. */ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); @@ -155,4 +156,117 @@ void sample_face_attribute(const Mesh &mesh, }); } +MeshAttributeInterpolator::MeshAttributeInterpolator(const Mesh *mesh, + const Span<float3> positions, + const Span<int> looptri_indices) + : mesh_(mesh), positions_(positions), looptri_indices_(looptri_indices) +{ + BLI_assert(positions.size() == looptri_indices.size()); +} + +Span<float3> MeshAttributeInterpolator::ensure_barycentric_coords() +{ + if (!bary_coords_.is_empty()) { + BLI_assert(bary_coords_.size() == positions_.size()); + return bary_coords_; + } + bary_coords_.reinitialize(positions_.size()); + + Span<MLoopTri> looptris = get_mesh_looptris(*mesh_); + + for (const int i : bary_coords_.index_range()) { + const int looptri_index = looptri_indices_[i]; + const MLoopTri &looptri = looptris[looptri_index]; + + const int v0_index = mesh_->mloop[looptri.tri[0]].v; + const int v1_index = mesh_->mloop[looptri.tri[1]].v; + const int v2_index = mesh_->mloop[looptri.tri[2]].v; + + interp_weights_tri_v3(bary_coords_[i], + mesh_->mvert[v0_index].co, + mesh_->mvert[v1_index].co, + mesh_->mvert[v2_index].co, + positions_[i]); + } + return bary_coords_; +} + +Span<float3> MeshAttributeInterpolator::ensure_nearest_weights() +{ + if (!nearest_weights_.is_empty()) { + BLI_assert(nearest_weights_.size() == positions_.size()); + return nearest_weights_; + } + nearest_weights_.reinitialize(positions_.size()); + + Span<MLoopTri> looptris = get_mesh_looptris(*mesh_); + + for (const int i : nearest_weights_.index_range()) { + const int looptri_index = looptri_indices_[i]; + const MLoopTri &looptri = looptris[looptri_index]; + + const int v0_index = mesh_->mloop[looptri.tri[0]].v; + const int v1_index = mesh_->mloop[looptri.tri[1]].v; + const int v2_index = mesh_->mloop[looptri.tri[2]].v; + + const float d0 = len_squared_v3v3(positions_[i], mesh_->mvert[v0_index].co); + const float d1 = len_squared_v3v3(positions_[i], mesh_->mvert[v1_index].co); + const float d2 = len_squared_v3v3(positions_[i], mesh_->mvert[v2_index].co); + + nearest_weights_[i] = MIN3_PAIR(d0, d1, d2, float3(1, 0, 0), float3(0, 1, 0), float3(0, 0, 1)); + } + return nearest_weights_; +} + +void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, + OutputAttribute &dst_attribute, + eAttributeMapMode mode) +{ + if (!src_attribute || !dst_attribute) { + return; + } + const GVArray &src_varray = *src_attribute.varray; + GMutableSpan dst_span = dst_attribute.as_span(); + if (src_varray.is_empty() || dst_span.is_empty()) { + return; + } + + /* Compute barycentric coordinates only when they are needed. */ + Span<float3> weights; + if (ELEM(src_attribute.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { + switch (mode) { + case eAttributeMapMode::INTERPOLATED: + weights = ensure_barycentric_coords(); + break; + case eAttributeMapMode::NEAREST: + weights = ensure_nearest_weights(); + break; + } + } + + /* Interpolate the source attributes on the surface. */ + switch (src_attribute.domain) { + case ATTR_DOMAIN_POINT: { + sample_point_attribute(*mesh_, looptri_indices_, weights, src_varray, dst_span); + break; + } + case ATTR_DOMAIN_FACE: { + sample_face_attribute(*mesh_, looptri_indices_, src_varray, dst_span); + break; + } + case ATTR_DOMAIN_CORNER: { + sample_corner_attribute(*mesh_, looptri_indices_, weights, src_varray, dst_span); + break; + } + case ATTR_DOMAIN_EDGE: { + /* Not yet supported. */ + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + } // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c index 6a7ff0851f5..2e22e521a13 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ b/source/blender/blenkernel/intern/mesh_tangent.c @@ -656,7 +656,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert, /* Calculation */ if (looptri_len != 0) { - TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + 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/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c new file mode 100644 index 00000000000..213f2929d63 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_tessellate.c @@ -0,0 +1,760 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bke + * + * This file contains code for polygon tessellation + * (creating triangles from polygons). + * + * \see bmesh_mesh_tessellate.c for the #BMesh equivalent of this file. + */ + +#include <limits.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" /* Own include. */ + +#include "BLI_strict_flags.h" + +/** Compared against total loops. */ +#define MESH_FACE_TESSELLATE_THREADED_LIMIT 4096 + +/* -------------------------------------------------------------------- */ +/** \name MFace Tessellation + * + * #MFace is a legacy data-structure that should be avoided, use #MLoopTri instead. + * \{ */ + +/** + * Convert all CD layers from loop/poly to tessface data. + * + * \param loopindices: is an array of an int[4] per tessface, + * mapping tessface's verts to loops indices. + * + * \note when mface is not NULL, mface[face_index].v4 + * is used to test quads, else, loopindices[face_index][3] is used. + */ +static void mesh_loops_to_tessdata(CustomData *fdata, + CustomData *ldata, + MFace *mface, + const int *polyindices, + uint (*loopindices)[4], + const int num_faces) +{ + /* NOTE(mont29): performances are sub-optimal when we get a NULL #MFace, + * we could be ~25% quicker with dedicated code. + * The issue is, unless having two different functions with nearly the same code, + * there's not much ways to solve this. Better IMHO to live with it for now (sigh). */ + const int numUV = CustomData_number_of_layers(ldata, CD_MLOOPUV); + const int numCol = CustomData_number_of_layers(ldata, CD_MLOOPCOL); + const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL); + const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP); + const bool hasLoopNormal = CustomData_has_layer(ldata, CD_NORMAL); + const bool hasLoopTangent = CustomData_has_layer(ldata, CD_TANGENT); + int findex, i, j; + const int *pidx; + uint(*lidx)[4]; + + for (i = 0; i < numUV; i++) { + MTFace *texface = CustomData_get_layer_n(fdata, CD_MTFACE, i); + MLoopUV *mloopuv = CustomData_get_layer_n(ldata, CD_MLOOPUV, i); + + for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; + pidx++, lidx++, findex++, texface++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + copy_v2_v2(texface->uv[j], mloopuv[(*lidx)[j]].uv); + } + } + } + + for (i = 0; i < numCol; i++) { + MCol(*mcol)[4] = CustomData_get_layer_n(fdata, CD_MCOL, i); + MLoopCol *mloopcol = CustomData_get_layer_n(ldata, CD_MLOOPCOL, i); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); + } + } + } + + if (hasPCol) { + MCol(*mcol)[4] = CustomData_get_layer(fdata, CD_PREVIEW_MCOL); + MLoopCol *mloopcol = CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); + } + } + } + + if (hasOrigSpace) { + OrigSpaceFace *of = CustomData_get_layer(fdata, CD_ORIGSPACE); + OrigSpaceLoop *lof = CustomData_get_layer(ldata, CD_ORIGSPACE_MLOOP); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + copy_v2_v2(of->uv[j], lof[(*lidx)[j]].uv); + } + } + } + + if (hasLoopNormal) { + short(*fnors)[4][3] = CustomData_get_layer(fdata, CD_TESSLOOPNORMAL); + float(*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, fnors++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + normal_float_to_short_v3((*fnors)[j], lnors[(*lidx)[j]]); + } + } + } + + if (hasLoopTangent) { + /* Need to do for all UV maps at some point. */ + float(*ftangents)[4] = CustomData_get_layer(fdata, CD_TANGENT); + float(*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); + + for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; + pidx++, lidx++, findex++) { + int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; + for (j = nverts; j--;) { + copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]); + } + } + } +} + +/** + * Recreate #MFace Tessellation. + * + * \param do_face_nor_copy: Controls whether the normals from the poly + * are copied to the tessellated faces. + * + * \return number of tessellation faces. + * + * \note This doesn't use multi-threading like #BKE_mesh_recalc_looptri since + * it's not used in many places and #MFace should be phased out. + */ +int BKE_mesh_tessface_calc_ex(CustomData *fdata, + CustomData *ldata, + CustomData *pdata, + MVert *mvert, + int totface, + int totloop, + int totpoly, + const bool do_face_nor_copy) +{ +#define USE_TESSFACE_SPEEDUP +#define USE_TESSFACE_QUADS + +/* We abuse #MFace.edcode to tag quad faces. See below for details. */ +#define TESSFACE_IS_QUAD 1 + + const int looptri_num = poly_to_tri_count(totpoly, totloop); + + MPoly *mp, *mpoly; + MLoop *ml, *mloop; + MFace *mface, *mf; + MemArena *arena = NULL; + int *mface_to_poly_map; + uint(*lindices)[4]; + int poly_index, mface_index; + uint j; + + mpoly = CustomData_get_layer(pdata, CD_MPOLY); + mloop = CustomData_get_layer(ldata, CD_MLOOP); + + /* Allocate the length of `totfaces`, avoid many small reallocation's, + * if all faces are triangles it will be correct, `quads == 2x` allocations. */ + /* Take care since memory is _not_ zeroed so be sure to initialize each field. */ + mface_to_poly_map = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface_to_poly_map), __func__); + mface = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface), __func__); + lindices = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*lindices), __func__); + + mface_index = 0; + mp = mpoly; + for (poly_index = 0; poly_index < totpoly; poly_index++, mp++) { + const uint mp_loopstart = (uint)mp->loopstart; + const uint mp_totloop = (uint)mp->totloop; + uint l1, l2, l3, l4; + uint *lidx; + if (mp_totloop < 3) { + /* Do nothing. */ + } + +#ifdef USE_TESSFACE_SPEEDUP + +# define ML_TO_MF(i1, i2, i3) \ + mface_to_poly_map[mface_index] = poly_index; \ + mf = &mface[mface_index]; \ + lidx = lindices[mface_index]; \ + /* Set loop indices, transformed to vert indices later. */ \ + l1 = mp_loopstart + i1; \ + l2 = mp_loopstart + i2; \ + l3 = mp_loopstart + i3; \ + mf->v1 = mloop[l1].v; \ + mf->v2 = mloop[l2].v; \ + mf->v3 = mloop[l3].v; \ + mf->v4 = 0; \ + lidx[0] = l1; \ + lidx[1] = l2; \ + lidx[2] = l3; \ + lidx[3] = 0; \ + mf->mat_nr = mp->mat_nr; \ + mf->flag = mp->flag; \ + mf->edcode = 0; \ + (void)0 + +/* ALMOST IDENTICAL TO DEFINE ABOVE (see EXCEPTION) */ +# define ML_TO_MF_QUAD() \ + mface_to_poly_map[mface_index] = poly_index; \ + mf = &mface[mface_index]; \ + lidx = lindices[mface_index]; \ + /* Set loop indices, transformed to vert indices later. */ \ + l1 = mp_loopstart + 0; /* EXCEPTION */ \ + l2 = mp_loopstart + 1; /* EXCEPTION */ \ + l3 = mp_loopstart + 2; /* EXCEPTION */ \ + l4 = mp_loopstart + 3; /* EXCEPTION */ \ + mf->v1 = mloop[l1].v; \ + mf->v2 = mloop[l2].v; \ + mf->v3 = mloop[l3].v; \ + mf->v4 = mloop[l4].v; \ + lidx[0] = l1; \ + lidx[1] = l2; \ + lidx[2] = l3; \ + lidx[3] = l4; \ + mf->mat_nr = mp->mat_nr; \ + mf->flag = mp->flag; \ + mf->edcode = TESSFACE_IS_QUAD; \ + (void)0 + + else if (mp_totloop == 3) { + ML_TO_MF(0, 1, 2); + mface_index++; + } + else if (mp_totloop == 4) { +# ifdef USE_TESSFACE_QUADS + ML_TO_MF_QUAD(); + mface_index++; +# else + ML_TO_MF(0, 1, 2); + mface_index++; + ML_TO_MF(0, 2, 3); + mface_index++; +# endif + } +#endif /* USE_TESSFACE_SPEEDUP */ + else { + const float *co_curr, *co_prev; + + float normal[3]; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const uint totfilltri = mp_totloop - 2; + + if (UNLIKELY(arena == NULL)) { + arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + tris = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)totfilltri); + projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)mp_totloop); + + zero_v3(normal); + + /* Calculate the normal, flipped: to get a positive 2D cross product. */ + ml = mloop + mp_loopstart; + co_prev = mvert[ml[mp_totloop - 1].v].co; + for (j = 0; j < mp_totloop; j++, ml++) { + co_curr = mvert[ml->v].co; + add_newell_cross_v3_v3v3(normal, co_prev, co_curr); + co_prev = co_curr; + } + if (UNLIKELY(normalize_v3(normal) == 0.0f)) { + normal[2] = 1.0f; + } + + /* Project verts to 2D. */ + axis_dominant_v3_to_m3_negate(axis_mat, normal); + + ml = mloop + mp_loopstart; + for (j = 0; j < mp_totloop; j++, ml++) { + mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); + } + + BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, arena); + + /* Apply fill. */ + for (j = 0; j < totfilltri; j++) { + uint *tri = tris[j]; + lidx = lindices[mface_index]; + + mface_to_poly_map[mface_index] = poly_index; + mf = &mface[mface_index]; + + /* Set loop indices, transformed to vert indices later. */ + l1 = mp_loopstart + tri[0]; + l2 = mp_loopstart + tri[1]; + l3 = mp_loopstart + tri[2]; + + mf->v1 = mloop[l1].v; + mf->v2 = mloop[l2].v; + mf->v3 = mloop[l3].v; + mf->v4 = 0; + + lidx[0] = l1; + lidx[1] = l2; + lidx[2] = l3; + lidx[3] = 0; + + mf->mat_nr = mp->mat_nr; + mf->flag = mp->flag; + mf->edcode = 0; + + mface_index++; + } + + BLI_memarena_clear(arena); + } + } + + if (arena) { + BLI_memarena_free(arena); + arena = NULL; + } + + CustomData_free(fdata, totface); + totface = mface_index; + + BLI_assert(totface <= looptri_num); + + /* Not essential but without this we store over-allocated memory in the #CustomData layers. */ + if (LIKELY(looptri_num != totface)) { + mface = MEM_reallocN(mface, sizeof(*mface) * (size_t)totface); + mface_to_poly_map = MEM_reallocN(mface_to_poly_map, + sizeof(*mface_to_poly_map) * (size_t)totface); + } + + CustomData_add_layer(fdata, CD_MFACE, CD_ASSIGN, mface, totface); + + /* #CD_ORIGINDEX will contain an array of indices from tessellation-faces to the polygons + * they are directly tessellated from. */ + CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, mface_to_poly_map, totface); + CustomData_from_bmeshpoly(fdata, ldata, totface); + + if (do_face_nor_copy) { + /* If polys have a normals layer, copying that to faces can help + * avoid the need to recalculate normals later. */ + if (CustomData_has_layer(pdata, CD_NORMAL)) { + float(*pnors)[3] = CustomData_get_layer(pdata, CD_NORMAL); + float(*fnors)[3] = CustomData_add_layer(fdata, CD_NORMAL, CD_CALLOC, NULL, totface); + for (mface_index = 0; mface_index < totface; mface_index++) { + copy_v3_v3(fnors[mface_index], pnors[mface_to_poly_map[mface_index]]); + } + } + } + + /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: + * Polygons take care of their loops ordering, hence not of their vertices ordering. + * Currently, our tfaces' fourth vertex index might be 0 even for a quad. + * However, we know our fourth loop index is never 0 for quads + * (because they are sorted for polygons, and our quads are still mere copies of their polygons). + * So we pass NULL as MFace pointer, and #mesh_loops_to_tessdata + * will use the fourth loop index as quad test. */ + mesh_loops_to_tessdata(fdata, ldata, NULL, mface_to_poly_map, lindices, totface); + + /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: + * ...However, most TFace code uses 'MFace->v4 == 0' test to check whether it is a tri or quad. + * BKE_mesh_mface_index_validate() will check this and rotate the tessellated face if needed. + */ +#ifdef USE_TESSFACE_QUADS + mf = mface; + for (mface_index = 0; mface_index < totface; mface_index++, mf++) { + if (mf->edcode == TESSFACE_IS_QUAD) { + BKE_mesh_mface_index_validate(mf, fdata, mface_index, 4); + mf->edcode = 0; + } + } +#endif + + MEM_freeN(lindices); + + return totface; + +#undef USE_TESSFACE_SPEEDUP +#undef USE_TESSFACE_QUADS + +#undef ML_TO_MF +#undef ML_TO_MF_QUAD +} + +void BKE_mesh_tessface_calc(Mesh *mesh) +{ + mesh->totface = BKE_mesh_tessface_calc_ex( + &mesh->fdata, + &mesh->ldata, + &mesh->pdata, + mesh->mvert, + mesh->totface, + mesh->totloop, + mesh->totpoly, + /* Calculate normals right after, don't copy from polys here. */ + false); + + BKE_mesh_update_customdata_pointers(mesh, true); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop Tessellation + * + * Fill in #MLoopTri data-structure. + * \{ */ + +/** + * \param face_normal: This will be optimized out as a constant. + */ +BLI_INLINE void mesh_calc_tessellation_for_face_impl(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p, + const bool face_normal, + const float normal_precalc[3]) +{ + const uint mp_loopstart = (uint)mpoly[poly_index].loopstart; + const uint mp_totloop = (uint)mpoly[poly_index].totloop; + +#define ML_TO_MLT(i1, i2, i3) \ + { \ + ARRAY_SET_ITEMS(mlt->tri, mp_loopstart + i1, mp_loopstart + i2, mp_loopstart + i3); \ + mlt->poly = poly_index; \ + } \ + ((void)0) + + switch (mp_totloop) { + case 3: { + ML_TO_MLT(0, 1, 2); + break; + } + case 4: { + ML_TO_MLT(0, 1, 2); + MLoopTri *mlt_a = mlt++; + ML_TO_MLT(0, 2, 3); + MLoopTri *mlt_b = mlt; + + if (UNLIKELY(face_normal ? is_quad_flip_v3_first_third_fast_with_normal( + /* Simpler calculation (using the normal). */ + mvert[mloop[mlt_a->tri[0]].v].co, + mvert[mloop[mlt_a->tri[1]].v].co, + mvert[mloop[mlt_a->tri[2]].v].co, + mvert[mloop[mlt_b->tri[2]].v].co, + normal_precalc) : + is_quad_flip_v3_first_third_fast( + /* Expensive calculation (no normal). */ + mvert[mloop[mlt_a->tri[0]].v].co, + mvert[mloop[mlt_a->tri[1]].v].co, + mvert[mloop[mlt_a->tri[2]].v].co, + mvert[mloop[mlt_b->tri[2]].v].co))) { + /* Flip out of degenerate 0-2 state. */ + mlt_a->tri[2] = mlt_b->tri[2]; + mlt_b->tri[0] = mlt_a->tri[1]; + } + break; + } + default: { + const MLoop *ml; + float axis_mat[3][3]; + + /* Calculate `axis_mat` to project verts to 2D. */ + if (face_normal == false) { + float normal[3]; + const float *co_curr, *co_prev; + + zero_v3(normal); + + /* Calc normal, flipped: to get a positive 2D cross product. */ + ml = mloop + mp_loopstart; + co_prev = mvert[ml[mp_totloop - 1].v].co; + for (uint j = 0; j < mp_totloop; j++, ml++) { + co_curr = mvert[ml->v].co; + add_newell_cross_v3_v3v3(normal, co_prev, co_curr); + co_prev = co_curr; + } + if (UNLIKELY(normalize_v3(normal) == 0.0f)) { + normal[2] = 1.0f; + } + axis_dominant_v3_to_m3_negate(axis_mat, normal); + } + else { + axis_dominant_v3_to_m3_negate(axis_mat, normal_precalc); + } + + const uint totfilltri = mp_totloop - 2; + + MemArena *pf_arena = *pf_arena_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + uint(*tris)[3] = tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * (size_t)totfilltri); + float(*projverts)[2] = projverts = BLI_memarena_alloc( + pf_arena, sizeof(*projverts) * (size_t)mp_totloop); + + ml = mloop + mp_loopstart; + for (uint j = 0; j < mp_totloop; j++, ml++) { + mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); + } + + BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, pf_arena); + + /* Apply fill. */ + for (uint j = 0; j < totfilltri; j++, mlt++) { + const uint *tri = tris[j]; + ML_TO_MLT(tri[0], tri[1], tri[2]); + } + + BLI_memarena_clear(pf_arena); + + break; + } + } +#undef ML_TO_MLT +} + +static void mesh_calc_tessellation_for_face(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p) +{ + mesh_calc_tessellation_for_face_impl( + mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, false, NULL); +} + +static void mesh_calc_tessellation_for_face_with_normal(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p, + const float normal_precalc[3]) +{ + mesh_calc_tessellation_for_face_impl( + mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, true, normal_precalc); +} + +static void mesh_recalc_looptri__single_threaded(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + MemArena *pf_arena = NULL; + const MPoly *mp = mpoly; + uint tri_index = 0; + + if (poly_normals != NULL) { + for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) { + mesh_calc_tessellation_for_face_with_normal(mloop, + mpoly, + mvert, + poly_index, + &mlooptri[tri_index], + &pf_arena, + poly_normals[poly_index]); + tri_index += (uint)(mp->totloop - 2); + } + } + else { + for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) { + mesh_calc_tessellation_for_face( + mloop, mpoly, mvert, poly_index, &mlooptri[tri_index], &pf_arena); + tri_index += (uint)(mp->totloop - 2); + } + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + pf_arena = NULL; + } + BLI_assert(tri_index == (uint)poly_to_tri_count(totpoly, totloop)); + UNUSED_VARS_NDEBUG(totloop); +} + +struct TessellationUserData { + const MLoop *mloop; + const MPoly *mpoly; + const MVert *mvert; + + /** Output array. */ + MLoopTri *mlooptri; + + /** Optional pre-calculated polygon normals array. */ + const float (*poly_normals)[3]; +}; + +struct TessellationUserTLS { + MemArena *pf_arena; +}; + +static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + const struct TessellationUserData *data = userdata; + struct TessellationUserTLS *tls_data = tls->userdata_chunk; + const int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart); + mesh_calc_tessellation_for_face_impl(data->mloop, + data->mpoly, + data->mvert, + (uint)index, + &data->mlooptri[tri_index], + &tls_data->pf_arena, + false, + NULL); +} + +static void mesh_calc_tessellation_for_face_with_normal_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + const struct TessellationUserData *data = userdata; + struct TessellationUserTLS *tls_data = tls->userdata_chunk; + const int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart); + mesh_calc_tessellation_for_face_impl(data->mloop, + data->mpoly, + data->mvert, + (uint)index, + &data->mlooptri[tri_index], + &tls_data->pf_arena, + true, + data->poly_normals[index]); +} + +static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata), + void *__restrict tls_v) +{ + struct TessellationUserTLS *tls_data = tls_v; + if (tls_data->pf_arena) { + BLI_memarena_free(tls_data->pf_arena); + } +} + +static void mesh_recalc_looptri__multi_threaded(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int UNUSED(totloop), + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + struct TessellationUserTLS tls_data_dummy = {NULL}; + + struct TessellationUserData data = { + .mloop = mloop, + .mpoly = mpoly, + .mvert = mvert, + .mlooptri = mlooptri, + .poly_normals = poly_normals, + }; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + + settings.userdata_chunk = &tls_data_dummy; + settings.userdata_chunk_size = sizeof(tls_data_dummy); + + settings.func_free = mesh_calc_tessellation_for_face_free_fn; + + BLI_task_parallel_range(0, + totpoly, + &data, + poly_normals ? mesh_calc_tessellation_for_face_with_normal_fn : + mesh_calc_tessellation_for_face_fn, + &settings); +} + +/** + * Calculate tessellation into #MLoopTri which exist only for this purpose. + */ +void BKE_mesh_recalc_looptri(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri) +{ + if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { + mesh_recalc_looptri__single_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL); + } + else { + mesh_recalc_looptri__multi_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL); + } +} + +/** + * A version of #BKE_mesh_recalc_looptri which takes pre-calculated polygon normals + * (used to avoid having to calculate the face normal for NGON tessellation). + * + * \note Only use this function if normals have already been calculated, there is no need + * to calculate normals just to use this function as it will cause the normals for triangles + * to be calculated which aren't needed for tessellation. + */ +void BKE_mesh_recalc_looptri_with_normals(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + BLI_assert(poly_normals != NULL); + if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { + mesh_recalc_looptri__single_threaded( + mloop, mpoly, mvert, totloop, totpoly, mlooptri, poly_normals); + } + else { + mesh_recalc_looptri__multi_threaded( + mloop, mpoly, mvert, totloop, totpoly, mlooptri, poly_normals); + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index b3c53df2d5f..df84cf6607f 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -63,7 +63,7 @@ typedef union { typedef struct SortFace { EdgeUUID es[4]; - unsigned int index; + uint index; } SortFace; /* Used to detect polys (faces) using exactly the same vertices. */ @@ -72,7 +72,7 @@ typedef struct SortPoly { int *verts; int numverts; int loopstart; - unsigned int index; + uint index; bool invalid; /* Poly index. */ } SortPoly; @@ -217,19 +217,29 @@ static int search_polyloop_cmp(const void *v1, const void *v2) * Validate the mesh, \a do_fixes requires \a mesh to be non-null. * * \return false if no changes needed to be made. + * + * Vertex Normals + * ============== + * + * While zeroed normals are checked, these checks aren't comprehensive. + * Technically, to detect errors here a normal recalculation and comparison is necessary. + * However this function is mainly to prevent severe errors in geometry + * (invalid data that will crash Blender, or cause some features to behave incorrectly), + * not to detect subtle differences in the resulting normals which could be caused + * by importers that load normals (for example). */ /* NOLINTNEXTLINE: readability-function-size */ bool BKE_mesh_validate_arrays(Mesh *mesh, MVert *mverts, - unsigned int totvert, + uint totvert, MEdge *medges, - unsigned int totedge, + uint totedge, MFace *mfaces, - unsigned int totface, + uint totface, MLoop *mloops, - unsigned int totloop, + uint totloop, MPoly *mpolys, - unsigned int totpoly, + uint totpoly, MDeformVert *dverts, /* assume totvert length */ const bool do_verbose, const bool do_fixes, @@ -260,7 +270,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, MEdge *me; MLoop *ml; MPoly *mp; - unsigned int i, j; + uint i, j; int *v; bool is_valid = true; @@ -328,10 +338,21 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, } if (fix_normal) { - PRINT_ERR("\tVertex %u: has zero normal, assuming Z-up normal", i); - if (do_fixes) { - mv->no[2] = SHRT_MAX; - fix_flag.verts = true; + /* If the vertex normal accumulates to zero or isn't part of a face, the location is used. + * When the location is also zero, a zero normal warning should not be raised. + * since this is the expected behavior of normal calculation. + * + * This avoids false positives but isn't foolproof as it's possible the vertex + * is part of a polygon that has a normal which this vertex should be using, + * although it's also possible degenerate/opposite faces accumulate to a zero vector. + * To detect this a full normal recalculation would be needed, which is out of scope + * for a basic validity check (see "Vertex Normal" in the doc-string). */ + if (!is_zero_v3(mv->co)) { + PRINT_ERR("\tVertex %u: has zero normal, assuming Z-up normal", i); + if (do_fixes) { + mv->no[2] = SHRT_MAX; + fix_flag.verts = true; + } } } } @@ -398,14 +419,14 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, SortFace *sort_faces = MEM_callocN(sizeof(SortFace) * totface, "search faces"); SortFace *sf; SortFace *sf_prev; - unsigned int totsortface = 0; + uint totsortface = 0; PRINT_ERR("No Polys, only tessellated Faces"); for (i = 0, mf = mfaces, sf = sort_faces; i < totface; i++, mf++) { bool remove = false; int fidx; - unsigned int fv[4]; + uint fv[4]; fidx = mf->v4 ? 3 : 2; do { @@ -815,7 +836,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, } /* Not technically incorrect since this is unsigned, however, - * a value over INT_MAX is almost certainly caused by wrapping an unsigned int. */ + * a value over INT_MAX is almost certainly caused by wrapping an uint. */ if (dw->def_nr >= INT_MAX) { PRINT_ERR("\tVertex deform %u, has invalid group %u", i, dw->def_nr); if (do_fixes) { @@ -1279,7 +1300,7 @@ void BKE_mesh_strip_loose_edges(Mesh *me) MEdge *e; MLoop *l; int a, b; - unsigned int *new_idx = MEM_mallocN(sizeof(int) * me->totedge, __func__); + uint *new_idx = MEM_mallocN(sizeof(int) * me->totedge, __func__); for (a = b = 0, e = me->medge; a < me->totedge; a++, e++) { if (e->v1 != e->v2) { @@ -1317,13 +1338,12 @@ void BKE_mesh_strip_loose_edges(Mesh *me) /* make edges in a Mesh, for outside of editmode */ struct EdgeSort { - unsigned int v1, v2; + uint v1, v2; char is_loose, is_draw; }; /* edges have to be added with lowest index first for sorting */ -static void to_edgesort( - struct EdgeSort *ed, unsigned int v1, unsigned int v2, char is_loose, short is_draw) +static void to_edgesort(struct EdgeSort *ed, uint v1, uint v2, char is_loose, short is_draw) { if (v1 < v2) { ed->v1 = v1; @@ -1378,8 +1398,8 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), EdgeHash *hash; struct EdgeSort *edsort, *ed; int a, totedge = 0; - unsigned int totedge_final = 0; - unsigned int edge_index; + uint totedge_final = 0; + uint edge_index; /* we put all edges in array, sort them, and detect doubles that way */ @@ -1442,10 +1462,10 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), med->flag |= ME_LOOSEEDGE; } - /* order is swapped so extruding this edge as a surface wont flip face normals + /* order is swapped so extruding this edge as a surface won't flip face normals * with cyclic curves */ if (ed->v1 + 1 != ed->v2) { - SWAP(unsigned int, med->v1, med->v2); + SWAP(uint, med->v1, med->v2); } med++; } diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index 0b0aeb50d37..574ab785445 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -87,7 +87,7 @@ static void reserve_hash_maps(const Mesh *mesh, MutableSpan<EdgeMap> edge_maps) { const int totedge_guess = std::max(keep_existing_edges ? mesh->totedge : 0, mesh->totpoly * 2); - parallel_for_each( + threading::parallel_for_each( edge_maps, [&](EdgeMap &edge_map) { edge_map.reserve(totedge_guess / edge_maps.size()); }); } @@ -96,7 +96,7 @@ static void add_existing_edges_to_hash_maps(Mesh *mesh, uint32_t parallel_mask) { /* Assume existing edges are valid. */ - parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { + threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { const int task_index = &edge_map - &edge_maps[0]; for (const MEdge &edge : Span(mesh->medge, mesh->totedge)) { OrderedEdge ordered_edge{edge.v1, edge.v2}; @@ -113,7 +113,7 @@ static void add_polygon_edges_to_hash_maps(Mesh *mesh, uint32_t parallel_mask) { const Span<MLoop> loops{mesh->mloop, mesh->totloop}; - parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { + threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { const int task_index = &edge_map - &edge_maps[0]; for (const MPoly &poly : Span(mesh->mpoly, mesh->totpoly)) { Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); @@ -146,7 +146,7 @@ static void serialize_and_initialize_deduplicated_edges(MutableSpan<EdgeMap> edg edge_index_offsets[i + 1] = edge_index_offsets[i] + edge_maps[i].size(); } - parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { + threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { const int task_index = &edge_map - &edge_maps[0]; int new_edge_index = edge_index_offsets[task_index]; @@ -174,7 +174,7 @@ static void update_edge_indices_in_poly_loops(Mesh *mesh, uint32_t parallel_mask) { const MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - parallel_for(IndexRange(mesh->totpoly), 100, [&](IndexRange range) { + threading::parallel_for(IndexRange(mesh->totpoly), 100, [&](IndexRange range) { for (const int poly_index : range) { MPoly &poly = mesh->mpoly[poly_index]; MutableSpan<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); @@ -215,7 +215,7 @@ static int get_parallel_maps_count(const Mesh *mesh) static void clear_hash_tables(MutableSpan<EdgeMap> edge_maps) { - parallel_for_each(edge_maps, [](EdgeMap &edge_map) { edge_map.clear(); }); + threading::parallel_for_each(edge_maps, [](EdgeMap &edge_map) { edge_map.clear(); }); } } // namespace blender::bke::calc_edges diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c index 5df9f7816e3..fe6af432314 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.c +++ b/source/blender/blenkernel/intern/mesh_wrapper.c @@ -40,6 +40,7 @@ #include "BLI_ghash.h" #include "BLI_math.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -58,7 +59,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, const Mesh *me_settings) { Mesh *me = BKE_id_new_nomain(ID_ME, NULL); - BKE_mesh_copy_settings(me, me_settings); + BKE_mesh_copy_parameters_for_eval(me, me_settings); BKE_mesh_runtime_ensure_edit_data(me); me->runtime.wrapper_type = ME_WRAPPER_TYPE_BMESH; @@ -95,15 +96,9 @@ Mesh *BKE_mesh_wrapper_from_editmesh(BMEditMesh *em, return BKE_mesh_wrapper_from_editmesh_with_coords(em, cd_mask_extra, NULL, me_settings); } -void BKE_mesh_wrapper_ensure_mdata(Mesh *me) +static void mesh_wrapper_ensure_mdata_isolated(void *userdata) { - ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex; - BLI_mutex_lock(mesh_eval_mutex); - - if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { - BLI_mutex_unlock(mesh_eval_mutex); - return; - } + Mesh *me = userdata; const eMeshWrapperType geom_type_orig = me->runtime.wrapper_type; me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; @@ -136,6 +131,20 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) if (me->runtime.wrapper_type_finalize) { BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra); } +} + +void BKE_mesh_wrapper_ensure_mdata(Mesh *me) +{ + ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex; + BLI_mutex_lock(mesh_eval_mutex); + + if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { + BLI_mutex_unlock(mesh_eval_mutex); + return; + } + + /* Must isolate multithreaded tasks while holding a mutex lock. */ + BLI_task_isolate(mesh_wrapper_ensure_mdata_isolated, me); BLI_mutex_unlock(mesh_eval_mutex); } diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 3b67237f5eb..e60f0102b9a 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -729,7 +729,6 @@ Object *BKE_modifiers_is_deformed_by_armature(Object *ob) ArmatureGpencilModifierData *agmd = NULL; GpencilModifierData *gmd = BKE_gpencil_modifiers_get_virtual_modifierlist( ob, &gpencilvirtualModifierData); - gmd = ob->greasepencil_modifiers.first; /* return the first selected armature, this lets us use multiple armatures */ for (; gmd; gmd = gmd->next) { @@ -749,7 +748,6 @@ Object *BKE_modifiers_is_deformed_by_armature(Object *ob) VirtualModifierData virtualModifierData; ArmatureModifierData *amd = NULL; ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); - md = ob->modifiers.first; /* return the first selected armature, this lets us use multiple armatures */ for (; md; md = md->next) { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index a3eb55d8c4a..4e0e784a5b8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -3342,8 +3342,6 @@ static bNodeSocket *make_socket_interface(bNodeTree *ntree, const char *name) { bNodeSocketType *stype = nodeSocketTypeFind(idname); - int own_index = ntree->cur_index++; - if (stype == nullptr) { return nullptr; } @@ -3355,7 +3353,7 @@ static bNodeSocket *make_socket_interface(bNodeTree *ntree, sock->type = SOCK_CUSTOM; /* int type undefined by default */ /* assign new unique index */ - own_index = ntree->cur_index++; + const int own_index = ntree->cur_index++; /* use the own_index as socket identifier */ if (in_out == SOCK_IN) { BLI_snprintf(sock->identifier, MAX_NAME, "Input_%d", own_index); @@ -5058,7 +5056,10 @@ static void registerGeometryNodes() register_node_type_geo_curve_deform(); register_node_type_geo_curve_length(); register_node_type_geo_curve_to_mesh(); + register_node_type_geo_curve_to_points(); register_node_type_geo_curve_resample(); + register_node_type_geo_curve_reverse(); + register_node_type_geo_curve_subdivide(); register_node_type_geo_delete_geometry(); register_node_type_geo_edge_split(); register_node_type_geo_input_material(); @@ -5083,8 +5084,10 @@ static void registerGeometryNodes() register_node_type_geo_point_separate(); register_node_type_geo_point_translate(); register_node_type_geo_points_to_volume(); + register_node_type_geo_raycast(); register_node_type_geo_sample_texture(); register_node_type_geo_select_by_material(); + register_node_type_geo_separate_components(); register_node_type_geo_subdivide(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index b73f6a5b78c..8cfd75b015c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -957,8 +957,8 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) ob, eModifierType_Fluidsim); if (fluidmd && fluidmd->fss) { - BLO_read_id_address( - reader, ob->id.lib, &fluidmd->fss->ipo); /* XXX deprecated - old animation system */ + /* XXX: deprecated - old animation system. */ + BLO_read_id_address(reader, ob->id.lib, &fluidmd->fss->ipo); } } @@ -2890,7 +2890,7 @@ void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob) /* copy transform * - cob means this proxy comes from a collection, just apply the matrix - * so the object wont move from its dupli-transform. + * so the object won't move from its dupli-transform. * * - no cob means this is being made from a linked object, * this is closer to making a copy of the object - in-place. */ @@ -4447,7 +4447,7 @@ bool BKE_object_obdata_texspace_get(Object *ob, short **r_texflag, float **r_loc } /** Get evaluated mesh for given object. */ -Mesh *BKE_object_get_evaluated_mesh(Object *object) +Mesh *BKE_object_get_evaluated_mesh(const Object *object) { ID *data_eval = object->runtime.data_eval; return (data_eval && GS(data_eval->name) == ID_ME) ? (Mesh *)data_eval : NULL; @@ -4460,7 +4460,7 @@ Mesh *BKE_object_get_evaluated_mesh(Object *object) * - For copied-on-write objects it will give pointer to a copied-on-write * mesh which corresponds to original object's mesh. */ -Mesh *BKE_object_get_pre_modified_mesh(Object *object) +Mesh *BKE_object_get_pre_modified_mesh(const Object *object) { if (object->type == OB_MESH && object->runtime.data_orig != NULL) { BLI_assert(object->id.tag & LIB_TAG_COPIED_ON_WRITE); @@ -4481,7 +4481,7 @@ Mesh *BKE_object_get_pre_modified_mesh(Object *object) * - For evaluated objects it will be same mesh as corresponding original * object uses as data. */ -Mesh *BKE_object_get_original_mesh(Object *object) +Mesh *BKE_object_get_original_mesh(const Object *object) { Mesh *result = NULL; if (object->id.orig_id == NULL) { diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index e6909127503..b1afd968bdc 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -349,38 +349,45 @@ void BKE_object_eval_uber_transform(Depsgraph *depsgraph, Object *object) BKE_object_eval_proxy_copy(depsgraph, object); } -void BKE_object_batch_cache_dirty_tag(Object *ob) +void BKE_object_data_batch_cache_dirty_tag(ID *object_data) { - switch (ob->type) { - case OB_MESH: - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + switch (GS(object_data->name)) { + case ID_ME: + BKE_mesh_batch_cache_dirty_tag((struct Mesh *)object_data, BKE_MESH_BATCH_DIRTY_ALL); break; - case OB_LATTICE: - BKE_lattice_batch_cache_dirty_tag(ob->data, BKE_LATTICE_BATCH_DIRTY_ALL); + case ID_LT: + BKE_lattice_batch_cache_dirty_tag((struct Lattice *)object_data, + BKE_LATTICE_BATCH_DIRTY_ALL); break; - case OB_CURVE: - case OB_FONT: - case OB_SURF: - BKE_curve_batch_cache_dirty_tag(ob->data, BKE_CURVE_BATCH_DIRTY_ALL); + case ID_CU: + BKE_curve_batch_cache_dirty_tag((struct Curve *)object_data, BKE_CURVE_BATCH_DIRTY_ALL); break; - case OB_MBALL: - BKE_mball_batch_cache_dirty_tag(ob->data, BKE_MBALL_BATCH_DIRTY_ALL); + case ID_MB: + BKE_mball_batch_cache_dirty_tag((struct MetaBall *)object_data, BKE_MBALL_BATCH_DIRTY_ALL); break; - case OB_GPENCIL: - BKE_gpencil_batch_cache_dirty_tag(ob->data); + case ID_GD: + BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)object_data); break; - case OB_HAIR: - BKE_hair_batch_cache_dirty_tag(ob->data, BKE_HAIR_BATCH_DIRTY_ALL); + case ID_HA: + BKE_hair_batch_cache_dirty_tag((struct Hair *)object_data, BKE_HAIR_BATCH_DIRTY_ALL); break; - case OB_POINTCLOUD: - BKE_pointcloud_batch_cache_dirty_tag(ob->data, BKE_POINTCLOUD_BATCH_DIRTY_ALL); + case ID_PT: + BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)object_data, + BKE_POINTCLOUD_BATCH_DIRTY_ALL); break; - case OB_VOLUME: - BKE_volume_batch_cache_dirty_tag(ob->data, BKE_VOLUME_BATCH_DIRTY_ALL); + case ID_VO: + BKE_volume_batch_cache_dirty_tag((struct Volume *)object_data, BKE_VOLUME_BATCH_DIRTY_ALL); + break; + default: break; } } +void BKE_object_batch_cache_dirty_tag(Object *ob) +{ + BKE_object_data_batch_cache_dirty_tag(ob->data); +} + void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob) { DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob); diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 9b9ed0adcf4..9d53dad8d03 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -663,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(&osd, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + 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/particle.c b/source/blender/blenkernel/intern/particle.c index 3ae5d039125..021121034f1 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -384,8 +384,9 @@ void BKE_particle_partdeflect_blend_read_lib(BlendLibReader *reader, ID *id, Par static void particle_settings_blend_read_lib(BlendLibReader *reader, ID *id) { ParticleSettings *part = (ParticleSettings *)id; - BLO_read_id_address( - reader, part->id.lib, &part->ipo); /* XXX deprecated - old animation system */ + + /* XXX: deprecated - old animation system. */ + BLO_read_id_address(reader, part->id.lib, &part->ipo); BLO_read_id_address(reader, part->id.lib, &part->instance_object); BLO_read_id_address(reader, part->id.lib, &part->instance_collection); @@ -1437,7 +1438,7 @@ static void do_particle_interpolation(ParticleSystem *psys, int point_vel = (point && point->keys->vel); float real_t, dfra, keytime, invdt = 1.0f; - /* billboards wont fill in all of these, so start cleared */ + /* billboards won't fill in all of these, so start cleared */ memset(keys, 0, sizeof(keys)); /* interpret timing and find keys */ @@ -3179,7 +3180,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim, return; } - task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW); totchild = ctx.totchild; totparent = ctx.totparent; diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 6cae6cd6fa2..13f0cb28428 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -481,7 +481,7 @@ static void distribute_from_verts_exec(ParticleTask *thread, ParticleData *pa, i mface = ctx->mesh->mface; - int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ + int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls won't need skipping */ /* TODO_PARTICLE - use original index */ pa->num = ctx->index[p]; @@ -538,7 +538,7 @@ static void distribute_from_faces_exec(ParticleTask *thread, ParticleData *pa, i float randu, randv; int distr = ctx->distr; int i; - int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ + int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls won't need skipping */ MFace *mface; @@ -587,7 +587,7 @@ static void distribute_from_volume_exec(ParticleTask *thread, ParticleData *pa, float cur_d, min_d, randu, randv; int distr = ctx->distr; int i, intersect, tot; - int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ + int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls won't need skipping */ MFace *mface; MVert *mvert = mesh->mvert; @@ -692,7 +692,7 @@ static void distribute_children_exec(ParticleTask *thread, ChildParticle *cpa, i float randu, randv; int cfrom = ctx->cfrom; int i; - int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ + int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls won't need skipping */ MFace *mf; @@ -1330,7 +1330,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) return; } - task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + 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/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index ce4be411c9a..d236dbbf101 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -1007,7 +1007,7 @@ void psys_get_birth_coords( mul_qt_v3(q_imat, rot_vec_local); /* vtan_local */ - copy_v3_v3(vtan_local, vtan); /* flips, cant use */ + copy_v3_v3(vtan_local, vtan); /* flips, can't use */ mul_qt_v3(q_imat, vtan_local); /* ensure orthogonal matrix (rot_vec aligned) */ @@ -1320,6 +1320,14 @@ void psys_get_pointcache_start_end(Scene *scene, ParticleSystem *psys, int *sfra *efra = min_ii((int)(part->end + part->lifetime + 1.0f), max_ii(scene->r.pefra, scene->r.efra)); } +/* BVH tree balancing inside a mutex lock must be run in isolation. Balancing + * is multithreaded, and we do not want the current thread to start another task + * that may involve acquiring the same mutex lock that it is waiting for. */ +static void bvhtree_balance_isolated(void *userdata) +{ + BLI_bvhtree_balance((BVHTree *)userdata); +} + /************************************************/ /* Effectors */ /************************************************/ @@ -1356,7 +1364,8 @@ static void psys_update_particle_bvhtree(ParticleSystem *psys, float cfra) } } } - BLI_bvhtree_balance(psys->bvhtree); + + BLI_task_isolate(bvhtree_balance_isolated, psys->bvhtree); psys->bvhtree_frame = cfra; @@ -3312,13 +3321,11 @@ static MDeformVert *hair_set_pinning(MDeformVert *dvert, float weight) static void hair_create_input_mesh(ParticleSimulationData *sim, int totpoint, int totedge, - Mesh **r_mesh, - ClothHairData **r_hairdata) + Mesh **r_mesh) { ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; Mesh *mesh; - ClothHairData *hairdata; MVert *mvert; MEdge *medge; MDeformVert *dvert; @@ -3339,9 +3346,8 @@ static void hair_create_input_mesh(ParticleSimulationData *sim, medge = mesh->medge; dvert = mesh->dvert; - hairdata = *r_hairdata; - if (!hairdata) { - *r_hairdata = hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data"); + if (psys->clmd->hairdata == NULL) { + psys->clmd->hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data"); } /* calculate maximum segment length */ @@ -3493,7 +3499,7 @@ static void do_hair_dynamics(ParticleSimulationData *sim) } } - hair_create_input_mesh(sim, totpoint, totedge, &psys->hair_in_mesh, &psys->clmd->hairdata); + hair_create_input_mesh(sim, totpoint, totedge, &psys->hair_in_mesh); if (psys->hair_out_mesh) { BKE_id_free(NULL, psys->hair_out_mesh); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 77dde3a921a..9f316ec60c0 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -2056,7 +2056,7 @@ bool ray_face_intersection_tri(const float ray_start[3], return false; } -/* Take advantage of the fact we know this wont be an intersection. +/* Take advantage of the fact we know this won't be an intersection. * Just handle ray-tri edges. */ static float dist_squared_ray_to_tri_v3_fast(const float ray_origin[3], const float ray_direction[3], diff --git a/source/blender/blenkernel/intern/report.c b/source/blender/blenkernel/intern/report.c index 43ab1a71647..c877ec6b6b0 100644 --- a/source/blender/blenkernel/intern/report.c +++ b/source/blender/blenkernel/intern/report.c @@ -107,7 +107,7 @@ void BKE_report(ReportList *reports, ReportType type, const char *_message) int len; const char *message = TIP_(_message); - /* in background mode always print otherwise there are cases the errors wont be displayed, + /* in background mode always print otherwise there are cases the errors won't be displayed, * but still add to the report list since this is used for python exception handling */ if (G.background || !reports || ((reports->flag & RPT_PRINT) && (type >= reports->printlevel))) { printf("%s: %s\n", BKE_report_type_str(type), message); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 86d4c03d51a..41f70db3fba 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1145,6 +1145,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &ed->act_seq); ed->cache = NULL; ed->prefetch_job = NULL; + ed->runtime.sequence_lookup = NULL; /* recursive link sequences, lb will be correctly initialized */ link_recurs_seq(reader, &ed->seqbase); @@ -1465,8 +1466,8 @@ static void scene_blend_read_lib(BlendLibReader *reader, ID *id) IDP_BlendReadLib(reader, seq->prop); if (seq->ipo) { - BLO_read_id_address( - reader, sce->id.lib, &seq->ipo); /* XXX deprecated - old animation system */ + /* XXX: deprecated - old animation system. */ + BLO_read_id_address(reader, sce->id.lib, &seq->ipo); } seq->scene_sound = NULL; if (seq->scene) { diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index d0d63192ebf..7a5892baaf6 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -467,7 +467,7 @@ static void panel_list_copy(ListBase *newlb, const ListBase *lb) Panel *panel = lb->first; for (; new_panel; new_panel = new_panel->next, panel = panel->next) { new_panel->activedata = NULL; - new_panel->runtime.custom_data_ptr = NULL; + memset(&new_panel->runtime, 0x0, sizeof(new_panel->runtime)); panel_list_copy(&new_panel->children, &panel->children); } } @@ -476,6 +476,8 @@ ARegion *BKE_area_region_copy(const SpaceType *st, const ARegion *region) { ARegion *newar = MEM_dupallocN(region); + memset(&newar->runtime, 0x0, sizeof(newar->runtime)); + newar->prev = newar->next = NULL; BLI_listbase_clear(&newar->handlers); BLI_listbase_clear(&newar->uiblocks); @@ -1355,12 +1357,21 @@ static void write_area(BlendWriter *writer, ScrArea *area) } else if (sl->spacetype == SPACE_SPREADSHEET) { BLO_write_struct(writer, SpaceSpreadsheet, sl); - SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; + + LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) { + BLO_write_struct(writer, SpreadsheetRowFilter, row_filter); + BLO_write_string(writer, row_filter->value_string); + } + LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) { BLO_write_struct(writer, SpreadsheetColumn, column); BLO_write_struct(writer, SpreadsheetColumnID, column->id); BLO_write_string(writer, column->id->name); + /* While the display name is technically runtime data, we write it here, otherwise the row + * filters might not now their type if their region draws before the main region. + * This would ideally be cleared here. */ + BLO_write_string(writer, column->display_name); } LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { switch (context->type) { @@ -1419,6 +1430,8 @@ static void direct_link_panel_list(BlendDataReader *reader, ListBase *lb) static void direct_link_region(BlendDataReader *reader, ARegion *region, int spacetype) { + memset(®ion->runtime, 0x0, sizeof(region->runtime)); + direct_link_panel_list(reader, ®ion->panels); BLO_read_list(reader, ®ion->panels_category_active); @@ -1454,7 +1467,6 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa BLO_read_data_address(reader, &rv3d->localvd); BLO_read_data_address(reader, &rv3d->clipbb); - rv3d->depths = NULL; rv3d->render_engine = NULL; rv3d->sms = NULL; rv3d->smooth_timer = NULL; @@ -1560,16 +1572,15 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) if (sl->spacetype == SPACE_VIEW3D) { View3D *v3d = (View3D *)sl; + + memset(&v3d->runtime, 0x0, sizeof(v3d->runtime)); + if (v3d->gpd) { BLO_read_data_address(reader, &v3d->gpd); BKE_gpencil_blend_read_data(reader, v3d->gpd); } BLO_read_data_address(reader, &v3d->localvd); - /* Runtime data */ - v3d->runtime.properties_storage = NULL; - v3d->runtime.flag = 0; - /* render can be quite heavy, set to solid on load */ if (v3d->shading.type == OB_RENDER) { v3d->shading.type = OB_SOLID; @@ -1584,7 +1595,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) SpaceGraph *sipo = (SpaceGraph *)sl; BLO_read_data_address(reader, &sipo->ads); - BLI_listbase_clear(&sipo->runtime.ghost_curves); + memset(&sipo->runtime, 0x0, sizeof(sipo->runtime)); } else if (sl->spacetype == SPACE_NLA) { SpaceNla *snla = (SpaceNla *)sl; @@ -1652,7 +1663,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) } else if (sl->spacetype == SPACE_TEXT) { SpaceText *st = (SpaceText *)sl; - memset(&st->runtime, 0, sizeof(st->runtime)); + memset(&st->runtime, 0x0, sizeof(st->runtime)); } else if (sl->spacetype == SPACE_SEQ) { SpaceSeq *sseq = (SpaceSeq *)sl; @@ -1724,6 +1735,11 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) BLO_read_data_address(reader, &sfile->params); BLO_read_data_address(reader, &sfile->asset_params); } + else if (sl->spacetype == SPACE_ACTION) { + SpaceAction *saction = (SpaceAction *)sl; + + memset(&saction->runtime, 0x0, sizeof(saction->runtime)); + } else if (sl->spacetype == SPACE_CLIP) { SpaceClip *sclip = (SpaceClip *)sl; @@ -1735,11 +1751,18 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; sspreadsheet->runtime = NULL; - + BLO_read_list(reader, &sspreadsheet->row_filters); + LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) { + BLO_read_data_address(reader, &row_filter->value_string); + } BLO_read_list(reader, &sspreadsheet->columns); LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) { BLO_read_data_address(reader, &column->id); BLO_read_data_address(reader, &column->id->name); + /* While the display name is technically runtime data, it is loaded here, otherwise the row + * filters might not now their type if their region draws before the main region. + * This would ideally be cleared here. */ + BLO_read_data_address(reader, &column->display_name); } BLO_read_list(reader, &sspreadsheet->context_path); diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c index 3727ec866ca..60f0b744e59 100644 --- a/source/blender/blenkernel/intern/shader_fx.c +++ b/source/blender/blenkernel/intern/shader_fx.c @@ -165,6 +165,18 @@ const ShaderFxTypeInfo *BKE_shaderfx_get_info(ShaderFxType type) } /** + * Check whether given shaderfx is not local (i.e. from linked data) when the object is a library + * override. + * + * \param shaderfx: May be NULL, in which case we consider it as a non-local shaderfx case. + */ +bool BKE_shaderfx_is_nonlocal_in_liboverride(const Object *ob, const ShaderFxData *shaderfx) +{ + return (ID_IS_OVERRIDE_LIBRARY(ob) && + ((shaderfx == NULL) || (shaderfx->flag & eShaderFxFlag_OverrideLibrary_Local) == 0)); +} + +/** * Get an effect's panel type, which was defined in the #panelRegister callback. * * \note ShaderFx panel types are assumed to be named with the struct name field concatenated to diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 6dd1f66f6b5..f4a9d328d86 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -187,8 +187,8 @@ static void sound_blend_read_data(BlendDataReader *reader, ID *id) static void sound_blend_read_lib(BlendLibReader *reader, ID *id) { bSound *sound = (bSound *)id; - BLO_read_id_address( - reader, sound->id.lib, &sound->ipo); /* XXX deprecated - old animation system */ + /* XXX: deprecated - old animation system. */ + BLO_read_id_address(reader, sound->id.lib, &sound->ipo); } static void sound_blend_read_expand(BlendExpander *expander, ID *id) diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 8956ba6adae..584156ea40f 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -40,6 +40,60 @@ Spline::Type Spline::type() const return type_; } +void Spline::copy_base_settings(const Spline &src, Spline &dst) +{ + dst.normal_mode = src.normal_mode; + dst.is_cyclic_ = src.is_cyclic_; +} + +static SplinePtr create_spline(const Spline::Type type) +{ + switch (type) { + case Spline::Type::Poly: + return std::make_unique<PolySpline>(); + case Spline::Type::Bezier: + return std::make_unique<BezierSpline>(); + case Spline::Type::NURBS: + return std::make_unique<NURBSpline>(); + } + BLI_assert_unreachable(); + return {}; +} + +/** + * Return a new spline with the same data, settings, and attributes. + */ +SplinePtr Spline::copy() const +{ + SplinePtr dst = this->copy_without_attributes(); + dst->attributes = this->attributes; + return dst; +} + +/** + * Return a new spline with the same type and settings like "cyclic", but without any data. + */ +SplinePtr Spline::copy_only_settings() const +{ + SplinePtr dst = create_spline(type_); + this->copy_base_settings(*this, *dst); + this->copy_settings(*dst); + return dst; +} + +/** + * The same as #copy, but skips copying dynamic attributes to the new spline. + */ +SplinePtr Spline::copy_without_attributes() const +{ + SplinePtr dst = this->copy_only_settings(); + this->copy_data(*dst); + + /* Though the attributes storage is empty, it still needs to know the correct size. */ + dst->attributes.reallocate(dst->size()); + return dst; +} + void Spline::translate(const blender::float3 &translation) { for (float3 &position : this->positions()) { @@ -74,9 +128,9 @@ float Spline::length() const int Spline::segments_size() const { - const int points_len = this->size(); + const int size = this->size(); - return is_cyclic_ ? points_len : points_len - 1; + return is_cyclic_ ? size : size - 1; } bool Spline::is_cyclic() const @@ -209,10 +263,86 @@ static float3 rotate_direction_around_axis(const float3 &direction, return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); } -static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals) +static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> r_normals) { - for (const int i : normals.index_range()) { - normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized(); + BLI_assert(r_normals.size() == tangents.size()); + + /* Same as in `vec_to_quat`. */ + const float epsilon = 1e-4f; + for (const int i : r_normals.index_range()) { + const float3 &tangent = tangents[i]; + if (fabsf(tangent.x) + fabsf(tangent.y) < epsilon) { + r_normals[i] = {1.0f, 0.0f, 0.0f}; + } + else { + r_normals[i] = float3(tangent.y, -tangent.x, 0.0f).normalized(); + } + } +} + +/** + * Rotate the last normal in the same way the tangent has been rotated. + */ +static float3 calculate_next_normal(const float3 &last_normal, + const float3 &last_tangent, + const float3 ¤t_tangent) +{ + if (last_tangent.is_zero() || current_tangent.is_zero()) { + return last_normal; + } + const float angle = angle_normalized_v3v3(last_tangent, current_tangent); + if (angle != 0.0) { + const float3 axis = float3::cross(last_tangent, current_tangent).normalized(); + return rotate_direction_around_axis(last_normal, axis, angle); + } + return last_normal; +} + +static void calculate_normals_minimum(Span<float3> tangents, + const bool cyclic, + MutableSpan<float3> r_normals) +{ + BLI_assert(r_normals.size() == tangents.size()); + + if (r_normals.is_empty()) { + return; + } + + const float epsilon = 1e-4f; + + /* Set initial normal. */ + const float3 &first_tangent = tangents[0]; + if (fabs(first_tangent.x) + fabs(first_tangent.y) < epsilon) { + r_normals[0] = {1.0f, 0.0f, 0.0f}; + } + else { + r_normals[0] = float3(first_tangent.y, -first_tangent.x, 0.0f).normalized(); + } + + /* Forward normal with minimum twist along the entire spline. */ + for (const int i : IndexRange(1, r_normals.size() - 1)) { + r_normals[i] = calculate_next_normal(r_normals[i - 1], tangents[i - 1], tangents[i]); + } + + if (!cyclic) { + return; + } + + /* Compute how much the first normal deviates from the normal that has been forwarded along the + * entire cyclic spline. */ + const float3 uncorrected_last_normal = calculate_next_normal( + r_normals.last(), tangents.last(), tangents[0]); + float correction_angle = angle_signed_on_axis_v3v3_v3( + r_normals[0], uncorrected_last_normal, tangents[0]); + if (correction_angle > M_PI) { + correction_angle = correction_angle - 2 * M_PI; + } + + /* Gradually apply correction by rotating all normals slightly. */ + const float angle_step = correction_angle / r_normals.size(); + for (const int i : r_normals.index_range()) { + const float angle = angle_step * i; + r_normals[i] = rotate_direction_around_axis(r_normals[i], tangents[i], angle); } } @@ -234,14 +364,28 @@ Span<float3> Spline::evaluated_normals() const const int eval_size = this->evaluated_points_size(); evaluated_normals_cache_.resize(eval_size); - Span<float3> tangents = evaluated_tangents(); + Span<float3> tangents = this->evaluated_tangents(); MutableSpan<float3> normals = evaluated_normals_cache_; /* Only Z up normals are supported at the moment. */ - calculate_normals_z_up(tangents, normals); + switch (this->normal_mode) { + case ZUp: { + calculate_normals_z_up(tangents, normals); + break; + } + case Minimum: { + calculate_normals_minimum(tangents, is_cyclic_, normals); + break; + } + case Tangent: { + /* Tangent mode is not yet supported. */ + calculate_normals_z_up(tangents, normals); + break; + } + } /* Rotate the generated normals with the interpolated tilt data. */ - GVArray_Typed<float> tilts = this->interpolate_to_evaluated_points(this->tilts()); + GVArray_Typed<float> tilts = this->interpolate_to_evaluated(this->tilts()); for (const int i : normals.index_range()) { normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]); } @@ -380,23 +524,23 @@ void Spline::sample_length_parameters_to_index_factors(MutableSpan<float> parame Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const { - const int points_len = this->evaluated_points_size(); + const int eval_size = this->evaluated_points_size(); if (is_cyclic_) { - if (index_factor < points_len) { + if (index_factor < eval_size) { const int index = std::floor(index_factor); - const int next_index = (index < points_len - 1) ? index + 1 : 0; + const int next_index = (index < eval_size - 1) ? index + 1 : 0; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{points_len - 1, 0, 1.0f}; + return LookupResult{eval_size - 1, 0, 1.0f}; } - if (index_factor < points_len - 1) { + if (index_factor < eval_size - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{points_len - 2, points_len - 1, 1.0f}; + return LookupResult{eval_size - 2, eval_size - 1, 1.0f}; } void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const @@ -407,9 +551,9 @@ void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) } } -GVArrayPtr Spline::interpolate_to_evaluated_points(GSpan data) const +GVArrayPtr Spline::interpolate_to_evaluated(GSpan data) const { - return this->interpolate_to_evaluated_points(GVArray_For_GSpan(data)); + return this->interpolate_to_evaluated(GVArray_For_GSpan(data)); } /** @@ -417,9 +561,9 @@ GVArrayPtr Spline::interpolate_to_evaluated_points(GSpan data) const * points) to arbitrary parameters in between the evaluated points. The interpolation is quite * simple, but this handles the cyclic and end point special cases. */ -void Spline::sample_based_on_index_factors(const GVArray &src, - Span<float> index_factors, - GMutableSpan dst) const +void Spline::sample_with_index_factors(const GVArray &src, + Span<float> index_factors, + GMutableSpan dst) const { BLI_assert(src.size() == this->evaluated_points_size()); @@ -427,7 +571,7 @@ void Spline::sample_based_on_index_factors(const GVArray &src, using T = decltype(dummy); const GVArray_Typed<T> src_typed = src.typed<T>(); MutableSpan<T> dst_typed = dst.typed<T>(); - blender::parallel_for(dst_typed.index_range(), 1024, [&](IndexRange range) { + blender::threading::parallel_for(dst_typed.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { const LookupResult interp = this->lookup_data_from_index_factor(index_factors[i]); dst_typed[i] = blender::attribute_math::mix2(interp.factor, diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 3e421dcfc13..02d26ac715b 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -25,18 +25,26 @@ using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::fn::GVArray; +using blender::fn::GVArray_For_ArrayContainer; +using blender::fn::GVArrayPtr; -SplinePtr BezierSpline::copy() const +void BezierSpline::copy_settings(Spline &dst) const { - return std::make_unique<BezierSpline>(*this); + BezierSpline &bezier = static_cast<BezierSpline &>(dst); + bezier.resolution_ = resolution_; } -SplinePtr BezierSpline::copy_settings() const +void BezierSpline::copy_data(Spline &dst) const { - std::unique_ptr<BezierSpline> copy = std::make_unique<BezierSpline>(); - copy_base_settings(*this, *copy); - copy->resolution_ = resolution_; - return copy; + BezierSpline &bezier = static_cast<BezierSpline &>(dst); + bezier.positions_ = positions_; + bezier.handle_types_left_ = handle_types_left_; + bezier.handle_positions_left_ = handle_positions_left_; + bezier.handle_types_right_ = handle_types_right_; + bezier.handle_positions_right_ = handle_positions_right_; + bezier.radii_ = radii_; + bezier.tilts_ = tilts_; } int BezierSpline::size() const @@ -352,9 +360,9 @@ static void bezier_forward_difference_3d(const float3 &point_0, } } -void BezierSpline::evaluate_bezier_segment(const int index, - const int next_index, - MutableSpan<float3> positions) const +void BezierSpline::evaluate_segment(const int index, + const int next_index, + MutableSpan<float3> positions) const { if (this->segment_is_vector(index)) { BLI_assert(positions.size() == 1); @@ -389,13 +397,13 @@ Span<int> BezierSpline::control_point_offsets() const return offset_cache_; } - const int points_len = this->size(); - offset_cache_.resize(points_len + 1); + const int size = this->size(); + offset_cache_.resize(size + 1); MutableSpan<int> offsets = offset_cache_; int offset = 0; - for (const int i : IndexRange(points_len)) { + for (const int i : IndexRange(size)) { offsets[i] = offset; offset += this->segment_is_vector(i) ? 1 : resolution_; } @@ -417,7 +425,7 @@ static void calculate_mappings_linear_resolution(Span<int> offsets, } const int grain_size = std::max(2048 / resolution, 1); - parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) { + blender::threading::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) { for (const int i_control_point : range) { const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point]; const float segment_len_inv = 1.0f / segment_len; @@ -497,14 +505,13 @@ Span<float3> BezierSpline::evaluated_positions() const Span<int> offsets = this->control_point_offsets(); const int grain_size = std::max(512 / resolution_, 1); - parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) { + blender::threading::parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) { for (const int i : range) { - this->evaluate_bezier_segment( - i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); + this->evaluate_segment(i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); } }); if (is_cyclic_) { - this->evaluate_bezier_segment( + this->evaluate_segment( size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1])); } else { @@ -525,66 +532,66 @@ Span<float3> BezierSpline::evaluated_positions() const BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor( const float index_factor) const { - const int points_len = this->size(); + const int size = this->size(); if (is_cyclic_) { - if (index_factor < points_len) { + if (index_factor < size) { const int index = std::floor(index_factor); - const int next_index = (index < points_len - 1) ? index + 1 : 0; + const int next_index = (index < size - 1) ? index + 1 : 0; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{points_len - 1, 0, 1.0f}; + return InterpolationData{size - 1, 0, 1.0f}; } - if (index_factor < points_len - 1) { + if (index_factor < size - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{points_len - 2, points_len - 1, 1.0f}; + return InterpolationData{size - 2, size - 1, 1.0f}; } /* Use a spline argument to avoid adding this to the header. */ template<typename T> -static void interpolate_to_evaluated_points_impl(const BezierSpline &spline, - const blender::VArray<T> &source_data, - MutableSpan<T> result_data) +static void interpolate_to_evaluated_impl(const BezierSpline &spline, + const blender::VArray<T> &src, + MutableSpan<T> dst) { + BLI_assert(src.size() == spline.size()); + BLI_assert(dst.size() == spline.evaluated_points_size()); Span<float> mappings = spline.evaluated_mappings(); - for (const int i : result_data.index_range()) { + for (const int i : dst.index_range()) { BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor( mappings[i]); - const T &value = source_data[interp.control_point_index]; - const T &next_value = source_data[interp.next_control_point_index]; + const T &value = src[interp.control_point_index]; + const T &next_value = src[interp.next_control_point_index]; - result_data[i] = blender::attribute_math::mix2(interp.factor, value, next_value); + dst[i] = blender::attribute_math::mix2(interp.factor, value, next_value); } } -blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points( - const blender::fn::GVArray &source_data) const +GVArrayPtr BezierSpline::interpolate_to_evaluated(const GVArray &src) const { - BLI_assert(source_data.size() == this->size()); + BLI_assert(src.size() == this->size()); - if (source_data.is_single()) { - return source_data.shallow_copy(); + if (src.is_single()) { + return src.shallow_copy(); } const int eval_size = this->evaluated_points_size(); if (eval_size == 1) { - return source_data.shallow_copy(); + return src.shallow_copy(); } - blender::fn::GVArrayPtr new_varray; - blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) { + GVArrayPtr new_varray; + blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { Array<T> values(eval_size); - interpolate_to_evaluated_points_impl<T>(*this, source_data.typed<T>(), values); - new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>( - std::move(values)); + interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values); + new_varray = std::make_unique<GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index bfb0d652b1a..85fb9730e83 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -26,21 +26,28 @@ using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::fn::GVArray; +using blender::fn::GVArray_For_ArrayContainer; using blender::fn::GVArray_Typed; +using blender::fn::GVArrayPtr; -SplinePtr NURBSpline::copy() const +void NURBSpline::copy_settings(Spline &dst) const { - return std::make_unique<NURBSpline>(*this); + NURBSpline &nurbs = static_cast<NURBSpline &>(dst); + nurbs.knots_mode = knots_mode; + nurbs.resolution_ = resolution_; + nurbs.order_ = order_; } -SplinePtr NURBSpline::copy_settings() const +void NURBSpline::copy_data(Spline &dst) const { - std::unique_ptr<NURBSpline> copy = std::make_unique<NURBSpline>(); - copy_base_settings(*this, *copy); - copy->knots_mode = knots_mode; - copy->resolution_ = resolution_; - copy->order_ = order_; - return copy; + NURBSpline &nurbs = static_cast<NURBSpline &>(dst); + nurbs.positions_ = positions_; + nurbs.weights_ = weights_; + nurbs.knots_ = knots_; + nurbs.knots_dirty_ = false; + nurbs.radii_ = radii_; + nurbs.tilts_ = tilts_; } int NURBSpline::size() const @@ -268,18 +275,18 @@ Span<float> NURBSpline::knots() const } static void calculate_basis_for_point(const float parameter, - const int points_len, + const int size, const int order, Span<float> knots, MutableSpan<float> basis_buffer, NURBSpline::BasisCache &basis_cache) { /* Clamp parameter due to floating point inaccuracy. */ - const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]); + const float t = std::clamp(parameter, knots[0], knots[size + order - 1]); int start = 0; int end = 0; - for (const int i : IndexRange(points_len + order - 1)) { + for (const int i : IndexRange(size + order - 1)) { const bool knots_equal = knots[i] == knots[i + 1]; if (knots_equal || t < knots[i] || t > knots[i + 1]) { basis_buffer[i] = 0.0f; @@ -289,14 +296,14 @@ static void calculate_basis_for_point(const float parameter, basis_buffer[i] = 1.0f; start = std::max(i - order - 1, 0); end = i; - basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f); + basis_buffer.slice(i + 1, size + order - 1 - i).fill(0.0f); break; } - basis_buffer[points_len + order - 1] = 0.0f; + basis_buffer[size + order - 1] = 0.0f; for (const int i_order : IndexRange(2, order - 1)) { - if (end + i_order >= points_len + order) { - end = points_len + order - 1 - i_order; + if (end + i_order >= size + order) { + end = size + order - 1 - i_order; } for (const int i : IndexRange(start, end - start + 1)) { float new_basis = 0.0f; @@ -326,18 +333,18 @@ static void calculate_basis_for_point(const float parameter, basis_cache.start_index = start; } -void NURBSpline::calculate_basis_cache() const +Span<NURBSpline::BasisCache> NURBSpline::calculate_basis_cache() const { if (!basis_cache_dirty_) { - return; + return basis_cache_; } std::lock_guard lock{basis_cache_mutex_}; if (!basis_cache_dirty_) { - return; + return basis_cache_; } - const int points_len = this->size(); + const int size = this->size(); const int eval_size = this->evaluated_points_size(); BLI_assert(this->evaluated_edges_size() > 0); basis_cache_.resize(eval_size); @@ -353,17 +360,17 @@ void NURBSpline::calculate_basis_cache() const Array<float> basis_buffer(this->knots_size()); const float start = knots[order - 1]; - const float end = is_cyclic_ ? knots[points_len + order - 1] : knots[points_len]; + const float end = is_cyclic_ ? knots[size + order - 1] : knots[size]; const float step = (end - start) / this->evaluated_edges_size(); float parameter = start; for (const int i : IndexRange(eval_size)) { BasisCache &basis = basis_cache[i]; calculate_basis_for_point( - parameter, points_len + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis); + parameter, size + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis); BLI_assert(basis.weights.size() <= order); for (const int j : basis.weights.index_range()) { - const int point_index = (basis.start_index + j) % points_len; + const int point_index = (basis.start_index + j) % size; basis.weights[j] *= control_weights[point_index]; } @@ -371,50 +378,47 @@ void NURBSpline::calculate_basis_cache() const } basis_cache_dirty_ = false; + return basis_cache_; } template<typename T> -void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights, - const blender::VArray<T> &source_data, - MutableSpan<T> result_data) +void interpolate_to_evaluated_impl(Span<NURBSpline::BasisCache> weights, + const blender::VArray<T> &src, + MutableSpan<T> dst) { - const int points_len = source_data.size(); - BLI_assert(result_data.size() == weights.size()); - blender::attribute_math::DefaultMixer<T> mixer(result_data); + const int size = src.size(); + BLI_assert(dst.size() == weights.size()); + blender::attribute_math::DefaultMixer<T> mixer(dst); - for (const int i : result_data.index_range()) { + for (const int i : dst.index_range()) { Span<float> point_weights = weights[i].weights; const int start_index = weights[i].start_index; - for (const int j : point_weights.index_range()) { - const int point_index = (start_index + j) % points_len; - mixer.mix_in(i, source_data[point_index], point_weights[j]); + const int point_index = (start_index + j) % size; + mixer.mix_in(i, src[point_index], point_weights[j]); } } mixer.finalize(); } -blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points( - const blender::fn::GVArray &source_data) const +GVArrayPtr NURBSpline::interpolate_to_evaluated(const GVArray &src) const { - BLI_assert(source_data.size() == this->size()); + BLI_assert(src.size() == this->size()); - if (source_data.is_single()) { - return source_data.shallow_copy(); + if (src.is_single()) { + return src.shallow_copy(); } - this->calculate_basis_cache(); - Span<BasisCache> weights(basis_cache_); + Span<BasisCache> basis_cache = this->calculate_basis_cache(); - blender::fn::GVArrayPtr new_varray; - blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) { + GVArrayPtr new_varray; + blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { Array<T> values(this->evaluated_points_size()); - interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values); - new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>( - std::move(values)); + interpolate_to_evaluated_impl<T>(basis_cache, src.typed<T>(), values); + new_varray = std::make_unique<GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); @@ -436,7 +440,7 @@ Span<float3> NURBSpline::evaluated_positions() const evaluated_position_cache_.resize(eval_size); /* TODO: Avoid copying the evaluated data from the temporary array. */ - GVArray_Typed<float3> evaluated = Spline::interpolate_to_evaluated_points(positions_.as_span()); + GVArray_Typed<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span()); evaluated->materialize(evaluated_position_cache_); position_cache_dirty_ = false; diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index 5f8e81d5ad0..dfd24b2566e 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -22,17 +22,20 @@ using blender::float3; using blender::MutableSpan; using blender::Span; +using blender::fn::GVArray; +using blender::fn::GVArrayPtr; -SplinePtr PolySpline::copy() const +void PolySpline::copy_settings(Spline &UNUSED(dst)) const { - return std::make_unique<PolySpline>(*this); + /* Poly splines have no settings not covered by the base class. */ } -SplinePtr PolySpline::copy_settings() const +void PolySpline::copy_data(Spline &dst) const { - std::unique_ptr<PolySpline> copy = std::make_unique<PolySpline>(); - copy_base_settings(*this, *copy); - return copy; + PolySpline &poly = static_cast<PolySpline &>(dst); + poly.positions_ = positions_; + poly.radii_ = radii_; + poly.tilts_ = tilts_; } int PolySpline::size() const @@ -115,10 +118,9 @@ Span<float3> PolySpline::evaluated_positions() const * the original data. Therefore the lifetime of the returned virtual array must not be longer than * the source data. */ -blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points( - const blender::fn::GVArray &source_data) const +GVArrayPtr PolySpline::interpolate_to_evaluated(const GVArray &src) const { - BLI_assert(source_data.size() == this->size()); + BLI_assert(src.size() == this->size()); - return source_data.shallow_copy(); + return src.shallow_copy(); } diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index 5f732ba91ab..4ca6f93b014 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -1042,7 +1042,7 @@ typedef struct AverageGridsBoundariesData { CCGKey *key; /* Optional lookup table. Maps task index to index in `subdiv_ccg->adjacent_vertices`. */ - int *adjacent_edge_index_map; + const int *adjacent_edge_index_map; } AverageGridsBoundariesData; typedef struct AverageGridsBoundariesTLSData { @@ -1117,7 +1117,7 @@ typedef struct AverageGridsCornerData { CCGKey *key; /* Optional lookup table. Maps task range index to index in subdiv_ccg->adjacent_vertices*/ - int *adjacent_vert_index_map; + const int *adjacent_vert_index_map; } AverageGridsCornerData; static void subdiv_ccg_average_grids_corners(SubdivCCG *subdiv_ccg, @@ -1161,7 +1161,7 @@ static void subdiv_ccg_average_grids_corners_task(void *__restrict userdata_v, static void subdiv_ccg_average_boundaries(SubdivCCG *subdiv_ccg, CCGKey *key, - int *adjacent_edge_index_map, + const int *adjacent_edge_index_map, int num_adjacent_edges) { TaskParallelSettings parallel_range_settings; @@ -1186,7 +1186,7 @@ static void subdiv_ccg_average_all_boundaries(SubdivCCG *subdiv_ccg, CCGKey *key static void subdiv_ccg_average_corners(SubdivCCG *subdiv_ccg, CCGKey *key, - int *adjacent_vert_index_map, + const int *adjacent_vert_index_map, int num_adjacent_vertices) { TaskParallelSettings parallel_range_settings; diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index a28136f8527..23eccbfba9b 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -1879,12 +1879,11 @@ static const MeshElemMap *ccgDM_getPolyMap(Object *ob, DerivedMesh *dm) /* WARNING! *MUST* be called in an 'loops_cache_rwlock' protected thread context! */ static void ccgDM_recalcLoopTri(DerivedMesh *dm) { - MLoopTri *mlooptri = dm->looptris.array; const int tottri = dm->numPolyData * 2; int i, poly_index; DM_ensure_looptri_data(dm); - mlooptri = dm->looptris.array_wip; + MLoopTri *mlooptri = dm->looptris.array_wip; BLI_assert(tottri == 0 || mlooptri != NULL); BLI_assert(poly_to_tri_count(dm->numPolyData, dm->numLoopData) == dm->looptris.num); diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 5cf76bb6452..c2fb5ef4238 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -152,7 +152,7 @@ static struct bUnitDef buMetricLenDef[] = { {"micrometer", "micrometers", "µm", "um", "Micrometers", "MICROMETERS", UN_SC_UM, 0.0, B_UNIT_DEF_NONE}, /* These get displayed because of float precision problems in the transform header, - * could work around, but for now probably people wont use these. */ + * could work around, but for now probably people won't use these. */ #if 0 {"nanometer", "Nanometers", "nm", NULL, 0.000000001, 0.0, B_UNIT_DEF_NONE}, {"picometer", "Picometers", "pm", NULL, 0.000000000001, 0.0, B_UNIT_DEF_NONE}, @@ -988,7 +988,7 @@ static int unit_scale_str(char *str, memcpy(str_found, str_tmp, len_num); /* Without the string terminator. */ } - /* Since the null terminator wont be moved if the stringlen_max + /* Since the null terminator won't be moved if the stringlen_max * was not long enough to fit everything in it. */ str[len_max - 1] = '\0'; return found_ofs + len_num; @@ -1136,8 +1136,8 @@ bool BKE_unit_replace_string( strncpy(str, str_tmp, len_max); } else { - /* BLI_snprintf would not fit into str_tmp, cant do much in this case. - * Check for this because otherwise BKE_unit_replace_string could call its self forever. */ + /* BLI_snprintf would not fit into str_tmp, can't do much in this case. + * Check for this because otherwise BKE_unit_replace_string could call itself forever. */ return changed; } diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index c0ce57818d1..5fc55aad6a2 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -36,6 +36,7 @@ #include "BLI_math.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_task.hh" #include "BLI_utildefines.h" #include "BKE_anim_data.h" @@ -324,15 +325,19 @@ struct VolumeGrid { openvdb::io::File file(filepath); - try { - file.setCopyMaxBytes(0); - file.open(); - openvdb::GridBase::Ptr vdb_grid = file.readGrid(name()); - entry->grid->setTree(vdb_grid->baseTreePtr()); - } - catch (const openvdb::IoError &e) { - entry->error_msg = e.what(); - } + /* Isolate file loading since that's potentially multithreaded and we are + * holding a mutex lock. */ + blender::threading::isolate_task([&] { + try { + file.setCopyMaxBytes(0); + file.open(); + openvdb::GridBase::Ptr vdb_grid = file.readGrid(name()); + entry->grid->setTree(vdb_grid->baseTreePtr()); + } + catch (const openvdb::IoError &e) { + entry->error_msg = e.what(); + } + }); std::atomic_thread_fence(std::memory_order_release); entry->is_loaded = true; @@ -740,7 +745,7 @@ static int volume_sequence_frame(const Depsgraph *depsgraph, const Volume *volum } } - /* Important to apply after, else we cant loop on e.g. frames 100 - 110. */ + /* Important to apply after, else we can't loop on e.g. frames 100 - 110. */ frame += frame_offset; return frame; diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h index e91726991ca..d42bd6af637 100644 --- a/source/blender/blenlib/BLI_delaunay_2d.h +++ b/source/blender/blenlib/BLI_delaunay_2d.h @@ -178,6 +178,8 @@ typedef enum CDT_output_type { CDT_FULL, /** All triangles fully enclosed by constraint edges or faces. */ CDT_INSIDE, + /** Like previous, but detect holes and omit those from output. */ + CDT_INSIDE_WITH_HOLES, /** Only point, edge, and face constraints, and their intersections. */ CDT_CONSTRAINTS, /** @@ -186,7 +188,9 @@ typedef enum CDT_output_type { * #BMesh faces in Blender: that is, * no vertex appears more than once and no isolated holes in faces. */ - CDT_CONSTRAINTS_VALID_BMESH + CDT_CONSTRAINTS_VALID_BMESH, + /** Like previous, but detect holes and omit those from output. */ + CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES, } CDT_output_type; /** diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh index 89be4cad848..a05f7724dd2 100644 --- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh +++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh @@ -26,7 +26,7 @@ #include "BLI_map.hh" #include "BLI_utility_mixins.hh" -namespace blender { +namespace blender::threading { namespace enumerable_thread_specific_utils { inline std::atomic<int> next_id = 0; @@ -70,4 +70,4 @@ template<typename T> class EnumerableThreadSpecific : NonCopyable, NonMovable { #endif /* WITH_TBB */ }; -} // namespace blender +} // namespace blender::threading diff --git a/source/blender/blenlib/BLI_math.h b/source/blender/blenlib/BLI_math.h index f6075367ac5..3b61c0feb51 100644 --- a/source/blender/blenlib/BLI_math.h +++ b/source/blender/blenlib/BLI_math.h @@ -70,4 +70,5 @@ #include "BLI_math_rotation.h" #include "BLI_math_solvers.h" #include "BLI_math_statistics.h" +#include "BLI_math_time.h" #include "BLI_math_vector.h" diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 46219ad5493..88dc20a64f2 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -237,6 +237,7 @@ float ceil_power_of_10(float f); #ifndef NDEBUG /** \note 0.0001 is too small because normals may be converted from short's: see T34322. */ # define BLI_ASSERT_UNIT_EPSILON 0.0002f +# define BLI_ASSERT_UNIT_EPSILON_DB 0.0002 /** * \note Checks are flipped so NAN doesn't assert. * This is done because we're making sure the value was normalized and in the case we @@ -253,8 +254,8 @@ float ceil_power_of_10(float f); # define BLI_ASSERT_UNIT_V3_DB(v) \ { \ const double _test_unit = len_squared_v3_db(v); \ - BLI_assert(!(fabs(_test_unit - 1.0) >= BLI_ASSERT_UNIT_EPSILON) || \ - !(fabs(_test_unit) >= BLI_ASSERT_UNIT_EPSILON)); \ + BLI_assert(!(fabs(_test_unit - 1.0) >= BLI_ASSERT_UNIT_EPSILON_DB) || \ + !(fabs(_test_unit) >= BLI_ASSERT_UNIT_EPSILON_DB)); \ } \ (void)0 @@ -295,6 +296,7 @@ float ceil_power_of_10(float f); #else # define BLI_ASSERT_UNIT_V2(v) (void)(v) # define BLI_ASSERT_UNIT_V3(v) (void)(v) +# define BLI_ASSERT_UNIT_V3_DB(v) (void)(v) # define BLI_ASSERT_UNIT_QUAT(v) (void)(v) # define BLI_ASSERT_ZERO_M3(m) (void)(m) # define BLI_ASSERT_ZERO_M4(m) (void)(m) diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index c744c5d13d3..43b31d76bb0 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -105,6 +105,11 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3], const float v2[3], const float v3[3], const float v4[3]); +bool is_quad_flip_v3_first_third_fast_with_normal(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const float normal[3]); /********************************* Distance **********************************/ @@ -672,8 +677,8 @@ void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x void planes_from_projmat(const float mat[4][4], float left[4], float right[4], - float top[4], float bottom[4], + float top[4], float near[4], float far[4]); diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index fe995b2e46e..ef10d02f10f 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -164,6 +164,9 @@ void compatible_eul(float eul[3], const float old[3]); void rotate_eul(float eul[3], const char axis, const float angle); +void add_eul_euleul(float r_eul[3], float a[3], float b[3], const short order); +void sub_eul_euleul(float r_eul[3], float a[3], float b[3], const short order); + /************************** Arbitrary Order Eulers ***************************/ /* warning: must match the eRotationModes in DNA_action_types.h diff --git a/source/blender/blenlib/BLI_math_time.h b/source/blender/blenlib/BLI_math_time.h new file mode 100644 index 00000000000..671ec6f857f --- /dev/null +++ b/source/blender/blenlib/BLI_math_time.h @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/************************ Time constants definitions***************************/ +#define SECONDS_IN_MILLISECONDS 0.001 +#define SECONDS_IN_MINUTE 60.0 +#define MINUTES_IN_HOUR 60.0 +#define HOURS_IN_DAY 24.0 + +#define MINUTES_IN_DAY (MINUTES_IN_HOUR * HOURS_IN_DAY) +#define SECONDS_IN_DAY (MINUTES_IN_DAY * SECONDS_IN_MINUTE) +#define SECONDS_IN_HOUR (MINUTES_IN_HOUR * SECONDS_IN_MINUTE) + +void BLI_math_time_seconds_decompose(double seconds, + double *r_days, + double *r_hours, + double *r_minutes, + double *r_seconds, + double *r_milliseconds); + +/**************************** Inline Definitions ******************************/ + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index b43f86af670..556a216bb89 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -163,6 +163,7 @@ MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f); MINLINE void madd_v3_v3v3(float r[3], const float a[3], const float b[3]); MINLINE void madd_v2_v2v2fl(float r[2], const float a[2], const float b[2], float f); MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f); +MINLINE void madd_v3_v3v3db_db(double r[3], const double a[3], const double b[3], double f); MINLINE void madd_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float c[3]); MINLINE void madd_v4_v4fl(float r[4], const float a[4], float f); MINLINE void madd_v4_v4v4(float r[4], const float a[4], const float b[4]); @@ -327,6 +328,10 @@ MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; +MINLINE bool is_zero_v2_db(const double a[2]) ATTR_WARN_UNUSED_RESULT; +MINLINE bool is_zero_v3_db(const double a[3]) ATTR_WARN_UNUSED_RESULT; +MINLINE bool is_zero_v4_db(const double a[4]) ATTR_WARN_UNUSED_RESULT; + bool is_finite_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/BLI_memarena.h b/source/blender/blenlib/BLI_memarena.h index 87a320e336d..d7798f12fcc 100644 --- a/source/blender/blenlib/BLI_memarena.h +++ b/source/blender/blenlib/BLI_memarena.h @@ -38,7 +38,8 @@ extern "C" { struct MemArena; typedef struct MemArena MemArena; -struct MemArena *BLI_memarena_new(const size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT +struct MemArena *BLI_memarena_new(const size_t bufsize, + const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC; void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1); void BLI_memarena_use_malloc(struct MemArena *ma) ATTR_NONNULL(1); diff --git a/source/blender/blenlib/BLI_memiter.h b/source/blender/blenlib/BLI_memiter.h index c7a715309e1..abb1bec809d 100644 --- a/source/blender/blenlib/BLI_memiter.h +++ b/source/blender/blenlib/BLI_memiter.h @@ -36,15 +36,14 @@ struct BLI_memiter; typedef struct BLI_memiter BLI_memiter; /* warning, ATTR_MALLOC flag on BLI_memiter_alloc causes crash, see: D2756 */ -BLI_memiter *BLI_memiter_create(unsigned int chunk_size) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; -void *BLI_memiter_alloc(BLI_memiter *mi, - unsigned int size) ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(1); +BLI_memiter *BLI_memiter_create(unsigned int chunk_size) + ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; +void *BLI_memiter_alloc(BLI_memiter *mi, unsigned int size) + ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1); void BLI_memiter_alloc_from(BLI_memiter *mi, uint elem_size, const void *data_from) ATTR_NONNULL(1, 3); -void *BLI_memiter_calloc(BLI_memiter *mi, - unsigned int size) ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(1); +void *BLI_memiter_calloc(BLI_memiter *mi, unsigned int size) + ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1); void BLI_memiter_destroy(BLI_memiter *mi) ATTR_NONNULL(1); void BLI_memiter_clear(BLI_memiter *mi) ATTR_NONNULL(1); unsigned int BLI_memiter_count(const BLI_memiter *mi) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); @@ -59,11 +58,11 @@ typedef struct BLI_memiter_handle { uint elem_left; } BLI_memiter_handle; -void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter) ATTR_NONNULL(); -bool BLI_memiter_iter_done(const BLI_memiter_handle *iter) ATTR_NONNULL(); -void *BLI_memiter_iter_step(BLI_memiter_handle *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter) ATTR_NONNULL(1, 2); +bool BLI_memiter_iter_done(const BLI_memiter_handle *iter) ATTR_NONNULL(1); +void *BLI_memiter_iter_step(BLI_memiter_handle *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); void *BLI_memiter_iter_step_size(BLI_memiter_handle *iter, uint *r_size) ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(); + ATTR_NONNULL(1, 2); #ifdef __cplusplus } diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h index c11802d6270..4d9381093c7 100644 --- a/source/blender/blenlib/BLI_mempool.h +++ b/source/blender/blenlib/BLI_mempool.h @@ -38,9 +38,12 @@ typedef struct BLI_mempool BLI_mempool; BLI_mempool *BLI_mempool_create(unsigned int esize, unsigned int totelem, unsigned int pchunk, - unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; -void *BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); -void *BLI_mempool_calloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); + unsigned int flag) + ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; +void *BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL + ATTR_NONNULL(1); +void *BLI_mempool_calloc(BLI_mempool *pool) + ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1); void BLI_mempool_free(BLI_mempool *pool, void *addr) ATTR_NONNULL(1, 2); void BLI_mempool_clear_ex(BLI_mempool *pool, const int totelem_reserve) ATTR_NONNULL(1); void BLI_mempool_clear(BLI_mempool *pool) ATTR_NONNULL(1); diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 83ccfda7e38..d6b068c3889 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -67,55 +67,17 @@ typedef enum TaskPriority { TASK_PRIORITY_HIGH, } TaskPriority; -/** - * Task isolation helps avoid unexpected task scheduling decisions that can lead to bugs if wrong - * assumptions were made. Typically that happens when doing "nested threading", i.e. one thread - * schedules a bunch of main-tasks and those spawn new sub-tasks. - * - * What can happen is that when a main-task waits for its sub-tasks to complete on other threads, - * another main-task is scheduled within the already running main-task. Generally, this is good, - * because it leads to better performance. However, sometimes code (often unintentionally) makes - * the assumption that at most one main-task runs on a thread at a time. - * - * The bugs often show themselves in two ways: - * - Deadlock, when a main-task holds a mutex while waiting for its sub-tasks to complete. - * - Data corruption, when a main-task makes wrong assumptions about a thread-local variable. - * - * Task isolation can avoid these bugs by making sure that a main-task does not start executing - * another main-task while waiting for its sub-tasks. More precisely, a function that runs in an - * isolated region is only allowed to run sub-tasks that were spawned in the same isolated region. - * - * Unfortunately, incorrect use of task isolation can lead to deadlocks itself. This can happen - * when threading primitives are used that separate spawning tasks from executing them. The problem - * occurs when a task is spawned in one isolated region while the tasks are waited for in another - * isolated region. In this setup, the thread that is waiting for the spawned tasks to complete - * cannot run the tasks itself. On a single thread, that causes a deadlock already. When there are - * multiple threads, another thread will typically run the task and avoid the deadlock. However, if - * this situation happens on all threads at the same time, all threads will deadlock. This happened - * in T88598. - */ -typedef enum TaskIsolation { - /* Do not use task isolation. Always use this when tasks are pushed recursively. */ - TASK_ISOLATION_OFF, - /* Run each task in its own isolated region. */ - TASK_ISOLATION_ON, -} TaskIsolation; - typedef struct TaskPool TaskPool; typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata); typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata); /* Regular task pool that immediately starts executing tasks as soon as they * are pushed, either on the current or another thread. */ -TaskPool *BLI_task_pool_create(void *userdata, - TaskPriority priority, - TaskIsolation task_isolation); +TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority); /* Background: always run tasks in a background thread, never immediately * execute them. For running background jobs. */ -TaskPool *BLI_task_pool_create_background(void *userdata, - TaskPriority priority, - TaskIsolation task_isolation); +TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority); /* Background Serial: run tasks one after the other in the background, * without parallelization between the tasks. */ @@ -125,9 +87,7 @@ TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority pr * as threads can't immediately start working. But it can be used if the data * structures the threads operate on are not fully initialized until all tasks * are created. */ -TaskPool *BLI_task_pool_create_suspended(void *userdata, - TaskPriority priority, - TaskIsolation task_isolation); +TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority); /* No threads: immediately executes tasks on the same thread. For debugging. */ TaskPool *BLI_task_pool_create_no_threads(void *userdata); @@ -365,6 +325,36 @@ struct TaskNode *BLI_task_graph_node_create(struct TaskGraph *task_graph, bool BLI_task_graph_node_push_work(struct TaskNode *task_node); void BLI_task_graph_edge_create(struct TaskNode *from_node, struct TaskNode *to_node); +/* Task Isolation + * + * Task isolation helps avoid unexpected task scheduling decisions that can lead to bugs if wrong + * assumptions were made. Typically that happens when doing "nested threading", i.e. one thread + * schedules a bunch of main-tasks and those spawn new sub-tasks. + * + * What can happen is that when a main-task waits for its sub-tasks to complete on other threads, + * another main-task is scheduled within the already running main-task. Generally, this is good, + * because it leads to better performance. However, sometimes code (often unintentionally) makes + * the assumption that at most one main-task runs on a thread at a time. + * + * The bugs often show themselves in two ways: + * - Deadlock, when a main-task holds a mutex while waiting for its sub-tasks to complete. + * - Data corruption, when a main-task makes wrong assumptions about a thread-local variable. + * + * Task isolation can avoid these bugs by making sure that a main-task does not start executing + * another main-task while waiting for its sub-tasks. More precisely, a function that runs in an + * isolated region is only allowed to run sub-tasks that were spawned in the same isolated region. + * + * Unfortunately, incorrect use of task isolation can lead to deadlocks itself. This can happen + * when threading primitives are used that separate spawning tasks from executing them. The problem + * occurs when a task is spawned in one isolated region while the tasks are waited for in another + * isolated region. In this setup, the thread that is waiting for the spawned tasks to complete + * cannot run the tasks itself. On a single thread, that causes a deadlock already. When there are + * multiple threads, another thread will typically run the task and avoid the deadlock. However, if + * this situation happens on all threads at the same time, all threads will deadlock. This happened + * in T88598. + */ +void BLI_task_isolate(void (*func)(void *userdata), void *userdata); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh index 8e963c958b2..5f5a17f6b58 100644 --- a/source/blender/blenlib/BLI_task.hh +++ b/source/blender/blenlib/BLI_task.hh @@ -31,6 +31,7 @@ # include <tbb/blocked_range.h> # include <tbb/parallel_for.h> # include <tbb/parallel_for_each.h> +# include <tbb/task_arena.h> # ifdef WIN32 /* We cannot keep this defined, since other parts of the code deal with this on their own, leading * to multiple define warnings unless we un-define this, however we can only undefine this if we @@ -44,7 +45,7 @@ #include "BLI_index_range.hh" #include "BLI_utildefines.h" -namespace blender { +namespace blender::threading { template<typename Range, typename Function> void parallel_for_each(Range &range, const Function &function) @@ -75,4 +76,14 @@ void parallel_for(IndexRange range, int64_t grain_size, const Function &function #endif } -} // namespace blender +/** See #BLI_task_isolate for a description of what isolating a task means. */ +template<typename Function> void isolate_task(const Function &function) +{ +#ifdef WITH_TBB + tbb::this_task_arena::isolate(function); +#else + function(); +#endif +} + +} // namespace blender::threading diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index 1c6e6ffe578..0bf117df43b 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -785,6 +785,7 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); * To use after the enum declaration. */ /* If any enumerator `C` is set to say `A|B`, then `C` would be the max enum value. */ # define ENUM_OPERATORS(_enum_type, _max_enum_value) \ + extern "C++" { \ inline constexpr _enum_type operator|(_enum_type a, _enum_type b) \ { \ return static_cast<_enum_type>(static_cast<int>(a) | b); \ @@ -804,7 +805,8 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); inline _enum_type &operator&=(_enum_type &a, _enum_type b) \ { \ return a = static_cast<_enum_type>(static_cast<int>(a) & b); \ - } + } \ + } /* extern "C++" */ #else /* Output nothing. */ diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index f771d26baab..0953e3f1946 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -105,7 +105,7 @@ int closedir(DIR *dp); const char *dirname(char *path); /* Windows utility functions. */ -void BLI_windows_register_blend_extension(const bool background); +bool BLI_windows_register_blend_extension(const bool background); void BLI_windows_get_default_root_dir(char *root_dir); int BLI_windows_get_executable_dir(char *str); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e04f3c1b19d..677df9db026 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -20,7 +20,7 @@ set(INC . - # ../blenkernel # dont add this back! + # ../blenkernel # don't add this back! ../makesdna ../../../intern/atomic ../../../intern/eigen @@ -103,6 +103,7 @@ set(SRC intern/math_rotation.c intern/math_solvers.c intern/math_statistics.c + intern/math_time.c intern/math_vec.cc intern/math_vector.c intern/math_vector_inline.c @@ -241,6 +242,7 @@ set(SRC BLI_math_rotation.h BLI_math_solvers.h BLI_math_statistics.h + BLI_math_time.h BLI_math_vector.h BLI_memarena.h BLI_memblock.h @@ -419,6 +421,7 @@ if(WITH_GTESTS) tests/BLI_math_matrix_test.cc tests/BLI_math_rotation_test.cc tests/BLI_math_solvers_test.cc + tests/BLI_math_time_test.cc tests/BLI_math_vector_test.cc tests/BLI_memiter_test.cc tests/BLI_memory_utils_test.cc diff --git a/source/blender/blenlib/intern/BLI_dynstr.c b/source/blender/blenlib/intern/BLI_dynstr.c index 7b25fecfa45..5255e8b9902 100644 --- a/source/blender/blenlib/intern/BLI_dynstr.c +++ b/source/blender/blenlib/intern/BLI_dynstr.c @@ -166,7 +166,7 @@ void BLI_dynstr_vappendf(DynStr *__restrict ds, const char *__restrict format, v message = MEM_callocN(sizeof(char) * len, "BLI_dynstr_appendf"); } - /* cant reuse the same args, so work on a copy */ + /* can't reuse the same args, so work on a copy */ va_copy(args_cpy, args); retval = vsnprintf(message, len, format, args_cpy); va_end(args_cpy); diff --git a/source/blender/blenlib/intern/array_store.c b/source/blender/blenlib/intern/array_store.c index b694f9c7fc0..250915383cf 100644 --- a/source/blender/blenlib/intern/array_store.c +++ b/source/blender/blenlib/intern/array_store.c @@ -192,7 +192,7 @@ /* Disallow chunks bigger than the regular chunk size scaled by this value * note: must be at least 2! - * however, this code runs wont run in tests unless its ~1.1 ugh. + * however, this code runs won't run in tests unless it's ~1.1 ugh. * so lower only to check splitting works. */ # define BCHUNK_SIZE_MAX_MUL 2 diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc index 9444d1a29cb..eb3e64c49e6 100644 --- a/source/blender/blenlib/intern/delaunay_2d.cc +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -226,6 +226,8 @@ template<typename Arith_t> struct CDTFace { int visit_index{0}; /** Marks this face no longer used. */ bool deleted{false}; + /** Marks this face as part of a hole. */ + bool hole{false}; CDTFace() = default; }; @@ -481,9 +483,9 @@ template<typename T> void cdt_draw(const std::string &label, const CDTArrangemen * This is just for developer debugging anyway, and should never be called in production Blender. */ # ifdef _WIN32 - const char *drawfile = "./debug_draw.html"; + const char *drawfile = "./cdt_debug_draw.html"; # else - const char *drawfile = "/tmp/debug_draw.html"; + const char *drawfile = "/tmp/cdt_debug_draw.html"; # endif constexpr int max_draw_width = 1800; constexpr int max_draw_height = 1600; @@ -2364,9 +2366,6 @@ template<typename T> void remove_non_constraint_edges_leave_valid_bmesh(CDT_stat template<typename T> void remove_outer_edges_until_constraints(CDT_state<T> *cdt_state) { - // LinkNode *fstack = NULL; - // SymEdge *se, *se_start; - // CDTFace *f, *fsym; int visit = ++cdt_state->visit_count; cdt_state->cdt.outer_face->visit_index = visit; @@ -2415,6 +2414,137 @@ template<typename T> void remove_outer_edges_until_constraints(CDT_state<T> *cdt } } +template<typename T> void remove_faces_in_holes(CDT_state<T> *cdt_state) +{ + CDTArrangement<T> *cdt = &cdt_state->cdt; + for (int i : cdt->faces.index_range()) { + CDTFace<T> *f = cdt->faces[i]; + if (!f->deleted && f->hole) { + f->deleted = true; + SymEdge<T> *se = f->symedge; + SymEdge<T> *se_start = se; + SymEdge<T> *se_next = nullptr; + do { + BLI_assert(se != nullptr); + se_next = se->next; /* In case we delete this edge. */ + if (se->edge && !is_constrained_edge(se->edge)) { + /* Invalidate one half of this edge. The other has will be or has been + * handled with the adjacent triangle is processed: it should be part of the same hole. + */ + se->next = nullptr; + } + se = se_next; + } while (se != se_start); + } + } +} + +/** + * Set the hole member of each CDTFace to true for each face that is detected to be part of a + * hole. A hole face is define as one for which, when a ray is shot from a point inside the face + * to infinity, it crosses an even number of constraint edges. We'll choose a ray direction that + * is extremely unlikely to exactly superimpose some edge, so avoiding the need to be careful + * about such overlaps. + * + * To improve performance, we gather together faces that should have the same winding number, and + * only shoot rays once. + */ +template<typename T> void detect_holes(CDT_state<T> *cdt_state) +{ + CDTArrangement<T> *cdt = &cdt_state->cdt; + + /* Make it so that each face with the same visit_index is connected through a path of + * non-constraint edges. */ + Vector<CDTFace<T> *> fstack; + Vector<CDTFace<T> *> region_rep_face; + for (int i : cdt->faces.index_range()) { + cdt->faces[i]->visit_index = -1; + } + int cur_region = -1; + cdt->outer_face->visit_index = -2; /* Don't visit this one. */ + for (int i : cdt->faces.index_range()) { + CDTFace<T> *f = cdt->faces[i]; + if (!f->deleted && f->symedge && f->visit_index == -1) { + fstack.append(f); + ++cur_region; + region_rep_face.append(f); + while (!fstack.is_empty()) { + CDTFace<T> *f = fstack.pop_last(); + if (f->visit_index != -1) { + continue; + } + f->visit_index = cur_region; + SymEdge<T> *se_start = f->symedge; + SymEdge<T> *se = se_start; + do { + if (se->edge && !is_constrained_edge(se->edge)) { + CDTFace<T> *fsym = sym(se)->face; + if (fsym && !fsym->deleted && fsym->visit_index == -1) { + fstack.append(fsym); + } + } + se = se->next; + } while (se != se_start); + } + } + } + cdt_state->visit_count = ++cur_region; /* Good start for next use of visit_count. */ + + /* Now get hole status for each region_rep_face. */ + + /* Pick a ray end almost certain to be outside everything and in direction + * that is unlikely to hit a vertex or overlap an edge exactly. */ + FatCo<T> ray_end; + ray_end.exact = vec2<T>(123456, 654321); + for (int i : region_rep_face.index_range()) { + CDTFace<T> *f = region_rep_face[i]; + FatCo<T> mid; + mid.exact[0] = (f->symedge->vert->co.exact[0] + f->symedge->next->vert->co.exact[0] + + f->symedge->next->next->vert->co.exact[0]) / + 3; + mid.exact[1] = (f->symedge->vert->co.exact[1] + f->symedge->next->vert->co.exact[1] + + f->symedge->next->next->vert->co.exact[1]) / + 3; + int hits = 0; + /* TODO: Use CDT data structure here to greatly reduce search for intersections! */ + for (const CDTEdge<T> *e : cdt->edges) { + if (!is_deleted_edge(e) && is_constrained_edge(e)) { + if (e->symedges[0].face->visit_index == e->symedges[1].face->visit_index) { + continue; /* Don't count hits on edges between faces in same region. */ + } + auto isect = vec2<T>::isect_seg_seg(ray_end.exact, + mid.exact, + e->symedges[0].vert->co.exact, + e->symedges[1].vert->co.exact); + switch (isect.kind) { + case vec2<T>::isect_result::LINE_LINE_CROSS: { + hits++; + break; + } + case vec2<T>::isect_result::LINE_LINE_EXACT: + case vec2<T>::isect_result::LINE_LINE_NONE: + case vec2<T>::isect_result::LINE_LINE_COLINEAR: + break; + } + } + } + f->hole = (hits % 2) == 0; + } + + /* Finally, propagate hole status to all holes of a region. */ + for (int i : cdt->faces.index_range()) { + CDTFace<T> *f = cdt->faces[i]; + int region = f->visit_index; + if (region < 0) { + continue; + } + CDTFace<T> *f_region_rep = region_rep_face[region]; + if (i >= 0) { + f->hole = f_region_rep->hole; + } + } +} + /** * Remove edges and merge faces to get desired output, as per options. * \note the cdt cannot be further changed after this. @@ -2439,6 +2569,13 @@ void prepare_cdt_for_output(CDT_state<T> *cdt_state, const CDT_output_type outpu } } + bool need_holes = output_type == CDT_INSIDE_WITH_HOLES || + output_type == CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES; + + if (need_holes) { + detect_holes(cdt_state); + } + if (output_type == CDT_CONSTRAINTS) { remove_non_constraint_edges(cdt_state); } @@ -2448,6 +2585,14 @@ void prepare_cdt_for_output(CDT_state<T> *cdt_state, const CDT_output_type outpu else if (output_type == CDT_INSIDE) { remove_outer_edges_until_constraints(cdt_state); } + else if (output_type == CDT_INSIDE_WITH_HOLES) { + remove_outer_edges_until_constraints(cdt_state); + remove_faces_in_holes(cdt_state); + } + else if (output_type == CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES) { + remove_non_constraint_edges_leave_valid_bmesh(cdt_state); + remove_faces_in_holes(cdt_state); + } } template<typename T> diff --git a/source/blender/blenlib/intern/kdtree_impl.h b/source/blender/blenlib/intern/kdtree_impl.h index c92dc2e95a3..2a0e8b3ec68 100644 --- a/source/blender/blenlib/intern/kdtree_impl.h +++ b/source/blender/blenlib/intern/kdtree_impl.h @@ -882,7 +882,7 @@ static void deduplicate_recursive(const struct DeDuplicateParams *p, uint i) * although it can still be used as a target. * \returns The number of merges found (includes any merges already in the \a duplicates array). * - * \note Merging is always a single step (target indices wont be marked for merging). + * \note Merging is always a single step (target indices won't be marked for merging). */ int BLI_kdtree_nd_(calc_duplicates_fast)(const KDTree *tree, const float range, diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 508de506ae8..de50ae27b94 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -3344,6 +3344,13 @@ float closest_to_ray_v3(float r_close[3], const float ray_dir[3]) { float h[3], lambda; + + if (UNLIKELY(is_zero_v3(ray_dir))) { + lambda = 0.0f; + copy_v3_v3(r_close, ray_orig); + return lambda; + } + sub_v3_v3v3(h, p, ray_orig); lambda = dot_v3v3(ray_dir, h) / dot_v3v3(ray_dir, ray_dir); madd_v3_v3v3fl(r_close, ray_orig, ray_dir, lambda); @@ -4467,7 +4474,7 @@ void interp_weights_poly_v2(float *w, float v[][2], const int n, const float co[ d_curr = d_next; DIR_V2_SET(&d_next, v_next, co); ht = mean_value_half_tan_v2_db(&d_curr, &d_next); - w[i_curr] = (float)((ht_prev + ht) / d_curr.len); + w[i_curr] = (d_curr.len == 0.0) ? 0.0f : (float)((ht_prev + ht) / d_curr.len); totweight += w[i_curr]; /* step */ @@ -4904,8 +4911,8 @@ void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x void planes_from_projmat(const float mat[4][4], float left[4], float right[4], - float top[4], float bottom[4], + float top[4], float near[4], float far[4]) { @@ -6211,6 +6218,19 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3], return dot_v3v3(cross_a, cross_b) > 0.0f; } +bool is_quad_flip_v3_first_third_fast_with_normal(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const float normal[3]) +{ + float dir_v3v1[3], tangent[3]; + sub_v3_v3v3(dir_v3v1, v3, v1); + cross_v3_v3v3(tangent, dir_v3v1, normal); + const float dot = dot_v3v3(v1, tangent); + return (dot_v3v3(v4, tangent) >= dot) || (dot_v3v3(v2, tangent) <= dot); +} + /** * Return the value which the distance between points will need to be scaled by, * to define a handle, given both points are on a perfect circle. @@ -6229,7 +6249,7 @@ float cubic_tangent_factor_circle_v3(const float tan_l[3], const float tan_r[3]) const float tan_dot = dot_v3v3(tan_l, tan_r); if (tan_dot > 1.0f - eps) { - /* no angle difference (use fallback, length wont make any difference) */ + /* no angle difference (use fallback, length won't make any difference) */ return (1.0f / 3.0f) * 0.75f; } if (tan_dot < -1.0f + eps) { diff --git a/source/blender/blenlib/intern/math_geom_inline.c b/source/blender/blenlib/intern/math_geom_inline.c index 23c351026f2..655d3fcc4c0 100644 --- a/source/blender/blenlib/intern/math_geom_inline.c +++ b/source/blender/blenlib/intern/math_geom_inline.c @@ -244,10 +244,13 @@ MINLINE int min_axis_v3(const float vec[3]) } /** - * Simple method to find how many tri's we need when we already know the corner+poly count. + * Simple function to either: + * - Calculate how many triangles needed from the total number of polygons + loops. + * - Calculate the first triangle index from the polygon index & that polygons loop-start. * - * \param poly_count: The number of ngon's/tris (1-2 sided faces will give incorrect results) - * \param corner_count: also known as loops in BMesh/DNA + * \param poly_count: The number of polygons or polygon-index + * (3+ sided faces, 1-2 sided give incorrect results). + * \param corner_count: The number of corners (also called loop-index). */ MINLINE int poly_to_tri_count(const int poly_count, const int corner_count) { diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index 57fe99ce019..469cd573372 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -1924,6 +1924,31 @@ void eulO_to_gimbal_axis(float gmat[3][3], const float eul[3], const short order gmat[R->axis[2]][R->axis[2]] = 1; } +void add_eul_euleul(float r_eul[3], float a[3], float b[3], const short order) +{ + float quat[4], quat_b[4]; + + eulO_to_quat(quat, a, order); + eulO_to_quat(quat_b, b, order); + + mul_qt_qtqt(quat, quat_b, quat); + + quat_to_eulO(r_eul, order, quat); +} + +void sub_eul_euleul(float r_eul[3], float a[3], float b[3], const short order) +{ + float quat[4], quat_b[4]; + + eulO_to_quat(quat, a, order); + eulO_to_quat(quat_b, b, order); + + invert_qt_normalized(quat_b); + mul_qt_qtqt(quat, quat_b, quat); + + quat_to_eulO(r_eul, order, quat); +} + /******************************* Dual Quaternions ****************************/ /** diff --git a/source/blender/blenlib/intern/math_time.c b/source/blender/blenlib/intern/math_time.c new file mode 100644 index 00000000000..b85de7817dd --- /dev/null +++ b/source/blender/blenlib/intern/math_time.c @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include "BLI_math.h" + +/** Explode given time value expressed in seconds, into a set of days, hours, minutes, seconds + * and/or milliseconds (depending on which return parameters are not NULL). + * + * \note The smallest given return parameter will get the potential fractional remaining time + * value. E.g. if you give `seconds=90.0` and do not pass `r_seconds` and `r_milliseconds`, + * `r_minutes` will be set to `1.5`. + */ +void BLI_math_time_seconds_decompose(double seconds, + double *r_days, + double *r_hours, + double *r_minutes, + double *r_seconds, + double *r_milliseconds) +{ + BLI_assert(r_days != NULL || r_hours != NULL || r_minutes != NULL || r_seconds != NULL || + r_milliseconds != NULL); + + if (r_days != NULL) { + seconds = modf(seconds / SECONDS_IN_DAY, r_days) * SECONDS_IN_DAY; + } + if (r_hours != NULL) { + seconds = modf(seconds / SECONDS_IN_HOUR, r_hours) * SECONDS_IN_HOUR; + } + if (r_minutes != NULL) { + seconds = modf(seconds / SECONDS_IN_MINUTE, r_minutes) * SECONDS_IN_MINUTE; + } + if (r_seconds != NULL) { + seconds = modf(seconds, r_seconds); + } + if (r_milliseconds != NULL) { + *r_milliseconds = seconds / SECONDS_IN_MILLISECONDS; + } + else if (r_seconds != NULL) { + *r_seconds += seconds; + } + else if (r_minutes != NULL) { + *r_minutes += seconds / SECONDS_IN_MINUTE; + } + else if (r_hours != NULL) { + *r_hours += seconds / SECONDS_IN_HOUR; + } + else if (r_days != NULL) { + *r_days = seconds / SECONDS_IN_DAY; + } +} diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index fb7b96fde78..35dfe421cf0 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -657,10 +657,13 @@ void angle_poly_v3(float *angles, const float *verts[3], int len) */ void project_v2_v2v2(float out[2], const float p[2], const float v_proj[2]) { - const float mul = dot_v2v2(p, v_proj) / dot_v2v2(v_proj, v_proj); + if (UNLIKELY(is_zero_v2(v_proj))) { + zero_v2(out); + return; + } - out[0] = mul * v_proj[0]; - out[1] = mul * v_proj[1]; + const float mul = dot_v2v2(p, v_proj) / dot_v2v2(v_proj, v_proj); + mul_v2_v2fl(out, v_proj, mul); } /** @@ -668,20 +671,24 @@ void project_v2_v2v2(float out[2], const float p[2], const float v_proj[2]) */ void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3]) { - const float mul = dot_v3v3(p, v_proj) / dot_v3v3(v_proj, v_proj); + if (UNLIKELY(is_zero_v3(v_proj))) { + zero_v3(out); + return; + } - out[0] = mul * v_proj[0]; - out[1] = mul * v_proj[1]; - out[2] = mul * v_proj[2]; + const float mul = dot_v3v3(p, v_proj) / dot_v3v3(v_proj, v_proj); + mul_v3_v3fl(out, v_proj, mul); } void project_v3_v3v3_db(double out[3], const double p[3], const double v_proj[3]) { - const double mul = dot_v3v3_db(p, v_proj) / dot_v3v3_db(v_proj, v_proj); + if (UNLIKELY(is_zero_v3_db(v_proj))) { + zero_v3_db(out); + return; + } - out[0] = mul * v_proj[0]; - out[1] = mul * v_proj[1]; - out[2] = mul * v_proj[2]; + const double mul = dot_v3v3_db(p, v_proj) / dot_v3v3_db(v_proj, v_proj); + mul_v3_v3db_db(out, v_proj, mul); } /** @@ -690,10 +697,9 @@ void project_v3_v3v3_db(double out[3], const double p[3], const double v_proj[3] void project_v2_v2v2_normalized(float out[2], const float p[2], const float v_proj[2]) { BLI_ASSERT_UNIT_V2(v_proj); - const float mul = dot_v2v2(p, v_proj); - out[0] = mul * v_proj[0]; - out[1] = mul * v_proj[1]; + const float mul = dot_v2v2(p, v_proj); + mul_v2_v2fl(out, v_proj, mul); } /** @@ -702,11 +708,9 @@ void project_v2_v2v2_normalized(float out[2], const float p[2], const float v_pr void project_v3_v3v3_normalized(float out[3], const float p[3], const float v_proj[3]) { BLI_ASSERT_UNIT_V3(v_proj); - const float mul = dot_v3v3(p, v_proj); - out[0] = mul * v_proj[0]; - out[1] = mul * v_proj[1]; - out[2] = mul * v_proj[2]; + const float mul = dot_v3v3(p, v_proj); + mul_v3_v3fl(out, v_proj, mul); } /** @@ -725,37 +729,31 @@ void project_v3_v3v3_normalized(float out[3], const float p[3], const float v_pr void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3]) { const float mul = dot_v3v3(p, v_plane) / dot_v3v3(v_plane, v_plane); - - out[0] = p[0] - (mul * v_plane[0]); - out[1] = p[1] - (mul * v_plane[1]); - out[2] = p[2] - (mul * v_plane[2]); + /* out[x] = p[x] - (mul * v_plane[x]) */ + madd_v3_v3v3fl(out, p, v_plane, -mul); } void project_plane_v2_v2v2(float out[2], const float p[2], const float v_plane[2]) { const float mul = dot_v2v2(p, v_plane) / dot_v2v2(v_plane, v_plane); - - out[0] = p[0] - (mul * v_plane[0]); - out[1] = p[1] - (mul * v_plane[1]); + /* out[x] = p[x] - (mul * v_plane[x]) */ + madd_v2_v2v2fl(out, p, v_plane, -mul); } void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const float v_plane[3]) { BLI_ASSERT_UNIT_V3(v_plane); const float mul = dot_v3v3(p, v_plane); - - out[0] = p[0] - (mul * v_plane[0]); - out[1] = p[1] - (mul * v_plane[1]); - out[2] = p[2] - (mul * v_plane[2]); + /* out[x] = p[x] - (mul * v_plane[x]) */ + madd_v3_v3v3fl(out, p, v_plane, -mul); } void project_plane_normalized_v2_v2v2(float out[2], const float p[2], const float v_plane[2]) { BLI_ASSERT_UNIT_V2(v_plane); const float mul = dot_v2v2(p, v_plane); - - out[0] = p[0] - (mul * v_plane[0]); - out[1] = p[1] - (mul * v_plane[1]); + /* out[x] = p[x] - (mul * v_plane[x]) */ + madd_v2_v2v2fl(out, p, v_plane, -mul); } /* project a vector on a plane defined by normal and a plane point p */ @@ -767,9 +765,8 @@ void project_v3_plane(float out[3], const float plane_no[3], const float plane_c sub_v3_v3v3(vector, out, plane_co); mul = dot_v3v3(vector, plane_no) / len_squared_v3(plane_no); - mul_v3_v3fl(vector, plane_no, mul); - - sub_v3_v3(out, vector); + /* out[x] = out[x] - (mul * plane_no[x]) */ + madd_v3_v3fl(out, plane_no, -mul); } /* Returns a vector bisecting the angle at b formed by a, b and c */ @@ -802,24 +799,18 @@ void bisect_v3_v3v3v3(float r[3], const float a[3], const float b[3], const floa */ void reflect_v3_v3v3(float out[3], const float v[3], const float normal[3]) { - const float dot2 = 2.0f * dot_v3v3(v, normal); - BLI_ASSERT_UNIT_V3(normal); - - out[0] = v[0] - (dot2 * normal[0]); - out[1] = v[1] - (dot2 * normal[1]); - out[2] = v[2] - (dot2 * normal[2]); + const float dot2 = 2.0f * dot_v3v3(v, normal); + /* out[x] = v[x] - (dot2 * normal[x]) */ + madd_v3_v3v3fl(out, v, normal, -dot2); } void reflect_v3_v3v3_db(double out[3], const double v[3], const double normal[3]) { + BLI_ASSERT_UNIT_V3_DB(normal); const double dot2 = 2.0 * dot_v3v3_db(v, normal); - - /* BLI_ASSERT_UNIT_V3_DB(normal); this assert is not known? */ - - out[0] = v[0] - (dot2 * normal[0]); - out[1] = v[1] - (dot2 * normal[1]); - out[2] = v[2] - (dot2 * normal[2]); + /* out[x] = v[x] - (dot2 * normal[x]) */ + madd_v3_v3v3db_db(out, v, normal, -dot2); } /** diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index ead354c2d87..db9ece81c59 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -722,6 +722,13 @@ MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], floa r[2] = a[2] + b[2] * f; } +MINLINE void madd_v3_v3v3db_db(double r[3], const double a[3], const double b[3], double f) +{ + r[0] = a[0] + b[0] * f; + r[1] = a[1] + b[1] * f; + r[2] = a[2] + b[2] * f; +} + MINLINE void madd_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float c[3]) { r[0] = a[0] + b[0] * c[0]; @@ -1282,6 +1289,21 @@ MINLINE bool is_zero_v4(const float v[4]) return (v[0] == 0.0f && v[1] == 0.0f && v[2] == 0.0f && v[3] == 0.0f); } +MINLINE bool is_zero_v2_db(const double v[2]) +{ + return (v[0] == 0.0 && v[1] == 0.0); +} + +MINLINE bool is_zero_v3_db(const double v[3]) +{ + return (v[0] == 0.0 && v[1] == 0.0 && v[2] == 0.0); +} + +MINLINE bool is_zero_v4_db(const double v[4]) +{ + return (v[0] == 0.0 && v[1] == 0.0 && v[2] == 0.0 && v[3] == 0.0); +} + MINLINE bool is_one_v3(const float v[3]) { return (v[0] == 1.0f && v[1] == 1.0f && v[2] == 1.0f); diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 25aeae519c1..9f7824a0029 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -1406,9 +1406,9 @@ static int find_cell_for_point_near_edge(mpq3 p, int dummy_index = p_sorted_dummy - sorted_tris.begin(); int prev_tri = (dummy_index == 0) ? sorted_tris[sorted_tris.size() - 1] : sorted_tris[dummy_index - 1]; - int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] : - sorted_tris[dummy_index + 1]; if (dbg_level > 0) { + int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] : + sorted_tris[dummy_index + 1]; std::cout << "prev tri to dummy = " << prev_tri << "; next tri to dummy = " << next_tri << "\n"; } @@ -1983,7 +1983,7 @@ static void populate_comp_bbs(const Vector<Vector<int>> &components, * absolute value of any coordinate. Do it first per component, * then get the overall max. */ Array<double> max_abs(components.size(), 0.0); - parallel_for(components.index_range(), comp_grainsize, [&](IndexRange comp_range) { + threading::parallel_for(components.index_range(), comp_grainsize, [&](IndexRange comp_range) { for (int c : comp_range) { BoundingBox &bb = comp_bb[c]; double &maxa = max_abs[c]; @@ -2691,7 +2691,7 @@ static IMesh raycast_tris_boolean(const IMesh &tm, tbb::spin_mutex mtx; # endif const int grainsize = 256; - parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) { + threading::parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) { Array<float> in_shape(nshapes, 0); Array<int> winding(nshapes, 0); for (int t : range) { @@ -3391,7 +3391,7 @@ static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out, } /* For now: need plane normals for all triangles. */ const int grainsize = 1024; - parallel_for(tm_out.face_index_range(), grainsize, [&](IndexRange range) { + threading::parallel_for(tm_out.face_index_range(), grainsize, [&](IndexRange range) { for (int i : range) { Face *tri = tm_out.face(i); tri->populate_plane(false); diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index cb6cce30f92..f3c348b2b44 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -180,7 +180,7 @@ void BLI_path_normalize(const char *relabase, char *path) else { if (path[0] == '/' && path[1] == '/') { if (path[2] == '\0') { - return; /* path is "//" - cant clean it */ + return; /* path is "//" - can't clean it */ } path = path + 2; /* leave the initial "//" untouched */ } @@ -236,7 +236,7 @@ void BLI_path_normalize(const char *relabase, char *path) } else { /* support for odd paths: eg /../home/me --> /home/me - * this is a valid path in blender but we cant handle this the usual way below + * this is a valid path in blender but we can't handle this the usual way below * simply strip this prefix then evaluate the path as usual. * pythons os.path.normpath() does this */ @@ -1646,8 +1646,8 @@ bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filenam /** * Converts `/foo/bar.txt` to `/foo/` and `bar.txt` * - * - Wont change \a string. - * - Wont create any directories. + * - Won't change \a string. + * - Won't create any directories. * - Doesn't use CWD, or deal with relative paths. * - Only fill's in \a dir and \a file when they are non NULL. */ @@ -2013,9 +2013,9 @@ void BLI_path_slash_native(char *path) { #ifdef WIN32 if (path && BLI_strnlen(path, 3) > 2) { - BLI_str_replace_char(path + 2, '/', '\\'); + BLI_str_replace_char(path + 2, ALTSEP, SEP); } #else - BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), '\\', '/'); + BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP); #endif } diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c index 98fa5c872b0..7425bab885c 100644 --- a/source/blender/blenlib/intern/polyfill_2d_beautify.c +++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c @@ -320,7 +320,7 @@ static void polyedge_rotate(struct HalfEdge *edges, struct HalfEdge *e) * The intention is that this calculates the output of #BLI_polyfill_calc * \note assumes the \a coords form a boundary, * so any edges running along contiguous (wrapped) indices, - * are ignored since the edges wont share 2 faces. + * are ignored since the edges won't share 2 faces. */ void BLI_polyfill_beautify(const float (*coords)[2], const uint coords_tot, diff --git a/source/blender/blenlib/intern/storage_apple.mm b/source/blender/blenlib/intern/storage_apple.mm index 2a4bbffa60e..8af98d61ecb 100644 --- a/source/blender/blenlib/intern/storage_apple.mm +++ b/source/blender/blenlib/intern/storage_apple.mm @@ -24,10 +24,15 @@ */ #import <Foundation/Foundation.h> +#include <string> +#include <sys/xattr.h> #include "BLI_fileops.h" #include "BLI_path_util.h" +/* Extended file attribute used by OneDrive to mark placeholder files. */ +static const char *ONEDRIVE_RECALLONOPEN_ATTRIBUTE = "com.microsoft.OneDrive.RecallOnOpen"; + /** * \param r_targetpath: Buffer for the target path an alias points to. * \return Whether the file at the input path is an alias. @@ -66,6 +71,67 @@ bool BLI_file_alias_target(const char *filepath, char r_targetpath[FILE_MAXDIR]) return true; } +/** + * Checks if the given string of listxattr() attributes contains a specific attribute. + * + * \param attributes: a string of null-terminated listxattr() attributes. + * \param search_attribute: the attribute to search for. + * \return 'true' when the attribute is found, otherwise 'false'. + */ +static bool find_attribute(const std::string &attributes, const char *search_attribute) +{ + /* Attributes is a list of consecutive null-terminated strings. */ + const char *end = attributes.data() + attributes.size(); + for (const char *item = attributes.data(); item < end; item += strlen(item) + 1) { + if (STREQ(item, search_attribute)) { + return true; + } + } + + return false; +} + +/** + * Checks if the file is merely a placeholder for a OneDrive file that hasn't yet been downloaded. + * + * \param path: the path of the file. + * \return 'true' when the file is a OneDrive placeholder, otherwise 'false'. + */ +static bool test_onedrive_file_is_placeholder(const char *path) +{ + /* Note: Currently only checking for the "com.microsoft.OneDrive.RecallOnOpen" extended file + * attribute. In theory this attribute can also be set on files that aren't located inside a + * OneDrive folder. Maybe additional checks are required? */ + + /* Get extended file attributes */ + ssize_t size = listxattr(path, nullptr, 0, XATTR_NOFOLLOW); + if (size < 1) { + return false; + } + + std::string attributes(size, '\0'); + size = listxattr(path, attributes.data(), size, XATTR_NOFOLLOW); + /* In case listxattr() has failed the second time it's called. */ + if (size < 1) { + return false; + } + + /* Check for presence of 'com.microsoft.OneDrive.RecallOnOpen' attribute. */ + return find_attribute(attributes, ONEDRIVE_RECALLONOPEN_ATTRIBUTE); +} + +/** + * Checks if the file is marked as offline and not immediately available. + * + * \param path: the path of the file. + * \return 'true' when the file is a placeholder, otherwise 'false'. + */ +static bool test_file_is_offline(const char *path) +{ + /* Logic for additional cloud storage providers could be added in the future. */ + return test_onedrive_file_is_placeholder(path); +} + eFileAttributes BLI_file_attributes(const char *path) { int ret = 0; @@ -76,15 +142,28 @@ eFileAttributes BLI_file_attributes(const char *path) const NSURL *fileURL = [[NSURL alloc] initFileURLWithFileSystemRepresentation:path isDirectory:NO relativeToURL:nil]; - NSArray *resourceKeys = - @[ NSURLIsAliasFileKey, NSURLIsHiddenKey, NSURLIsReadableKey, NSURLIsWritableKey ]; + + /* Querying NSURLIsReadableKey and NSURLIsWritableKey keys for OneDrive placeholder files + * triggers their unwanted download. */ + NSArray *resourceKeys = nullptr; + const bool is_offline = test_file_is_offline(path); + + if (is_offline) { + resourceKeys = @[ NSURLIsAliasFileKey, NSURLIsHiddenKey ]; + } + else { + resourceKeys = + @[ NSURLIsAliasFileKey, NSURLIsHiddenKey, NSURLIsReadableKey, NSURLIsWritableKey ]; + } const NSDictionary *resourceKeyValues = [fileURL resourceValuesForKeys:resourceKeys error:nil]; const bool is_alias = [resourceKeyValues[(void)(@"@%"), NSURLIsAliasFileKey] boolValue]; const bool is_hidden = [resourceKeyValues[(void)(@"@%"), NSURLIsHiddenKey] boolValue]; - const bool is_readable = [resourceKeyValues[(void)(@"@%"), NSURLIsReadableKey] boolValue]; - const bool is_writable = [resourceKeyValues[(void)(@"@%"), NSURLIsWritableKey] boolValue]; + const bool is_readable = is_offline || + [resourceKeyValues[(void)(@"@%"), NSURLIsReadableKey] boolValue]; + const bool is_writable = is_offline || + [resourceKeyValues[(void)(@"@%"), NSURLIsWritableKey] boolValue]; if (is_alias) { ret |= FILE_ATTR_ALIAS; @@ -98,6 +177,9 @@ eFileAttributes BLI_file_attributes(const char *path) if (!is_readable) { ret |= FILE_ATTR_SYSTEM; } + if (is_offline) { + ret |= FILE_ATTR_OFFLINE; + } } return (eFileAttributes)ret; diff --git a/source/blender/blenlib/intern/task_graph.cc b/source/blender/blenlib/intern/task_graph.cc index 32450c16630..ff7d0ecb4c4 100644 --- a/source/blender/blenlib/intern/task_graph.cc +++ b/source/blender/blenlib/intern/task_graph.cc @@ -91,7 +91,7 @@ struct TaskNode { #ifdef WITH_TBB tbb::flow::continue_msg run(const tbb::flow::continue_msg UNUSED(input)) { - tbb::this_task_arena::isolate([this] { run_func(task_data); }); + run_func(task_data); return tbb::flow::continue_msg(); } #endif diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c index ee41c277b34..f67671c65e0 100644 --- a/source/blender/blenlib/intern/task_iterator.c +++ b/source/blender/blenlib/intern/task_iterator.c @@ -237,7 +237,7 @@ 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(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH); if (use_userdata_chunk) { userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks); @@ -442,7 +442,7 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, return; } - task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + 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 diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index d72674c1c00..6250c1b9986 100644 --- a/source/blender/blenlib/intern/task_pool.cc +++ b/source/blender/blenlib/intern/task_pool.cc @@ -22,7 +22,6 @@ #include <cstdlib> #include <memory> -#include <thread> #include <utility> #include "MEM_guardedalloc.h" @@ -156,7 +155,6 @@ enum TaskPoolType { struct TaskPool { TaskPoolType type; bool use_threads; - TaskIsolation task_isolation; ThreadMutex user_mutex; void *userdata; @@ -164,8 +162,6 @@ struct TaskPool { #ifdef WITH_TBB /* TBB task pool. */ TBBTaskGroup tbb_group; - /* This is used to detect a common way to accidentally create a deadlock with task isolation. */ - std::thread::id task_pool_create_thread_id; #endif volatile bool is_suspended; BLI_mempool *suspended_mempool; @@ -179,33 +175,9 @@ struct TaskPool { /* Execute task. */ void Task::operator()() const { -#ifdef WITH_TBB - if (pool->task_isolation == TASK_ISOLATION_ON) { - tbb::this_task_arena::isolate([this] { run(pool, taskdata); }); - return; - } -#endif run(pool, taskdata); } -static void assert_on_valid_thread(TaskPool *pool) -{ - /* TODO: Remove this `return` to enable the check. */ - return; -#ifdef DEBUG -# ifdef WITH_TBB - if (pool->task_isolation == TASK_ISOLATION_ON) { - const std::thread::id current_id = std::this_thread::get_id(); - /* This task pool is modified from different threads. To avoid deadlocks, `TASK_ISOLATION_OFF` - * has to be used. Task isolation can still be used in a more fine-grained way within the - * tasks, but should not be enabled for the entire task pool. */ - BLI_assert(pool->task_pool_create_thread_id == current_id); - } -# endif -#endif - UNUSED_VARS_NDEBUG(pool); -} - /* TBB Task Pool. * * Task pool using the TBB scheduler for tasks. When building without TBB @@ -391,10 +363,7 @@ static void background_task_pool_free(TaskPool *pool) /* Task Pool */ -static TaskPool *task_pool_create_ex(void *userdata, - TaskPoolType type, - TaskPriority priority, - TaskIsolation task_isolation) +static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPriority priority) { const bool use_threads = BLI_task_scheduler_num_threads() > 1 && type != TASK_POOL_NO_THREADS; @@ -410,11 +379,6 @@ static TaskPool *task_pool_create_ex(void *userdata, pool->type = type; pool->use_threads = use_threads; - pool->task_isolation = task_isolation; - -#ifdef WITH_TBB - pool->task_pool_create_thread_id = std::this_thread::get_id(); -#endif pool->userdata = userdata; BLI_mutex_init(&pool->user_mutex); @@ -437,9 +401,9 @@ static TaskPool *task_pool_create_ex(void *userdata, /** * Create a normal task pool. Tasks will be executed as soon as they are added. */ -TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority, TaskIsolation task_isolation) +TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority) { - return task_pool_create_ex(userdata, TASK_POOL_TBB, priority, task_isolation); + return task_pool_create_ex(userdata, TASK_POOL_TBB, priority); } /** @@ -454,11 +418,9 @@ TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority, TaskIsolat * 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(void *userdata, - TaskPriority priority, - TaskIsolation task_isolation) +TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority) { - return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority, task_isolation); + return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority); } /** @@ -466,11 +428,9 @@ TaskPool *BLI_task_pool_create_background(void *userdata, * 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(void *userdata, - TaskPriority priority, - TaskIsolation task_isolation) +TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority) { - return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority, task_isolation); + return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority); } /** @@ -479,8 +439,7 @@ TaskPool *BLI_task_pool_create_suspended(void *userdata, */ TaskPool *BLI_task_pool_create_no_threads(void *userdata) { - return task_pool_create_ex( - userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + return task_pool_create_ex(userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH); } /** @@ -489,7 +448,7 @@ TaskPool *BLI_task_pool_create_no_threads(void *userdata) */ TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority) { - return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority, TASK_ISOLATION_ON); + return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority); } void BLI_task_pool_free(TaskPool *pool) @@ -517,8 +476,6 @@ void BLI_task_pool_push(TaskPool *pool, bool free_taskdata, TaskFreeFunction freedata) { - assert_on_valid_thread(pool); - Task task(pool, run, taskdata, free_taskdata, freedata); switch (pool->type) { @@ -536,8 +493,6 @@ void BLI_task_pool_push(TaskPool *pool, void BLI_task_pool_work_and_wait(TaskPool *pool) { - assert_on_valid_thread(pool); - switch (pool->type) { case TASK_POOL_TBB: case TASK_POOL_TBB_SUSPENDED: diff --git a/source/blender/blenlib/intern/task_range.cc b/source/blender/blenlib/intern/task_range.cc index a27241e91dd..871d04c1f35 100644 --- a/source/blender/blenlib/intern/task_range.cc +++ b/source/blender/blenlib/intern/task_range.cc @@ -90,13 +90,11 @@ struct RangeTask { void operator()(const tbb::blocked_range<int> &r) const { - tbb::this_task_arena::isolate([this, r] { - TaskParallelTLS tls; - tls.userdata_chunk = userdata_chunk; - for (int i = r.begin(); i != r.end(); ++i) { - func(userdata, i, &tls); - } - }); + TaskParallelTLS tls; + tls.userdata_chunk = userdata_chunk; + for (int i = r.begin(); i != r.end(); ++i) { + func(userdata, i, &tls); + } } void join(const RangeTask &other) diff --git a/source/blender/blenlib/intern/task_scheduler.cc b/source/blender/blenlib/intern/task_scheduler.cc index b22334a5676..69117e9dc7e 100644 --- a/source/blender/blenlib/intern/task_scheduler.cc +++ b/source/blender/blenlib/intern/task_scheduler.cc @@ -28,6 +28,7 @@ #ifdef WITH_TBB /* Need to include at least one header to get the version define. */ # include <tbb/blocked_range.h> +# include <tbb/task_arena.h> # if TBB_INTERFACE_VERSION_MAJOR >= 10 # include <tbb/global_control.h> # define WITH_TBB_GLOBAL_CONTROL @@ -76,3 +77,12 @@ int BLI_task_scheduler_num_threads() { return task_scheduler_num_threads; } + +void BLI_task_isolate(void (*func)(void *userdata), void *userdata) +{ +#ifdef WITH_TBB + tbb::this_task_arena::isolate([&] { func(userdata); }); +#else + func(userdata); +#endif +} diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index 3aa61d1fec5..d40f665ba0d 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -67,10 +67,9 @@ static void register_blend_extension_failed(HKEY root, const bool background) if (!background) { MessageBox(0, "Could not register file extension.", "Blender error", MB_OK | MB_ICONERROR); } - TerminateProcess(GetCurrentProcess(), 1); } -void BLI_windows_register_blend_extension(const bool background) +bool BLI_windows_register_blend_extension(const bool background) { LONG lresult; HKEY hkey = 0; @@ -107,6 +106,7 @@ void BLI_windows_register_blend_extension(const bool background) lresult = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Classes", 0, KEY_ALL_ACCESS, &root); if (lresult != ERROR_SUCCESS) { register_blend_extension_failed(0, background); + return false; } } @@ -119,6 +119,7 @@ void BLI_windows_register_blend_extension(const bool background) } if (lresult != ERROR_SUCCESS) { register_blend_extension_failed(root, background); + return false; } lresult = RegCreateKeyEx(root, @@ -137,6 +138,7 @@ void BLI_windows_register_blend_extension(const bool background) } if (lresult != ERROR_SUCCESS) { register_blend_extension_failed(root, background); + return false; } lresult = RegCreateKeyEx(root, @@ -155,6 +157,7 @@ void BLI_windows_register_blend_extension(const bool background) } if (lresult != ERROR_SUCCESS) { register_blend_extension_failed(root, background); + return false; } lresult = RegCreateKeyEx( @@ -166,6 +169,7 @@ void BLI_windows_register_blend_extension(const bool background) } if (lresult != ERROR_SUCCESS) { register_blend_extension_failed(root, background); + return false; } BLI_windows_get_executable_dir(InstallDir); @@ -184,7 +188,7 @@ void BLI_windows_register_blend_extension(const bool background) "all users"); MessageBox(0, MBox, "Blender", MB_OK | MB_ICONINFORMATION); } - TerminateProcess(GetCurrentProcess(), 0); + return true; } void BLI_windows_get_default_root_dir(char *root) diff --git a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc index d00e8bf55fd..59c4be6d952 100644 --- a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc +++ b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc @@ -17,6 +17,7 @@ extern "C" { #define DO_CPP_TESTS 1 #define DO_C_TESTS 1 +#define DO_TEXT_TESTS 0 #define DO_RANDOM_TESTS 0 #include "BLI_array.hh" @@ -267,7 +268,7 @@ template<typename T> void graph_draw(const std::string &label, const Array<vec2<T>> &verts, const Array<std::pair<int, int>> &edges, - const Array<Vector<int>> &UNUSED(faces)) + const Array<Vector<int>> &faces) { /* Would like to use BKE_tempdir_base() here, but that brings in dependence on kernel library. * This is just for developer debugging anyway, and should never be called in production Blender. @@ -281,7 +282,7 @@ void graph_draw(const std::string &label, constexpr int max_draw_height = 1000; constexpr int thin_line = 1; constexpr int vert_radius = 3; - constexpr bool draw_vert_labels = true; + constexpr bool draw_vert_labels = false; constexpr bool draw_edge_labels = false; if (verts.size() == 0) { @@ -339,6 +340,15 @@ void graph_draw(const std::string &label, "xml:space=\"preserve\"\n" << "width=\"" << view_width << "\" height=\"" << view_height << "\">n"; + for (const Vector<int> &fverts : faces) { + f << "<polygon fill=\"azure\" stroke=\"none\"\n points=\""; + for (int vi : fverts) { + const vec2<T> &co = verts[vi]; + f << SX(co[0]) << "," << SY(co[1]) << " "; + } + f << "\"\n />\n"; + } + for (const std::pair<int, int> &e : edges) { const vec2<T> &uco = verts[e.first]; const vec2<T> &vco = verts[e.second]; @@ -642,6 +652,110 @@ template<typename T> void lineinsquare_test() if (DO_DRAW) { graph_draw<T>("LineInSquare - constraints", out2.vert, out2.edge, out2.face); } + CDT_result<T> out3 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES); + EXPECT_EQ(out3.vert.size(), 6); + EXPECT_EQ(out3.face.size(), 6); + if (DO_DRAW) { + graph_draw<T>("LineInSquare - inside with holes", out3.vert, out3.edge, out3.face); + } + CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES); + EXPECT_EQ(out4.vert.size(), 6); + EXPECT_EQ(out4.face.size(), 2); + if (DO_DRAW) { + graph_draw<T>("LineInSquare - valid bmesh with holes", out4.vert, out4.edge, out4.face); + } +} + +template<typename T> void lineholeinsquare_test() +{ + const char *spec = R"(10 1 2 + -0.5 -0.5 + 0.5 -0.5 + -0.5 0.5 + 0.5 0.5 + -0.25 0.0 + 0.25 0.0 + -0.4 -0.4 + 0.4 -0.4 + 0.4 -0.3 + -0.4 -0.3 + 4 5 + 0 1 3 2 + 6 7 8 9 + )"; + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 10); + EXPECT_EQ(out.face.size(), 14); + if (DO_DRAW) { + graph_draw<T>("LineHoleInSquare - full", out.vert, out.edge, out.face); + } + CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out2.vert.size(), 10); + EXPECT_EQ(out2.face.size(), 2); + if (DO_DRAW) { + graph_draw<T>("LineHoleInSquare - constraints", out2.vert, out2.edge, out2.face); + } + CDT_result<T> out3 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES); + EXPECT_EQ(out3.vert.size(), 10); + EXPECT_EQ(out3.face.size(), 12); + if (DO_DRAW) { + graph_draw<T>("LineHoleInSquare - inside with holes", out3.vert, out3.edge, out3.face); + } + CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES); + EXPECT_EQ(out4.vert.size(), 10); + EXPECT_EQ(out4.face.size(), 2); + if (DO_DRAW) { + graph_draw<T>("LineHoleInSquare - valid bmesh with holes", out4.vert, out4.edge, out4.face); + } +} + +template<typename T> void nestedholes_test() +{ + const char *spec = R"(12 0 3 + -0.5 -0.5 + 0.5 -0.5 + -0.5 0.5 + 0.5 0.5 + -0.4 -0.4 + 0.4 -0.4 + 0.4 0.4 + -0.4 0.4 + -0.2 -0.2 + 0.2 -0.2 + 0.2 0.2 + -0.2 0.2 + 0 1 3 2 + 4 7 6 5 + 8 9 10 11 + )"; + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 12); + EXPECT_EQ(out.face.size(), 18); + if (DO_DRAW) { + graph_draw<T>("NestedHoles - full", out.vert, out.edge, out.face); + } + CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out2.vert.size(), 12); + EXPECT_EQ(out2.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("NestedHoles - constraints", out2.vert, out2.edge, out2.face); + } + CDT_result<T> out3 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES); + EXPECT_EQ(out3.vert.size(), 12); + EXPECT_EQ(out3.face.size(), 10); + if (DO_DRAW) { + graph_draw<T>("NestedHoles - inside with holes", out3.vert, out3.edge, out3.face); + } + CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES); + EXPECT_EQ(out4.vert.size(), 12); + EXPECT_EQ(out4.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("NestedHoles - valid bmesh with holes", out4.vert, out4.edge, out4.face); + } } template<typename T> void crosssegs_test() @@ -1025,16 +1139,28 @@ template<typename T> void overlapfaces_test() graph_draw<T>("OverlapFaces - inside", out2.vert, out2.edge, out2.face); } - CDT_result<T> out3 = delaunay_2d_calc(in, CDT_CONSTRAINTS); - EXPECT_EQ(out3.face.size(), 4); + CDT_result<T> out3 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES); + EXPECT_EQ(out3.face.size(), 14); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - inside with holes", out3.vert, out3.edge, out3.face); + } + + CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out4.face.size(), 4); if (DO_DRAW) { - graph_draw<T>("OverlapFaces - constraints", out3.vert, out3.edge, out3.face); + graph_draw<T>("OverlapFaces - constraints", out4.vert, out4.edge, out4.face); } - CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out4.face.size(), 5); + CDT_result<T> out5 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out5.face.size(), 5); if (DO_DRAW) { - graph_draw<T>("OverlapFaces - valid bmesh", out4.vert, out4.edge, out4.face); + graph_draw<T>("OverlapFaces - valid bmesh", out5.vert, out5.edge, out5.face); + } + + CDT_result<T> out6 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES); + EXPECT_EQ(out6.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - valid bmesh with holes", out6.vert, out6.edge, out6.face); } } @@ -1246,6 +1372,34 @@ template<typename T> void repeattri_test() } } +template<typename T> void square_o_test() +{ + const char *spec = R"(8 0 2 + 0.0 0.0 + 1.0 0.0 + 1.0 1.0 + 0.0 1.0 + 0.2 0.2 + 0.2 0.8 + 0.8 0.8 + 0.8 0.2 + 0 1 2 3 + 4 5 6 7 + )"; + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out1 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES); + EXPECT_EQ(out1.face.size(), 8); + if (DO_DRAW) { + graph_draw<T>("Square O - inside with holes", out1.vert, out1.edge, out1.face); + } + + CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES); + EXPECT_EQ(out2.face.size(), 2); + if (DO_DRAW) { + graph_draw<T>("Square O - valid bmesh with holes", out2.vert, out2.edge, out2.face); + } +} + TEST(delaunay_d, Empty) { empty_test<double>(); @@ -1301,6 +1455,16 @@ TEST(delaunay_d, LineInSquare) lineinsquare_test<double>(); } +TEST(delaunay_d, LineHoleInSquare) +{ + lineholeinsquare_test<double>(); +} + +TEST(delaunay_d, NestedHoles) +{ + nestedholes_test<double>(); +} + TEST(delaunay_d, CrossSegs) { crosssegs_test<double>(); @@ -1371,6 +1535,11 @@ TEST(delaunay_d, RepeatTri) repeattri_test<double>(); } +TEST(delaunay_d, SquareO) +{ + square_o_test<double>(); +} + # ifdef WITH_GMP TEST(delaunay_m, Empty) { @@ -1426,6 +1595,16 @@ TEST(delaunay_m, LineInSquare) lineinsquare_test<mpq_class>(); } +TEST(delaunay_m, LineHoleInSquare) +{ + lineholeinsquare_test<mpq_class>(); +} + +TEST(delaunay_m, NestedHoles) +{ + nestedholes_test<mpq_class>(); +} + TEST(delaunay_m, CrossSegs) { crosssegs_test<mpq_class>(); @@ -1496,7 +1675,6 @@ TEST(delaunay_m, RepeatTri) repeattri_test<mpq_class>(); } # endif - #endif #if DO_C_TESTS @@ -1524,6 +1702,182 @@ TEST(delaunay_d, CintTwoFace) } #endif +#if DO_TEXT_TESTS +template<typename T> +void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_output_type otype) +{ + constexpr bool print_timing = true; + /* + * Make something like a letter B: + * + * 4------------3 + * | ) + * | 12--11 ) + * | | ) a3 ) a1 + * | 9---10 ) + * | ) + * | 2 + * | ) + * | 8----7 ) + * | | ) a2 ) a0 + * | 5----6 ) + * | ) + * 0------------1 + * + * Where the numbers are the first 13 vertices, and the rest of + * the vertices are in arcs a0, a1, a2, a3, each of which have + * num_arc_points per arc in them. + */ + + const char *b_before_arcs = R"(13 0 3 + 0.0 0.0 + 1.0 0.0 + 1.0 1.5 + 1.0 3.0 + 0.0 3.0 + 0.2 0.2 + 0.6 0.2 + 0.6 1.4 + 0.2 1.4 + 0.2 1.6 + 0.6 1.6 + 0.6 2.8 + 0.2 2.8 + 3 4 0 1 2 + 6 5 8 7 + 10 9 12 11 + )"; + + CDT_input<T> b_before_arcs_in = fill_input_from_string<T>(b_before_arcs); + constexpr int narcs = 4; + int b_npts = b_before_arcs_in.vert.size() + narcs * num_arc_points; + constexpr int b_nfaces = 3; + Array<vec2<T>> b_vert(b_npts); + Array<Vector<int>> b_face(b_nfaces); + std::copy(b_before_arcs_in.vert.begin(), b_before_arcs_in.vert.end(), b_vert.begin()); + std::copy(b_before_arcs_in.face.begin(), b_before_arcs_in.face.end(), b_face.begin()); + if (num_arc_points > 0) { + b_face[0].pop_last(); // We'll add center point back between arcs for outer face. + for (int arc = 0; arc < narcs; ++arc) { + int arc_origin_vert; + int arc_terminal_vert; + bool ccw; + switch (arc) { + case 0: + arc_origin_vert = 1; + arc_terminal_vert = 2; + ccw = true; + break; + case 1: + arc_origin_vert = 2; + arc_terminal_vert = 3; + ccw = true; + break; + case 2: + arc_origin_vert = 7; + arc_terminal_vert = 6; + ccw = false; + break; + case 3: + arc_origin_vert = 11; + arc_terminal_vert = 10; + ccw = false; + break; + default: + BLI_assert(false); + } + vec2<T> start_co = b_vert[arc_origin_vert]; + vec2<T> end_co = b_vert[arc_terminal_vert]; + vec2<T> center_co = 0.5 * (start_co + end_co); + BLI_assert(start_co[0] == end_co[2]); + double radius = abs(math_to_double<T>(end_co[1] - center_co[1])); + double angle_delta = M_PI / (num_arc_points + 1); + int start_vert = b_before_arcs_in.vert.size() + arc * num_arc_points; + Vector<int> &face = b_face[(arc <= 1) ? 0 : arc - 1]; + for (int i = 0; i < num_arc_points; ++i) { + vec2<T> delta; + float ang = ccw ? (-M_PI_2 + (i + 1) * angle_delta) : (M_PI_2 - (i + 1) * angle_delta); + delta[0] = T(radius * cos(ang)); + delta[1] = T(radius * sin(ang)); + b_vert[start_vert + i] = center_co + delta; + face.append(start_vert + i); + } + if (arc == 0) { + face.append(arc_terminal_vert); + } + } + } + + CDT_input<T> in; + int tot_instances = num_lets_per_line * num_lines; + if (tot_instances == 1) { + in.vert = b_vert; + in.face = b_face; + } + else { + in.vert = Array<vec2<T>>(tot_instances * b_vert.size()); + in.face = Array<Vector<int>>(tot_instances * b_face.size()); + T cur_x = T(0); + T cur_y = T(0); + T delta_x = T(2); + T delta_y = T(3.25); + int instance = 0; + for (int line = 0; line < num_lines; ++line) { + for (int let = 0; let < num_lets_per_line; ++let) { + vec2<T> co_offset(cur_x, cur_y); + int in_v_offset = instance * b_vert.size(); + for (int v = 0; v < b_vert.size(); ++v) { + in.vert[in_v_offset + v] = b_vert[v] + co_offset; + } + int in_f_offset = instance * b_face.size(); + for (int f : b_face.index_range()) { + for (int fv : b_face[f]) { + in.face[in_f_offset + f].append(in_v_offset + fv); + } + } + cur_x += delta_x; + ++instance; + } + cur_y += delta_y; + cur_x = T(0); + } + } + in.epsilon = b_before_arcs_in.epsilon; + double tstart = PIL_check_seconds_timer(); + CDT_result<T> out = delaunay_2d_calc(in, otype); + double tend = PIL_check_seconds_timer(); + if (print_timing) { + std::cout << "time = " << tend - tstart << "\n"; + } + if (DO_DRAW) { + std::string label = "Text arcpts=" + std::to_string(num_arc_points); + if (num_lets_per_line > 1) { + label += " linelen=" + std::to_string(num_lets_per_line); + } + if (num_lines > 1) { + label += " lines=" + std::to_string(num_lines); + } + graph_draw<T>(label, out.vert, out.edge, out.face); + } +} + +TEST(delaunay_d, TextB10) +{ + text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES); +} + +TEST(delaunay_d, TextB200) +{ + text_test<double>(200, 1, 1, CDT_INSIDE_WITH_HOLES); +} + +TEST(delaunay_d, TextB10_10_10) +{ + text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES); +} + +#endif + #if DO_RANDOM_TESTS enum { diff --git a/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc b/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc index 8be89d66062..e9810aed179 100644 --- a/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc +++ b/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc @@ -81,7 +81,7 @@ TEST(LockfreeLinkList, InsertMultipleConcurrent) LockfreeLinkList list; BLI_linklist_lockfree_init(&list); /* Initialize task scheduler and pool. */ - TaskPool *pool = BLI_task_pool_create_suspended(&list, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + 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, nullptr); diff --git a/source/blender/blenlib/tests/BLI_math_time_test.cc b/source/blender/blenlib/tests/BLI_math_time_test.cc new file mode 100644 index 00000000000..61a504ac2d7 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_math_time_test.cc @@ -0,0 +1,35 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_math.h" + +TEST(math_time, SecondsExplode) +{ + const double seconds = 2.0 * SECONDS_IN_DAY + 13.0 * SECONDS_IN_HOUR + 33.0 * SECONDS_IN_MINUTE + + 9.0 + 369.0 * SECONDS_IN_MILLISECONDS; + const double epsilon = 1e-8; + + double r_days, r_hours, r_minutes, r_seconds, r_milliseconds; + + BLI_math_time_seconds_decompose( + seconds, &r_days, &r_hours, &r_minutes, &r_seconds, &r_milliseconds); + EXPECT_NEAR(2.0, r_days, epsilon); + EXPECT_NEAR(13.0, r_hours, epsilon); + EXPECT_NEAR(33.0, r_minutes, epsilon); + EXPECT_NEAR(9.0, r_seconds, epsilon); + EXPECT_NEAR(369.0, r_milliseconds, epsilon); + + BLI_math_time_seconds_decompose(seconds, nullptr, &r_hours, &r_minutes, &r_seconds, nullptr); + EXPECT_NEAR(61.0, r_hours, epsilon); + EXPECT_NEAR(33.0, r_minutes, epsilon); + EXPECT_NEAR(9.369, r_seconds, epsilon); + + BLI_math_time_seconds_decompose(seconds, nullptr, nullptr, nullptr, &r_seconds, nullptr); + EXPECT_NEAR(seconds, r_seconds, epsilon); + + BLI_math_time_seconds_decompose(seconds, &r_days, nullptr, &r_minutes, nullptr, &r_milliseconds); + EXPECT_NEAR(2.0, r_days, epsilon); + EXPECT_NEAR(813.0, r_minutes, epsilon); + EXPECT_NEAR(9369.0, r_milliseconds, epsilon); +} diff --git a/source/blender/blenlib/tests/BLI_stack_test.cc b/source/blender/blenlib/tests/BLI_stack_test.cc index 1fef5998b99..a2650e5509d 100644 --- a/source/blender/blenlib/tests/BLI_stack_test.cc +++ b/source/blender/blenlib/tests/BLI_stack_test.cc @@ -137,7 +137,7 @@ TEST(stack, Clear) BLI_stack_clear(stack); EXPECT_TRUE(BLI_stack_is_empty(stack)); - /* and again, this time check its valid */ + /* and again, this time check it is valid */ for (in = 0; in < tot; in++) { BLI_stack_push(stack, (void *)&in); } @@ -150,7 +150,7 @@ TEST(stack, Clear) EXPECT_TRUE(BLI_stack_is_empty(stack)); - /* without this, we wont test case when mixed free/used */ + /* without this, we won't test case when mixed free/used */ tot /= 2; } diff --git a/source/blender/blenlib/tests/BLI_string_test.cc b/source/blender/blenlib/tests/BLI_string_test.cc index 88cecaa5fee..0b68ee8b93c 100644 --- a/source/blender/blenlib/tests/BLI_string_test.cc +++ b/source/blender/blenlib/tests/BLI_string_test.cc @@ -421,9 +421,7 @@ TEST(string, StrFormatByteUnits) } struct WordInfo { - WordInfo() - { - } + WordInfo() = default; WordInfo(int start, int end) : start(start), end(end) { } @@ -441,9 +439,7 @@ static std::ostream &operator<<(std::ostream &os, const WordInfo &word_info) class StringFindSplitWords : public testing::Test { protected: - StringFindSplitWords() - { - } + StringFindSplitWords() = default; /* If max_words is -1 it will be initialized from the number of expected * words +1. This way there is no need to pass an explicit number of words, @@ -807,9 +803,7 @@ TEST_F(StringCasecmpNatural, TextAndNumbers) class StringEscape : public testing::Test { protected: - StringEscape() - { - } + StringEscape() = default; using CompareWordsArray = vector<std::array<const char *, 2>>; @@ -832,6 +826,9 @@ class StringEscape : public testing::Test { TEST_F(StringEscape, Simple) { + /* NOTE: clang-tidy `modernize-raw-string-literal` is disabled as it causes errors with MSVC. + * TODO: investigate resolving with `/Zc:preprocessor` flag. */ + const CompareWordsArray equal{ {"", ""}, {"/", "/"}, @@ -845,11 +842,16 @@ TEST_F(StringEscape, Simple) {"\\A", "\\\\A"}, {"A\\B", "A\\\\B"}, {"?", "?"}, + /* NOLINTNEXTLINE: modernize-raw-string-literal. */ {"\"\\", "\\\"\\\\"}, + /* NOLINTNEXTLINE: modernize-raw-string-literal. */ {"\\\"", "\\\\\\\""}, + /* NOLINTNEXTLINE: modernize-raw-string-literal. */ {"\"\\\"", "\\\"\\\\\\\""}, + /* NOLINTNEXTLINE: modernize-raw-string-literal. */ {"\"\"\"", "\\\"\\\"\\\""}, + /* NOLINTNEXTLINE: modernize-raw-string-literal. */ {"\\\\\\", "\\\\\\\\\\\\"}, }; @@ -868,8 +870,11 @@ TEST_F(StringEscape, Control) {"\f", "\\f"}, {"A\n", "A\\n"}, {"\nA", "\\nA"}, + /* NOLINTNEXTLINE: modernize-raw-string-literal. */ {"\n\r\t\a\b\f", "\\n\\r\\t\\a\\b\\f"}, + /* NOLINTNEXTLINE: modernize-raw-string-literal. */ {"\n_\r_\t_\a_\b_\f", "\\n_\\r_\\t_\\a_\\b_\\f"}, + /* NOLINTNEXTLINE: modernize-raw-string-literal. */ {"\n\\\r\\\t\\\a\\\b\\\f", "\\n\\\\\\r\\\\\\t\\\\\\a\\\\\\b\\\\\\f"}, }; diff --git a/source/blender/blenlib/tests/BLI_task_test.cc b/source/blender/blenlib/tests/BLI_task_test.cc index 52603428031..dd4441517a9 100644 --- a/source/blender/blenlib/tests/BLI_task_test.cc +++ b/source/blender/blenlib/tests/BLI_task_test.cc @@ -141,9 +141,9 @@ TEST(task, MempoolIter) /* *** Parallel iterations over mempool items with TLS. *** */ -typedef struct TaskMemPool_Chunk { +using TaskMemPool_Chunk = struct TaskMemPool_Chunk { ListBase *accumulate_items; -} TaskMemPool_Chunk; +}; static void task_mempool_iter_tls_func(void *UNUSED(userdata), MempoolIterData *item, @@ -206,7 +206,7 @@ TEST(task, MempoolIterTLS) BLI_parallel_mempool_settings_defaults(&settings); TaskMemPool_Chunk tls_data; - tls_data.accumulate_items = NULL; + tls_data.accumulate_items = nullptr; settings.userdata_chunk = &tls_data; settings.userdata_chunk_size = sizeof(tls_data); diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index ea0532d884b..86c7c367816 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -54,6 +54,7 @@ typedef struct BlendExpander BlendExpander; typedef struct BlendLibReader BlendLibReader; typedef struct BlendWriter BlendWriter; +struct BlendFileReadReport; struct Main; struct ReportList; @@ -216,7 +217,7 @@ bool BLO_read_requires_endian_switch(BlendDataReader *reader); bool BLO_read_data_is_undo(BlendDataReader *reader); void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr); void BLO_read_glob_list(BlendDataReader *reader, struct ListBase *list); -struct ReportList *BLO_read_data_reports(BlendDataReader *reader); +struct BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader); /* Blend Read Lib API * =================== @@ -233,7 +234,7 @@ ID *BLO_read_get_new_id_address(BlendLibReader *reader, struct Library *lib, str /* Misc. */ bool BLO_read_lib_is_undo(BlendLibReader *reader); struct Main *BLO_read_lib_get_main(BlendLibReader *reader); -struct ReportList *BLO_read_lib_reports(BlendLibReader *reader); +struct BlendFileReadReport *BLO_read_lib_reports(BlendLibReader *reader); /* Blend Expand API * =================== @@ -250,8 +251,10 @@ void BLO_expand_id(BlendExpander *expander, struct ID *id); * =================== */ -void BLO_reportf_wrap(struct ReportList *reports, ReportType type, const char *format, ...) - ATTR_PRINTF_FORMAT(3, 4); +void BLO_reportf_wrap(struct BlendFileReadReport *reports, + ReportType type, + const char *format, + ...) ATTR_PRINTF_FORMAT(3, 4); #ifdef __cplusplus } diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 89db216aada..307c09f8ff5 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -89,6 +89,35 @@ struct BlendFileReadParams { int undo_direction; /* eUndoStepDir */ }; +typedef struct BlendFileReadReport { + /* General reports handling. */ + struct ReportList *reports; + + /* Timing informations .*/ + struct { + double whole; + double libraries; + double lib_overrides; + double lib_overrides_resync; + double lib_overrides_recursive_resync; + } duration; + + /* Count informations. */ + struct { + /* Some numbers of IDs that ended up in a specific state, or required some specific process + * during this file read. */ + int missing_libraries; + int missing_linked_id; + /* Number of root override IDs that were resynced. */ + int resynced_lib_overrides; + } count; + + /* Number of libraries which had overrides that needed to be resynced, and a single linked list + * of those. */ + int resynced_lib_overrides_libraries_count; + struct LinkNode *resynced_lib_overrides_libraries; +} BlendFileReadReport; + /* skip reading some data-block types (may want to skip screen data too). */ typedef enum eBLOReadSkip { BLO_READ_SKIP_NONE = 0, @@ -101,7 +130,7 @@ typedef enum eBLOReadSkip { BlendFileData *BLO_read_from_file(const char *filepath, eBLOReadSkip skip_flags, - struct ReportList *reports); + struct BlendFileReadReport *reports); BlendFileData *BLO_read_from_memory(const void *mem, int memsize, eBLOReadSkip skip_flags, @@ -125,7 +154,7 @@ struct BLODataBlockInfo { struct AssetMetaData *asset_data; }; -BlendHandle *BLO_blendhandle_from_file(const char *filepath, struct ReportList *reports); +BlendHandle *BLO_blendhandle_from_file(const char *filepath, struct BlendFileReadReport *reports); BlendHandle *BLO_blendhandle_from_memory(const void *mem, int memsize); struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 36802fc8842..61a00ccdaa4 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC intern/versioning_290.c intern/versioning_300.c intern/versioning_cycles.c + intern/versioning_common.cc intern/versioning_defaults.c intern/versioning_dna.c intern/versioning_legacy.c @@ -72,6 +73,7 @@ set(SRC BLO_undofile.h BLO_writefile.h intern/readfile.h + intern/versioning_common.h ) set(LIB diff --git a/source/blender/blenloader/intern/blend_validate.c b/source/blender/blenloader/intern/blend_validate.c index f3fc1453461..0f729304128 100644 --- a/source/blender/blenloader/intern/blend_validate.c +++ b/source/blender/blenloader/intern/blend_validate.c @@ -83,7 +83,8 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports) } BKE_library_filepath_set(bmain, curlib, curlib->filepath); - BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath_abs, reports); + BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath_abs, + &(BlendFileReadReport){.reports = reports}); if (bh == NULL) { BKE_reportf(reports, diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 19033ba9bf1..1a324d56f06 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -68,7 +68,7 @@ void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp); * \param reports: Report errors in opening the file (can be NULL). * \return A handle on success, or NULL on failure. */ -BlendHandle *BLO_blendhandle_from_file(const char *filepath, ReportList *reports) +BlendHandle *BLO_blendhandle_from_file(const char *filepath, BlendFileReadReport *reports) { BlendHandle *bh; @@ -88,7 +88,8 @@ BlendHandle *BLO_blendhandle_from_memory(const void *mem, int memsize) { BlendHandle *bh; - bh = (BlendHandle *)blo_filedata_from_memory(mem, memsize, NULL); + bh = (BlendHandle *)blo_filedata_from_memory( + mem, memsize, &(BlendFileReadReport){.reports = NULL}); return bh; } @@ -366,14 +367,13 @@ void BLO_blendhandle_close(BlendHandle *bh) */ BlendFileData *BLO_read_from_file(const char *filepath, eBLOReadSkip skip_flags, - ReportList *reports) + BlendFileReadReport *reports) { BlendFileData *bfd = NULL; FileData *fd; fd = blo_filedata_from_file(filepath, reports); if (fd) { - fd->reports = reports; fd->skip_flags = skip_flags; bfd = blo_read_file_internal(fd, filepath); blo_filedata_free(fd); @@ -399,9 +399,8 @@ BlendFileData *BLO_read_from_memory(const void *mem, BlendFileData *bfd = NULL; FileData *fd; - fd = blo_filedata_from_memory(mem, memsize, reports); + fd = blo_filedata_from_memory(mem, memsize, &(BlendFileReadReport){.reports = reports}); if (fd) { - fd->reports = reports; fd->skip_flags = skip_flags; bfd = blo_read_file_internal(fd, ""); blo_filedata_free(fd); @@ -428,9 +427,8 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, FileData *fd; ListBase old_mainlist; - fd = blo_filedata_from_memfile(memfile, params, reports); + fd = blo_filedata_from_memfile(memfile, params, &(BlendFileReadReport){.reports = reports}); if (fd) { - fd->reports = reports; fd->skip_flags = params->skip_flags; BLI_strncpy(fd->relabase, filename, sizeof(fd->relabase)); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 47ed4e5c06f..dce5b5ebf87 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -40,6 +40,8 @@ # include <io.h> /* for open close read */ #endif +#include "CLG_log.h" + /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW @@ -72,6 +74,8 @@ #include "BLI_mmap.h" #include "BLI_threads.h" +#include "PIL_time.h" + #include "BLT_translation.h" #include "BKE_anim_data.h" @@ -186,14 +190,8 @@ /* Use GHash for restoring pointers by name */ #define USE_GHASH_RESTORE_POINTER -/* Define this to have verbose debug prints. */ -//#define USE_DEBUG_PRINT - -#ifdef USE_DEBUG_PRINT -# define DEBUG_PRINTF(...) printf(__VA_ARGS__) -#else -# define DEBUG_PRINTF(...) -#endif +static CLG_LogRef LOG = {"blo.readfile"}; +static CLG_LogRef LOG_UNDO = {"blo.readfile.undo"}; /* local prototypes */ static void read_libraries(FileData *basefd, ListBase *mainlist); @@ -227,7 +225,7 @@ typedef struct BHeadN { * bit kludge but better than doubling up on prints, * we could alternatively have a versions of a report function which forces printing - campbell */ -void BLO_reportf_wrap(ReportList *reports, ReportType type, const char *format, ...) +void BLO_reportf_wrap(BlendFileReadReport *reports, ReportType type, const char *format, ...) { char fixed_buf[1024]; /* should be long enough */ @@ -239,7 +237,7 @@ void BLO_reportf_wrap(ReportList *reports, ReportType type, const char *format, fixed_buf[sizeof(fixed_buf) - 1] = '\0'; - BKE_report(reports, type, fixed_buf); + BKE_report(reports->reports, type, fixed_buf); if (G.background == 0) { printf("%s: %s\n", BKE_report_type_str(type), fixed_buf); @@ -488,7 +486,7 @@ static void split_libdata(ListBase *lb_src, Main **lib_main_array, const uint li BLI_addtail(lb_dst, id); } else { - printf("%s: invalid library for '%s'\n", __func__, id->name); + CLOG_ERROR(&LOG, "Invalid library for '%s'", id->name); BLI_assert(0); } } @@ -635,7 +633,7 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab if (BLI_path_cmp(name1, libname) == 0) { if (G.debug & G_DEBUG) { - printf("blo_find_main: found library %s\n", libname); + CLOG_INFO(&LOG, 3, "Found library %s", libname); } return m; } @@ -662,7 +660,7 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab read_file_version(fd, m); if (G.debug & G_DEBUG) { - printf("blo_find_main: added new lib %s\n", filepath); + CLOG_INFO(&LOG, 3, "Added new lib %s", filepath); } return m; } @@ -1296,7 +1294,7 @@ static ssize_t fd_read_from_memfile(FileData *filedata, /* debug, should never happen */ if (chunk == NULL) { - printf("illegal read, chunk zero\n"); + CLOG_ERROR(&LOG, "Illegal read, got a NULL chunk"); return 0; } @@ -1331,8 +1329,10 @@ static ssize_t fd_read_from_memfile(FileData *filedata, return 0; } -static FileData *filedata_new(void) +static FileData *filedata_new(BlendFileReadReport *reports) { + BLI_assert(reports != NULL); + FileData *fd = MEM_callocN(sizeof(FileData), "FileData"); fd->filedes = -1; @@ -1344,6 +1344,8 @@ static FileData *filedata_new(void) fd->globmap = oldnewmap_new(); fd->libmap = oldnewmap_new(); + fd->reports = reports; + return fd; } @@ -1371,7 +1373,7 @@ static FileData *blo_decode_and_check(FileData *fd, ReportList *reports) } static FileData *blo_filedata_from_file_descriptor(const char *filepath, - ReportList *reports, + BlendFileReadReport *reports, int file) { FileDataReadFn *read_fn = NULL; @@ -1386,7 +1388,7 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, /* Regular file. */ errno = 0; if (read(file, header, sizeof(header)) != sizeof(header)) { - BKE_reportf(reports, + BKE_reportf(reports->reports, RPT_WARNING, "Unable to read '%s': %s", filepath, @@ -1416,7 +1418,7 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, (header[0] == 0x1f && header[1] == 0x8b)) { gzfile = BLI_gzopen(filepath, "rb"); if (gzfile == (gzFile)Z_NULL) { - BKE_reportf(reports, + BKE_reportf(reports->reports, RPT_WARNING, "Unable to open '%s': %s", filepath, @@ -1431,11 +1433,11 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, } if (read_fn == NULL) { - BKE_reportf(reports, RPT_WARNING, "Unrecognized file format '%s'", filepath); + BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath); return NULL; } - FileData *fd = filedata_new(); + FileData *fd = filedata_new(reports); fd->filedes = file; fd->gzfiledes = gzfile; @@ -1448,12 +1450,12 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, return fd; } -static FileData *blo_filedata_from_file_open(const char *filepath, ReportList *reports) +static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileReadReport *reports) { errno = 0; const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0); if (file == -1) { - BKE_reportf(reports, + BKE_reportf(reports->reports, RPT_WARNING, "Unable to open '%s': %s", filepath, @@ -1469,14 +1471,14 @@ static FileData *blo_filedata_from_file_open(const char *filepath, ReportList *r /* cannot be called with relative paths anymore! */ /* on each new library added, it now checks for the current FileData and expands relativeness */ -FileData *blo_filedata_from_file(const char *filepath, ReportList *reports) +FileData *blo_filedata_from_file(const char *filepath, BlendFileReadReport *reports) { FileData *fd = blo_filedata_from_file_open(filepath, reports); if (fd != NULL) { /* needed for library_append and read_libraries */ BLI_strncpy(fd->relabase, filepath, sizeof(fd->relabase)); - return blo_decode_and_check(fd, reports); + return blo_decode_and_check(fd, reports->reports); } return NULL; } @@ -1487,7 +1489,7 @@ FileData *blo_filedata_from_file(const char *filepath, ReportList *reports) */ static FileData *blo_filedata_from_file_minimal(const char *filepath) { - FileData *fd = blo_filedata_from_file_open(filepath, NULL); + FileData *fd = blo_filedata_from_file_open(filepath, &(BlendFileReadReport){.reports = NULL}); if (fd != NULL) { decode_blender_header(fd); if (fd->flags & FD_FLAGS_FILE_OK) { @@ -1515,7 +1517,7 @@ static ssize_t fd_read_gzip_from_memory(FileData *filedata, return 0; } if (err != Z_OK) { - printf("fd_read_gzip_from_memory: zlib error\n"); + CLOG_ERROR(&LOG, "ZLib error (code %d)", err); return 0; } @@ -1542,14 +1544,15 @@ static int fd_read_gzip_from_memory_init(FileData *fd) return 1; } -FileData *blo_filedata_from_memory(const void *mem, int memsize, ReportList *reports) +FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports) { if (!mem || memsize < SIZEOFBLENDERHEADER) { - BKE_report(reports, RPT_WARNING, (mem) ? TIP_("Unable to read") : TIP_("Unable to open")); + BKE_report( + reports->reports, RPT_WARNING, (mem) ? TIP_("Unable to read") : TIP_("Unable to open")); return NULL; } - FileData *fd = filedata_new(); + FileData *fd = filedata_new(reports); const char *cp = mem; fd->buffer = mem; @@ -1568,26 +1571,26 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, ReportList *rep fd->flags |= FD_FLAGS_NOT_MY_BUFFER; - return blo_decode_and_check(fd, reports); + return blo_decode_and_check(fd, reports->reports); } FileData *blo_filedata_from_memfile(MemFile *memfile, const struct BlendFileReadParams *params, - ReportList *reports) + BlendFileReadReport *reports) { if (!memfile) { - BKE_report(reports, RPT_WARNING, "Unable to open blend <memory>"); + BKE_report(reports->reports, RPT_WARNING, "Unable to open blend <memory>"); return NULL; } - FileData *fd = filedata_new(); + FileData *fd = filedata_new(reports); fd->memfile = memfile; fd->undo_direction = params->undo_direction; fd->read = fd_read_from_memfile; fd->flags |= FD_FLAGS_NOT_MY_BUFFER; - return blo_decode_and_check(fd, reports); + return blo_decode_and_check(fd, reports->reports); } void blo_filedata_free(FileData *fd) @@ -1602,8 +1605,9 @@ void blo_filedata_free(FileData *fd) } if (fd->strm.next_in) { - if (inflateEnd(&fd->strm) != Z_OK) { - printf("close gzip stream error\n"); + int err = inflateEnd(&fd->strm); + if (err != Z_OK) { + CLOG_ERROR(&LOG, "Close gzip stream error (code %d)", err); } } @@ -2586,7 +2590,7 @@ static void lib_link_scenes_check_set(Main *bmain) if (sce->flag & SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK) { sce->flag &= ~SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK; if (!scene_validate_setscene__liblink(sce, totscene)) { - printf("Found cyclic background scene when linking %s\n", sce->id.name + 2); + CLOG_WARN(&LOG, "Found cyclic background scene when linking %s", sce->id.name + 2); } } } @@ -3420,15 +3424,17 @@ static bool read_libblock_undo_restore_library(FileData *fd, Main *main, const I * That means we have to carefully check whether current lib or * libdata already exits in old main, if it does we merely copy it over into new main area, * otherwise we have to do a full read of that bhead... */ - DEBUG_PRINTF("UNDO: restore library %s\n", id->name); + CLOG_INFO(&LOG_UNDO, 2, "UNDO: restore library %s", id->name); Main *libmain = fd->old_mainlist->first; /* Skip oldmain itself... */ for (libmain = libmain->next; libmain; libmain = libmain->next) { - DEBUG_PRINTF(" compare with %s -> ", libmain->curlib ? libmain->curlib->id.name : "<NULL>"); if (libmain->curlib && STREQ(id->name, libmain->curlib->id.name)) { Main *oldmain = fd->old_mainlist->first; - DEBUG_PRINTF("match!\n"); + CLOG_INFO(&LOG_UNDO, + 2, + " compare with %s -> match", + libmain->curlib ? libmain->curlib->id.name : "<NULL>"); /* In case of a library, we need to re-add its main to fd->mainlist, * because if we have later a missing ID_LINK_PLACEHOLDER, * we need to get the correct lib it is linked to! @@ -3440,7 +3446,10 @@ static bool read_libblock_undo_restore_library(FileData *fd, Main *main, const I BLI_addtail(&main->libraries, libmain->curlib); return true; } - DEBUG_PRINTF("no match\n"); + CLOG_INFO(&LOG_UNDO, + 2, + " compare with %s -> NO match", + libmain->curlib ? libmain->curlib->id.name : "<NULL>"); } return false; @@ -3449,14 +3458,15 @@ static bool read_libblock_undo_restore_library(FileData *fd, Main *main, const I /* For undo, restore existing linked datablock from the old main. */ static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID *id, BHead *bhead) { - DEBUG_PRINTF("UNDO: restore linked datablock %s\n", id->name); - DEBUG_PRINTF(" from %s (%s): ", - main->curlib ? main->curlib->id.name : "<NULL>", - main->curlib ? main->curlib->filepath : "<NULL>"); + CLOG_INFO(&LOG_UNDO, 2, "UNDO: restore linked datablock %s", id->name); ID *id_old = BKE_libblock_find_name(main, GS(id->name), id->name + 2); if (id_old != NULL) { - DEBUG_PRINTF(" found!\n"); + CLOG_INFO(&LOG_UNDO, + 2, + " from %s (%s): found", + main->curlib ? main->curlib->id.name : "<NULL>", + main->curlib ? main->curlib->filepath : "<NULL>"); /* Even though we found our linked ID, there is no guarantee its address * is still the same. */ if (id_old != bhead->old) { @@ -3468,7 +3478,11 @@ static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID return true; } - DEBUG_PRINTF(" not found\n"); + CLOG_INFO(&LOG_UNDO, + 2, + " from %s (%s): NOT found", + main->curlib ? main->curlib->id.name : "<NULL>", + main->curlib ? main->curlib->filepath : "<NULL>"); return false; } @@ -3577,8 +3591,6 @@ static bool read_libblock_undo_restore( } /* Restore local datablocks. */ - DEBUG_PRINTF("UNDO: read %s (uuid %u) -> ", id->name, id->session_uuid); - ID *id_old = NULL; const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0; if (do_partial_undo && (bhead->code != ID_LINK_PLACEHOLDER)) { @@ -3593,7 +3605,11 @@ static bool read_libblock_undo_restore( if (id_old != NULL && read_libblock_is_identical(fd, bhead)) { /* Local datablock was unchanged, restore from the old main. */ - DEBUG_PRINTF("keep identical datablock\n"); + CLOG_INFO(&LOG_UNDO, + 2, + "UNDO: read %s (uuid %u) -> keep identical datablock", + id->name, + id->session_uuid); /* 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 @@ -3613,13 +3629,18 @@ static bool read_libblock_undo_restore( } if (id_old != NULL) { /* Local datablock was changed. Restore at the address of the old datablock. */ - DEBUG_PRINTF("read to old existing address\n"); + CLOG_INFO(&LOG_UNDO, + 2, + "UNDO: read %s (uuid %u) -> read to old existing address", + id->name, + id->session_uuid); *r_id_old = id_old; return false; } /* Local datablock does not exist in the undo step, so read from scratch. */ - DEBUG_PRINTF("read at new address\n"); + CLOG_INFO( + &LOG_UNDO, 2, "UNDO: read %s (uuid %u) -> read at new address", id->name, id->session_uuid); return false; } @@ -3664,7 +3685,7 @@ static BHead *read_libblock(FileData *fd, ListBase *lb = which_libbase(main, idcode); if (lb == NULL) { /* Unknown ID type. */ - printf("%s: unknown id code '%c%c'\n", __func__, (idcode & 0xff), (idcode >> 8)); + CLOG_WARN(&LOG, "Unknown id code '%c%c'", (idcode & 0xff), (idcode >> 8)); MEM_freeN(id); if (r_id) { *r_id = NULL; @@ -3856,12 +3877,14 @@ static void do_versions(FileData *fd, Library *lib, Main *main) BLI_strncpy(build_commit_datetime, "unknown", sizeof(build_commit_datetime)); } - printf("read file %s\n Version %d sub %d date %s hash %s\n", - fd->relabase, - main->versionfile, - main->subversionfile, - build_commit_datetime, - main->build_hash); + CLOG_INFO(&LOG, 0, "Read file %s", fd->relabase); + CLOG_INFO(&LOG, + 0, + " Version %d sub %d date %s hash %s", + main->versionfile, + main->subversionfile, + build_commit_datetime, + main->build_hash); } blo_do_versions_pre250(fd, lib, main); @@ -3883,8 +3906,13 @@ static void do_versions(FileData *fd, Library *lib, Main *main) static void do_versions_after_linking(Main *main, ReportList *reports) { - // printf("%s for %s (%s), %d.%d\n", __func__, main->curlib ? main->curlib->filepath : - // main->name, main->curlib ? "LIB" : "MAIN", main->versionfile, main->subversionfile); + CLOG_INFO(&LOG, + 2, + "Processing %s (%s), %d.%d", + main->curlib ? main->curlib->filepath : main->name, + main->curlib ? "LIB" : "MAIN", + main->versionfile, + main->subversionfile); /* Don't allow versioning to create new data-blocks. */ main->is_locked_for_linking = true; @@ -4105,7 +4133,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) ListBase mainlist = {NULL, NULL}; if (fd->memfile != NULL) { - DEBUG_PRINTF("\nUNDO: read step\n"); + CLOG_INFO(&LOG_UNDO, 2, "UNDO: read step"); } bfd = MEM_callocN(sizeof(BlendFileData), "blendfiledata"); @@ -4206,6 +4234,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) } if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) { + fd->reports->duration.libraries = PIL_check_seconds_timer(); read_libraries(fd, &mainlist); blo_join_main(&mainlist); @@ -4213,6 +4242,8 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) lib_link_all(fd, bfd->main); after_liblink_merged_bmain_process(bfd->main); + fd->reports->duration.libraries = PIL_check_seconds_timer() - fd->reports->duration.libraries; + /* Skip in undo case. */ if (fd->memfile == NULL) { /* Note that we can't recompute user-counts at this point in undo case, we play too much with @@ -4228,7 +4259,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) blo_split_main(&mainlist, bfd->main); LISTBASE_FOREACH (Main *, mainvar, &mainlist) { BLI_assert(mainvar->versionfile != 0); - do_versions_after_linking(mainvar, fd->reports); + do_versions_after_linking(mainvar, fd->reports->reports); } blo_join_main(&mainlist); @@ -4249,8 +4280,13 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) * we can re-generate overrides from their references. */ if (fd->memfile == NULL) { /* Do not apply in undo case! */ - BKE_lib_override_library_main_validate(bfd->main, fd->reports); + fd->reports->duration.lib_overrides = PIL_check_seconds_timer(); + + BKE_lib_override_library_main_validate(bfd->main, fd->reports->reports); BKE_lib_override_library_main_update(bfd->main); + + fd->reports->duration.lib_overrides = PIL_check_seconds_timer() - + fd->reports->duration.lib_overrides; } BKE_collections_after_lib_link(bfd->main); @@ -4908,9 +4944,7 @@ static ID *link_named_part( } else { /* already linked */ - if (G.debug) { - printf("append: already linked\n"); - } + CLOG_WARN(&LOG, "Append: ID '%s' is already linked", id->name); oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); if (!force_indirect && (id->tag & LIB_TAG_INDIRECT)) { id->tag &= ~LIB_TAG_INDIRECT; @@ -5184,7 +5218,7 @@ static void library_link_end(Main *mainl, blo_join_main((*fd)->mainlist); mainvar = (*fd)->mainlist->first; - mainl = NULL; /* blo_join_main free's mainl, cant use anymore */ + mainl = NULL; /* blo_join_main free's mainl, can't use anymore */ lib_link_all(*fd, mainvar); after_liblink_merged_bmain_process(mainvar); @@ -5206,7 +5240,7 @@ static void library_link_end(Main *mainl, * or they will go again through do_versions - bad, very bad! */ split_main_newid(mainvar, main_newid); - do_versions_after_linking(main_newid, (*fd)->reports); + do_versions_after_linking(main_newid, (*fd)->reports->reports); add_main_to_main(mainvar, main_newid); } @@ -5340,7 +5374,7 @@ static void read_library_linked_id( id->name + 2, mainvar->curlib->filepath_abs, library_parent_filepath(mainvar->curlib)); - basefd->library_id_missing_count++; + basefd->reports->count.missing_linked_id++; /* Generate a placeholder for this ID (simplified version of read_libblock actually...). */ if (r_id) { @@ -5414,7 +5448,7 @@ static void read_library_clear_weak_links(FileData *basefd, ListBase *mainlist, while (id) { ID *id_next = id->next; if ((id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) && (id->flag & LIB_INDIRECT_WEAK_LINK)) { - /* printf("Dropping weak link to %s\n", id->name); */ + CLOG_INFO(&LOG, 3, "Dropping weak link to '%s'", id->name); change_link_placeholder_to_real_ID_pointer(mainlist, basefd, id, NULL); BLI_freelinkN(lbarray[a], id); } @@ -5495,7 +5529,7 @@ static FileData *read_library_file_data(FileData *basefd, if (fd == NULL) { BLO_reportf_wrap( basefd->reports, RPT_INFO, TIP_("Cannot find lib '%s'"), mainptr->curlib->filepath_abs); - basefd->library_file_missing_count++; + basefd->reports->count.missing_libraries++; } return fd; @@ -5524,11 +5558,11 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) for (Main *mainptr = mainl->next; mainptr; mainptr = mainptr->next) { /* Does this library have any more linked data-blocks we need to read? */ if (has_linked_ids_to_read(mainptr)) { -#if 0 - printf("Reading linked data-blocks from %s (%s)\n", - mainptr->curlib->id.name, - mainptr->curlib->filepath); -#endif + CLOG_INFO(&LOG, + 3, + "Reading linked data-blocks from %s (%s)", + mainptr->curlib->id.name, + mainptr->curlib->filepath); /* Open file if it has not been done yet. */ FileData *fd = read_library_file_data(basefd, mainlist, mainl, mainptr); @@ -5589,15 +5623,6 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) mainptr->curlib->filedata = NULL; } BKE_main_free(main_newid); - - if (basefd->library_file_missing_count != 0 || basefd->library_id_missing_count != 0) { - BKE_reportf(basefd->reports, - RPT_WARNING, - "LIB: %d libraries and %d linked data-blocks are missing, please check the " - "Info and Outliner editors for details", - basefd->library_file_missing_count, - basefd->library_id_missing_count); - } } void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_address) @@ -5785,7 +5810,7 @@ void BLO_read_glob_list(BlendDataReader *reader, ListBase *list) link_glob_list(reader->fd, list); } -ReportList *BLO_read_data_reports(BlendDataReader *reader) +BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader) { return reader->fd->reports; } @@ -5800,7 +5825,7 @@ Main *BLO_read_lib_get_main(BlendLibReader *reader) return reader->main; } -ReportList *BLO_read_lib_reports(BlendLibReader *reader) +BlendFileReadReport *BLO_read_lib_reports(BlendLibReader *reader) { return reader->fd->reports; } diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index d1d4e0b3256..b04043f9641 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -145,11 +145,7 @@ typedef struct FileData { ListBase *old_mainlist; struct IDNameLib_Map *old_idmap; - struct ReportList *reports; - /* Counters for amount of missing libraries, and missing IDs in libraries. - * Used to generate a synthetic report in the UI. */ - int library_file_missing_count; - int library_id_missing_count; + struct BlendFileReadReport *reports; } FileData; #define SIZEOFBLENDERHEADER 12 @@ -161,11 +157,13 @@ void blo_split_main(ListBase *mainlist, struct Main *main); BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath); -FileData *blo_filedata_from_file(const char *filepath, struct ReportList *reports); -FileData *blo_filedata_from_memory(const void *mem, int memsize, struct ReportList *reports); +FileData *blo_filedata_from_file(const char *filepath, struct BlendFileReadReport *reports); +FileData *blo_filedata_from_memory(const void *mem, + int memsize, + struct BlendFileReadReport *reports); FileData *blo_filedata_from_memfile(struct MemFile *memfile, const struct BlendFileReadParams *params, - struct ReportList *reports); + struct BlendFileReadReport *reports); void blo_clear_proxy_pointers_from_lib(struct Main *oldmain); void blo_make_packed_pointer_map(FileData *fd, struct Main *oldmain); diff --git a/source/blender/blenloader/intern/readfile_tempload.c b/source/blender/blenloader/intern/readfile_tempload.c index 4566e1e9b4d..f440a06acf8 100644 --- a/source/blender/blenloader/intern/readfile_tempload.c +++ b/source/blender/blenloader/intern/readfile_tempload.c @@ -40,7 +40,8 @@ TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main, /* Copy the file path so any path remapping is performed properly. */ STRNCPY(temp_lib_ctx->bmain_base->name, real_main->name); - temp_lib_ctx->blendhandle = BLO_blendhandle_from_file(blend_file_path, reports); + temp_lib_ctx->blendhandle = BLO_blendhandle_from_file( + blend_file_path, &(BlendFileReadReport){.reports = reports}); BLO_library_link_params_init( &temp_lib_ctx->liblink_params, temp_lib_ctx->bmain_base, 0, LIB_TAG_TEMP_MAIN); diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 7c5eefa60f7..fe3d7f8e98f 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -861,7 +861,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) int i; for (i = 0; i < 3; i++) { if ((ob->dsize[i] == 0.0f) || /* simple case, user never touched dsize */ - (ob->scale[i] == 0.0f)) /* cant scale the dsize to give a non zero result, + (ob->scale[i] == 0.0f)) /* can't scale the dsize to give a non zero result, * so fallback to 1.0f */ { ob->dscale[i] = 1.0f; diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 289092f7f19..776f6c54363 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -263,8 +263,8 @@ static void do_version_action_editor_properties_region(ListBase *regionbase) static void do_version_bones_super_bbone(ListBase *lb) { LISTBASE_FOREACH (Bone *, bone, lb) { - bone->scale_in_x = bone->scale_in_y = 1.0f; - bone->scale_out_x = bone->scale_out_y = 1.0f; + bone->scale_in_x = bone->scale_in_z = 1.0f; + bone->scale_out_x = bone->scale_out_z = 1.0f; do_version_bones_super_bbone(&bone->childbase); } @@ -1268,8 +1268,8 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) if (ob->pose) { LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { /* see do_version_bones_super_bbone()... */ - pchan->scale_in_x = pchan->scale_in_y = 1.0f; - pchan->scale_out_x = pchan->scale_out_y = 1.0f; + pchan->scale_in_x = pchan->scale_in_z = 1.0f; + pchan->scale_out_x = pchan->scale_out_z = 1.0f; /* also make sure some legacy (unused for over a decade) flags are unset, * so that we can reuse them for stuff that matters now... diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index cf082393803..0059074c8ad 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -709,8 +709,8 @@ static void do_versions_area_ensure_tool_region(Main *bmain, static void do_version_bones_split_bbone_scale(ListBase *lb) { LISTBASE_FOREACH (Bone *, bone, lb) { - bone->scale_in_y = bone->scale_in_x; - bone->scale_out_y = bone->scale_out_x; + bone->scale_in_z = bone->scale_in_x; + bone->scale_out_z = bone->scale_out_x; do_version_bones_split_bbone_scale(&bone->childbase); } @@ -1785,7 +1785,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) static void do_versions_seq_unique_name_all_strips(Scene *sce, ListBase *seqbasep) { for (Sequence *seq = seqbasep->first; seq != NULL; seq = seq->next) { - SEQ_sequence_base_unique_name_recursive(&sce->ed->seqbase, seq); + SEQ_sequence_base_unique_name_recursive(sce, &sce->ed->seqbase, seq); if (seq->seqbase.first != NULL) { do_versions_seq_unique_name_all_strips(sce, &seq->seqbase); } @@ -1912,7 +1912,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_END; if (error & NTREE_DOVERSION_NEED_OUTPUT) { - BKE_report(fd->reports, RPT_ERROR, "Eevee material conversion problem. Error in console"); + BKE_report(fd->reports != NULL ? fd->reports->reports : NULL, + RPT_ERROR, + "Eevee material conversion problem. Error in console"); printf( "You need to connect Principled and Eevee Specular shader nodes to new material " "output " @@ -1920,7 +1922,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (error & NTREE_DOVERSION_TRANSPARENCY_EMISSION) { - BKE_report(fd->reports, RPT_ERROR, "Eevee material conversion problem. Error in console"); + BKE_report(fd->reports != NULL ? fd->reports->reports : NULL, + RPT_ERROR, + "Eevee material conversion problem. Error in console"); printf( "You need to combine transparency and emission shaders to the converted Principled " "shader nodes.\n"); @@ -3172,7 +3176,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) bool is_blend = false; { - char tool = tool_init; + char tool; switch (tool_init) { case PAINT_BLEND_MIX: tool = VPAINT_TOOL_DRAW; @@ -3969,8 +3973,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (Object *, ob, &bmain->objects) { if (ob->pose) { LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - pchan->scale_in_y = pchan->scale_in_x; - pchan->scale_out_y = pchan->scale_out_x; + pchan->scale_in_z = pchan->scale_in_x; + pchan->scale_out_z = pchan->scale_out_x; } } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 565e62158ff..156b1f4198d 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -80,6 +80,7 @@ #include "BLO_readfile.h" #include "readfile.h" +#include "versioning_common.h" /* Make preferences read-only, use versioning_userdef.c. */ #define U (*((const UserDef *)&U)) @@ -117,24 +118,38 @@ static bool can_use_proxy(const Sequence *seq, int psize) } /* image_size is width or height depending what RNA property is converted - X or Y. */ -static void seq_convert_transform_animation(const Scene *scene, +static void seq_convert_transform_animation(const Sequence *seq, + const Scene *scene, const char *path, - const int image_size) + const int image_size, + const int scene_size) { if (scene->adt == NULL || scene->adt->action == NULL) { return; } - FCurve *fcu = BKE_fcurve_find(&scene->adt->action->curves, path, 0); - if (fcu != NULL && !BKE_fcurve_is_empty(fcu)) { - BezTriple *bezt = fcu->bezt; - for (int i = 0; i < fcu->totvert; i++, bezt++) { - /* Same math as with old_image_center_*, but simplified. */ - bezt->vec[0][1] = image_size / 2 + bezt->vec[0][1] - scene->r.xsch / 2; - bezt->vec[1][1] = image_size / 2 + bezt->vec[1][1] - scene->r.xsch / 2; - bezt->vec[2][1] = image_size / 2 + bezt->vec[2][1] - scene->r.xsch / 2; + /* Hardcoded legacy bit-flags which has been removed. */ + const uint32_t use_transform_flag = (1 << 16); + const uint32_t use_crop_flag = (1 << 17); + + /* Convert offset animation, but only if crop is not used. */ + if ((seq->flag & use_transform_flag) != 0 && (seq->flag & use_crop_flag) == 0) { + FCurve *fcu = BKE_fcurve_find(&scene->adt->action->curves, path, 0); + if (fcu != NULL && !BKE_fcurve_is_empty(fcu)) { + BezTriple *bezt = fcu->bezt; + for (int i = 0; i < fcu->totvert; i++, bezt++) { + /* Same math as with old_image_center_*, but simplified. */ + bezt->vec[0][1] = (image_size - scene_size) / 2 + bezt->vec[0][1]; + bezt->vec[1][1] = (image_size - scene_size) / 2 + bezt->vec[1][1]; + bezt->vec[2][1] = (image_size - scene_size) / 2 + bezt->vec[2][1]; + } } } + else { /* Else, remove offset animation. */ + FCurve *fcu = BKE_fcurve_find(&scene->adt->action->curves, path, 0); + BLI_remlink(&scene->adt->action->curves, fcu); + BKE_fcurve_free(fcu); + } } static void seq_convert_transform_crop(const Scene *scene, @@ -231,18 +246,15 @@ static void seq_convert_transform_crop(const Scene *scene, t->xofs = old_image_center_x - scene->r.xsch / 2; t->yofs = old_image_center_y - scene->r.ysch / 2; - /* Convert offset animation, but only if crop is not used. */ - if ((seq->flag & use_transform_flag) != 0 && (seq->flag & use_crop_flag) == 0) { - char name_esc[(sizeof(seq->name) - 2) * 2], *path; - BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); + char name_esc[(sizeof(seq->name) - 2) * 2], *path; + BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); - path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_x", name_esc); - seq_convert_transform_animation(scene, path, image_size_x); - MEM_freeN(path); - path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_y", name_esc); - seq_convert_transform_animation(scene, path, image_size_y); - MEM_freeN(path); - } + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_x", name_esc); + seq_convert_transform_animation(seq, scene, path, image_size_x, scene->r.xsch); + MEM_freeN(path); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_y", name_esc); + seq_convert_transform_animation(seq, scene, path, image_size_y, scene->r.ysch); + MEM_freeN(path); seq->flag &= ~use_transform_flag; seq->flag &= ~use_crop_flag; @@ -861,26 +873,6 @@ static void version_node_join_geometry_for_multi_input_socket(bNodeTree *ntree) } } -static ARegion *do_versions_add_region_if_not_found(ListBase *regionbase, - int region_type, - const char *name, - int link_after_region_type) -{ - ARegion *link_after_region = NULL; - LISTBASE_FOREACH (ARegion *, region, regionbase) { - if (region->regiontype == region_type) { - return NULL; - } - if (region->regiontype == link_after_region_type) { - link_after_region = region; - } - } - ARegion *new_region = MEM_callocN(sizeof(ARegion), name); - new_region->regiontype = region_type; - BLI_insertlinkafter(regionbase, link_after_region, new_region); - return new_region; -} - /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 93e2af1b68e..0fe1267b871 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -25,12 +25,16 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_genfile.h" #include "DNA_listBase.h" #include "DNA_modifier_types.h" #include "DNA_text_types.h" +#include "BKE_animsys.h" +#include "BKE_fcurve_driver.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" @@ -38,6 +42,10 @@ #include "BLO_readfile.h" #include "readfile.h" +#include "MEM_guardedalloc.h" + +#include "versioning_common.h" + static void sort_linked_ids(Main *bmain) { ListBase *lb; @@ -186,6 +194,83 @@ static void version_node_socket_name(bNodeTree *ntree, } } +static bool replace_bbone_len_scale_rnapath(char **p_old_path, int *p_index) +{ + char *old_path = *p_old_path; + + if (old_path == NULL) { + return false; + } + + int len = strlen(old_path); + + if (BLI_str_endswith(old_path, ".bbone_curveiny") || + BLI_str_endswith(old_path, ".bbone_curveouty")) { + old_path[len - 1] = 'z'; + return true; + } + + if (BLI_str_endswith(old_path, ".bbone_scaleinx") || + BLI_str_endswith(old_path, ".bbone_scaleiny") || + BLI_str_endswith(old_path, ".bbone_scaleoutx") || + BLI_str_endswith(old_path, ".bbone_scaleouty")) { + int index = (old_path[len - 1] == 'y' ? 2 : 0); + + old_path[len - 1] = 0; + + if (p_index) { + *p_index = index; + } + else { + *p_old_path = BLI_sprintfN("%s[%d]", old_path, index); + MEM_freeN(old_path); + } + + return true; + } + + return false; +} + +static void do_version_bbone_len_scale_fcurve_fix(FCurve *fcu) +{ + /* Update driver variable paths. */ + if (fcu->driver) { + LISTBASE_FOREACH (DriverVar *, dvar, &fcu->driver->variables) { + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + replace_bbone_len_scale_rnapath(&dtar->rna_path, NULL); + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* Update F-Curve's path. */ + replace_bbone_len_scale_rnapath(&fcu->rna_path, &fcu->array_index); +} + +static void do_version_bbone_len_scale_animdata_cb(ID *UNUSED(id), + AnimData *adt, + void *UNUSED(wrapper_data)) +{ + LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, &adt->drivers) { + do_version_bbone_len_scale_fcurve_fix(fcu); + } +} + +static void do_version_bones_bbone_len_scale(ListBase *lb) +{ + LISTBASE_FOREACH (Bone *, bone, lb) { + if (bone->flag & BONE_ADD_PARENT_END_ROLL) { + bone->bbone_flag |= BBONE_ADD_PARENT_END_ROLL; + } + + copy_v3_fl3(bone->scale_in, bone->scale_in_x, 1.0f, bone->scale_in_z); + copy_v3_fl3(bone->scale_out, bone->scale_out_x, 1.0f, bone->scale_out_z); + + do_version_bones_bbone_len_scale(&bone->childbase); + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -227,6 +312,86 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 4)) { + /* Add a properties sidebar to the spreadsheet editor. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SPREADSHEET) { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + ARegion *new_sidebar = do_versions_add_region_if_not_found( + regionbase, RGN_TYPE_UI, "sidebar for spreadsheet", RGN_TYPE_FOOTER); + if (new_sidebar != NULL) { + new_sidebar->alignment = RGN_ALIGN_RIGHT; + new_sidebar->flag |= RGN_FLAG_HIDDEN; + } + } + } + } + } + + /* Enable spreadsheet filtering in old files without row filters. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SPREADSHEET) { + SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; + sspreadsheet->filter_flag |= SPREADSHEET_FILTER_ENABLE; + } + } + } + } + + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + version_node_socket_name(ntree, GEO_NODE_BOUNDING_BOX, "Mesh", "Bounding Box"); + } + } + FOREACH_NODETREE_END; + + if (!DNA_struct_elem_find(fd->filesdna, "FileAssetSelectParams", "int", "import_type")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_FILE) { + SpaceFile *sfile = (SpaceFile *)sl; + if (sfile->asset_params) { + sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND; + } + } + } + } + } + } + + /* Initialize length-wise scale B-Bone settings. */ + if (!DNA_struct_elem_find(fd->filesdna, "Bone", "int", "bbone_flag")) { + /* Update armature data and pose channels. */ + LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) { + do_version_bones_bbone_len_scale(&arm->bonebase); + } + + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + copy_v3_fl3(pchan->scale_in, pchan->scale_in_x, 1.0f, pchan->scale_in_z); + copy_v3_fl3(pchan->scale_out, pchan->scale_out_x, 1.0f, pchan->scale_out_z); + } + } + } + + /* Update action curves and drivers. */ + LISTBASE_FOREACH (bAction *, act, &bmain->actions) { + LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, &act->curves) { + do_version_bbone_len_scale_fcurve_fix(fcu); + } + } + + BKE_animdata_main_cb(bmain, do_version_bbone_len_scale_animdata_cb, NULL); + } + } + /** * Versioning code until next subversion bump goes here. * @@ -238,11 +403,5 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_BOUNDING_BOX, "Mesh", "Bounding Box"); - } - } - FOREACH_NODETREE_END; } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc new file mode 100644 index 00000000000..f5083b8e259 --- /dev/null +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup blenloader + */ +/* allow readfile to use deprecated functionality */ +#define DNA_DEPRECATED_ALLOW + +#include "DNA_screen_types.h" + +#include "BLI_listbase.h" + +#include "MEM_guardedalloc.h" + +#include "versioning_common.h" + +ARegion *do_versions_add_region_if_not_found(ListBase *regionbase, + int region_type, + const char *name, + int link_after_region_type) +{ + ARegion *link_after_region = nullptr; + LISTBASE_FOREACH (ARegion *, region, regionbase) { + if (region->regiontype == region_type) { + return nullptr; + } + if (region->regiontype == link_after_region_type) { + link_after_region = region; + } + } + + ARegion *new_region = static_cast<ARegion *>(MEM_callocN(sizeof(ARegion), name)); + new_region->regiontype = region_type; + BLI_insertlinkafter(regionbase, link_after_region, new_region); + return new_region; +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h new file mode 100644 index 00000000000..a1769d4639e --- /dev/null +++ b/source/blender/blenloader/intern/versioning_common.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup blenloader + */ + +#pragma once + +struct ARegion; +struct ListBase; + +#ifdef __cplusplus +extern "C" { +#endif + +struct ARegion *do_versions_add_region_if_not_found(struct ListBase *regionbase, + int region_type, + const char *name, + int link_after_region_type); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 38c00b3f132..fbcf690a773 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -605,7 +605,7 @@ static void writelist_id(WriteData *wd, int filecode, const char *structname, co * \{ */ /** - * Take care using 'use_active_win', since we wont want the currently active window + * Take care using 'use_active_win', since we won't want the currently active window * to change which scene renders (currently only used for undo). */ static void current_screen_compat(Main *mainvar, diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc index 280a4b42b36..e93348b2158 100644 --- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc +++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc @@ -123,7 +123,8 @@ bool BlendfileLoadingBaseTest::blendfile_load(const char *filepath) char abspath[FILENAME_MAX]; BLI_path_join(abspath, sizeof(abspath), test_assets_dir.c_str(), filepath, NULL); - bfile = BLO_read_from_file(abspath, BLO_READ_SKIP_NONE, nullptr /* reports */); + BlendFileReadReport bf_reports = {NULL}; + bfile = BLO_read_from_file(abspath, BLO_READ_SKIP_NONE, &bf_reports); if (bfile == nullptr) { ADD_FAILURE() << "Unable to load file '" << filepath << "' from test assets dir '" << test_assets_dir << "'"; diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index c83fc0645c4..f60fc72f8a9 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -24,7 +24,7 @@ */ /* disable holes for now, - * these are ifdef'd because they use more memory and cant be saved in DNA currently */ + * these are ifdef'd because they use more memory and can't be saved in DNA currently */ // #define USE_BMESH_HOLES struct BMEdge; @@ -346,7 +346,7 @@ typedef struct BMesh { /* Should be copy of scene select mode. */ /* Stored in #BMEditMesh too, this is a bit confusing, * make sure they're in sync! - * Only use when the edit mesh cant be accessed - campbell */ + * Only use when the edit mesh can't be accessed - campbell */ short selectmode; /* ID of the shape key this bmesh came from */ diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 3eab252df7a..f79f1925560 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -673,7 +673,7 @@ BMesh *BM_mesh_copy(BMesh *bm_old) ftable = MEM_mallocN(sizeof(BMFace *) * bm_old->totface, "BM_mesh_copy ftable"); BM_ITER_MESH_INDEX (v, &iter, bm_old, BM_VERTS_OF_MESH, i) { - /* copy between meshes so cant use 'example' argument */ + /* copy between meshes so can't use 'example' argument */ v_new = BM_vert_create(bm_new, v->co, NULL, BM_CREATE_SKIP_CD); BM_elem_attrs_copy_ex(bm_old, bm_new, v, v_new, 0xff, 0x0); v_new->head.hflag = v->head.hflag; /* low level! don't do this for normal api use */ diff --git a/source/blender/bmesh/intern/bmesh_inline.h b/source/blender/bmesh/intern/bmesh_inline.h index 2888d7e7526..4350b4d04ed 100644 --- a/source/blender/bmesh/intern/bmesh_inline.h +++ b/source/blender/bmesh/intern/bmesh_inline.h @@ -95,7 +95,7 @@ BLI_INLINE void _bm_elem_flag_merge_into(BMHeader *head, /** * notes on #BM_elem_index_set(...) usage, - * Set index is sometimes abused as temp storage, other times we cant be + * Set index is sometimes abused as temp storage, other times we can't be * sure if the index values are valid because certain operations have modified * the mesh structure. * diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index d479a555a58..190698f504c 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -33,6 +33,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_task.h" #include "bmesh.h" #include "bmesh_structure.h" @@ -40,31 +41,123 @@ /* For '_FLAG_OVERLAP'. */ #include "bmesh_private.h" -static void recount_totsels(BMesh *bm) +/* -------------------------------------------------------------------- */ +/** \name Recounting total selection. + * \{ */ + +typedef struct SelectionCountChunkData { + int selection_len; +} SelectionCountChunkData; + +static void recount_totsels_range_vert_func(void *UNUSED(userdata), + MempoolIterData *iter, + const TaskParallelTLS *__restrict tls) { - const char iter_types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH}; - int *tots[3]; - int i; + SelectionCountChunkData *count = tls->userdata_chunk; + const BMVert *eve = (const BMVert *)iter; + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + count->selection_len += 1; + } +} - /* Recount total selection variables. */ - bm->totvertsel = bm->totedgesel = bm->totfacesel = 0; - tots[0] = &bm->totvertsel; - tots[1] = &bm->totedgesel; - tots[2] = &bm->totfacesel; +static void recount_totsels_range_edge_func(void *UNUSED(userdata), + MempoolIterData *iter, + const TaskParallelTLS *__restrict tls) +{ + SelectionCountChunkData *count = tls->userdata_chunk; + const BMEdge *eed = (const BMEdge *)iter; + if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + count->selection_len += 1; + } +} - for (i = 0; i < 3; i++) { - BMIter iter; - BMElem *ele; - int count = 0; +static void recount_totsels_range_face_func(void *UNUSED(userdata), + MempoolIterData *iter, + const TaskParallelTLS *__restrict tls) +{ + SelectionCountChunkData *count = tls->userdata_chunk; + const BMFace *efa = (const BMFace *)iter; + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + count->selection_len += 1; + } +} - BM_ITER_MESH (ele, &iter, bm, iter_types[i]) { - if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) { - count += 1; - } - } - *tots[i] = count; +static void recount_totsels_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + SelectionCountChunkData *dst = chunk_join; + const SelectionCountChunkData *src = chunk; + dst->selection_len += src->selection_len; +} + +static TaskParallelMempoolFunc recount_totsels_get_range_func(BMIterType iter_type) +{ + BLI_assert(ELEM(iter_type, BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH)); + + TaskParallelMempoolFunc range_func = NULL; + if (iter_type == BM_VERTS_OF_MESH) { + range_func = recount_totsels_range_vert_func; + } + else if (iter_type == BM_EDGES_OF_MESH) { + range_func = recount_totsels_range_edge_func; } + else if (iter_type == BM_FACES_OF_MESH) { + range_func = recount_totsels_range_face_func; + } + return range_func; +} + +static int recount_totsel(BMesh *bm, BMIterType iter_type) +{ + const int MIN_ITER_SIZE = 1024; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.func_reduce = recount_totsels_reduce; + settings.min_iter_per_thread = MIN_ITER_SIZE; + + SelectionCountChunkData count = {0}; + settings.userdata_chunk = &count; + settings.userdata_chunk_size = sizeof(count); + + TaskParallelMempoolFunc range_func = recount_totsels_get_range_func(iter_type); + BM_iter_parallel(bm, iter_type, range_func, NULL, &settings); + return count.selection_len; +} + +static void recount_totvertsel(BMesh *bm) +{ + bm->totvertsel = recount_totsel(bm, BM_VERTS_OF_MESH); +} + +static void recount_totedgesel(BMesh *bm) +{ + bm->totedgesel = recount_totsel(bm, BM_EDGES_OF_MESH); +} + +static void recount_totfacesel(BMesh *bm) +{ + bm->totfacesel = recount_totsel(bm, BM_FACES_OF_MESH); +} + +static void recount_totsels(BMesh *bm) +{ + recount_totvertsel(bm); + recount_totedgesel(bm); + recount_totfacesel(bm); +} + +#ifndef NDEBUG +static bool recount_totsels_are_ok(BMesh *bm) +{ + return bm->totvertsel == recount_totsel(bm, BM_VERTS_OF_MESH) && + bm->totedgesel == recount_totsel(bm, BM_EDGES_OF_MESH) && + bm->totfacesel == recount_totsel(bm, BM_FACES_OF_MESH); } +#endif + +/** \} */ /* -------------------------------------------------------------------- */ /** \name BMesh helper functions for selection & hide flushing. @@ -231,6 +324,106 @@ void BM_mesh_select_mode_clean(BMesh *bm) BM_mesh_select_mode_clean_ex(bm, bm->selectmode); } +/* -------------------------------------------------------------------- */ +/** \name Select mode flush selection + * \{ */ + +typedef struct SelectionFlushChunkData { + int delta_selection_len; +} SelectionFlushChunkData; + +static void bm_mesh_select_mode_flush_vert_to_edge_iter_fn(void *UNUSED(userdata), + MempoolIterData *iter, + const TaskParallelTLS *__restrict tls) +{ + SelectionFlushChunkData *chunk_data = tls->userdata_chunk; + BMEdge *e = (BMEdge *)iter; + const bool is_selected = BM_elem_flag_test(e, BM_ELEM_SELECT); + const bool is_hidden = BM_elem_flag_test(e, BM_ELEM_HIDDEN); + if (!is_hidden && + (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && BM_elem_flag_test(e->v2, BM_ELEM_SELECT))) { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + chunk_data->delta_selection_len += is_selected ? 0 : 1; + } + else { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + chunk_data->delta_selection_len += is_selected ? -1 : 0; + } +} + +static void bm_mesh_select_mode_flush_edge_to_face_iter_fn(void *UNUSED(userdata), + MempoolIterData *iter, + const TaskParallelTLS *__restrict tls) +{ + SelectionFlushChunkData *chunk_data = tls->userdata_chunk; + BMFace *f = (BMFace *)iter; + BMLoop *l_iter; + BMLoop *l_first; + const bool is_selected = BM_elem_flag_test(f, BM_ELEM_SELECT); + bool ok = true; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(l_iter->e, BM_ELEM_SELECT)) { + ok = false; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = false; + } + + BM_elem_flag_set(f, BM_ELEM_SELECT, ok); + if (is_selected && !ok) { + chunk_data->delta_selection_len -= 1; + } + else if (ok && !is_selected) { + chunk_data->delta_selection_len += 1; + } +} + +static void bm_mesh_select_mode_flush_reduce_fn(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + SelectionFlushChunkData *dst = chunk_join; + const SelectionFlushChunkData *src = chunk; + dst->delta_selection_len += src->delta_selection_len; +} + +static void bm_mesh_select_mode_flush_vert_to_edge(BMesh *bm) +{ + SelectionFlushChunkData chunk_data = {0}; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = bm->totedge >= BM_OMP_LIMIT; + settings.userdata_chunk = &chunk_data; + settings.userdata_chunk_size = sizeof(chunk_data); + settings.func_reduce = bm_mesh_select_mode_flush_reduce_fn; + + BM_iter_parallel( + bm, BM_EDGES_OF_MESH, bm_mesh_select_mode_flush_vert_to_edge_iter_fn, NULL, &settings); + bm->totedgesel += chunk_data.delta_selection_len; +} + +static void bm_mesh_select_mode_flush_edge_to_face(BMesh *bm) +{ + SelectionFlushChunkData chunk_data = {0}; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = bm->totface >= BM_OMP_LIMIT; + settings.userdata_chunk = &chunk_data; + settings.userdata_chunk_size = sizeof(chunk_data); + settings.func_reduce = bm_mesh_select_mode_flush_reduce_fn; + + BM_iter_parallel( + bm, BM_FACES_OF_MESH, bm_mesh_select_mode_flush_edge_to_face_iter_fn, NULL, &settings); + bm->totfacesel += chunk_data.delta_selection_len; +} + /** * \brief Select Mode Flush * @@ -238,76 +431,38 @@ void BM_mesh_select_mode_clean(BMesh *bm) * (ie: all verts of an edge selects the edge and so on). * This should only be called by system and not tool authors. */ -void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode) +void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, eBMSelectionFlushFLags flags) { - BMEdge *e; - BMLoop *l_iter; - BMLoop *l_first; - BMFace *f; - - BMIter eiter; - BMIter fiter; - if (selectmode & SCE_SELECT_VERTEX) { - /* both loops only set edge/face flags and read off verts */ - BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && BM_elem_flag_test(e->v2, BM_ELEM_SELECT) && - !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - BM_elem_flag_enable(e, BM_ELEM_SELECT); - } - else { - BM_elem_flag_disable(e, BM_ELEM_SELECT); - } - } - BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { - bool ok = true; - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { - ok = false; - break; - } - } while ((l_iter = l_iter->next) != l_first); - } - else { - ok = false; - } - - BM_elem_flag_set(f, BM_ELEM_SELECT, ok); - } + bm_mesh_select_mode_flush_vert_to_edge(bm); } - else if (selectmode & SCE_SELECT_EDGE) { - BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { - bool ok = true; - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (!BM_elem_flag_test(l_iter->e, BM_ELEM_SELECT)) { - ok = false; - break; - } - } while ((l_iter = l_iter->next) != l_first); - } - else { - ok = false; - } - BM_elem_flag_set(f, BM_ELEM_SELECT, ok); - } + if (selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) { + bm_mesh_select_mode_flush_edge_to_face(bm); } /* Remove any deselected elements from the BMEditSelection */ BM_select_history_validate(bm); - recount_totsels(bm); + if (flags & BM_SELECT_LEN_FLUSH_RECALC_VERT) { + recount_totvertsel(bm); + } + if (flags & BM_SELECT_LEN_FLUSH_RECALC_EDGE) { + recount_totedgesel(bm); + } + if (flags & BM_SELECT_LEN_FLUSH_RECALC_FACE) { + recount_totfacesel(bm); + } + BLI_assert(recount_totsels_are_ok(bm)); } void BM_mesh_select_mode_flush(BMesh *bm) { - BM_mesh_select_mode_flush_ex(bm, bm->selectmode); + BM_mesh_select_mode_flush_ex(bm, bm->selectmode, BM_SELECT_LEN_FLUSH_RECALC_ALL); } +/** \} */ + /** * mode independent flushing up/down */ diff --git a/source/blender/bmesh/intern/bmesh_marking.h b/source/blender/bmesh/intern/bmesh_marking.h index 04a49e24757..ff6c359138a 100644 --- a/source/blender/bmesh/intern/bmesh_marking.h +++ b/source/blender/bmesh/intern/bmesh_marking.h @@ -26,6 +26,16 @@ typedef struct BMEditSelection { char htype; } BMEditSelection; +typedef enum eBMSelectionFlushFLags { + BM_SELECT_LEN_FLUSH_RECALC_NOTHING = 0, + BM_SELECT_LEN_FLUSH_RECALC_VERT = (1 << 0), + BM_SELECT_LEN_FLUSH_RECALC_EDGE = (1 << 1), + BM_SELECT_LEN_FLUSH_RECALC_FACE = (1 << 2), + BM_SELECT_LEN_FLUSH_RECALC_ALL = (BM_SELECT_LEN_FLUSH_RECALC_VERT | + BM_SELECT_LEN_FLUSH_RECALC_EDGE | + BM_SELECT_LEN_FLUSH_RECALC_FACE), +} eBMSelectionFlushFLags; + /* geometry hiding code */ #define BM_elem_hide_set(bm, ele, hide) _bm_elem_hide_set(bm, &(ele)->head, hide) void _bm_elem_hide_set(BMesh *bm, BMHeader *head, const bool hide); @@ -72,7 +82,7 @@ void BM_mesh_select_mode_clean_ex(BMesh *bm, const short selectmode); void BM_mesh_select_mode_clean(BMesh *bm); void BM_mesh_select_mode_set(BMesh *bm, int selectmode); -void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode); +void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, eBMSelectionFlushFLags flags); void BM_mesh_select_mode_flush(BMesh *bm); void BM_mesh_deselect_flush(BMesh *bm); diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index d0c6bc83088..80028564ff2 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -840,8 +840,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BMVert **verts_pool, *verts_copy, **vep; int i, totvert = bm->totvert; const uint *new_idx; - /* Special case: Python uses custom - data layers to hold PyObject references. - * These have to be kept in - place, else the PyObject's we point to, wont point back to us. */ + /* Special case: Python uses custom data layers to hold PyObject references. + * These have to be kept in place, else the PyObjects we point to, won't point back to us. */ const int cd_vert_pyptr = CustomData_get_offset(&bm->vdata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ @@ -892,8 +892,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BMEdge **edges_pool, *edges_copy, **edp; int i, totedge = bm->totedge; const uint *new_idx; - /* Special case: Python uses custom - data layers to hold PyObject references. - * These have to be kept in - place, else the PyObject's we point to, wont point back to us. */ + /* Special case: Python uses custom data layers to hold PyObject references. + * These have to be kept in place, else the PyObjects we point to, won't point back to us. */ const int cd_edge_pyptr = CustomData_get_offset(&bm->edata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ @@ -943,8 +943,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BMFace **faces_pool, *faces_copy, **fap; int i, totface = bm->totface; const uint *new_idx; - /* Special case: Python uses custom - data layers to hold PyObject references. - * These have to be kept in - place, else the PyObject's we point to, wont point back to us. */ + /* Special case: Python uses custom data layers to hold PyObject references. + * These have to be kept in place, else the PyObjects we point to, won't point back to us. */ const int cd_poly_pyptr = CustomData_get_offset(&bm->pdata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index a3eae6dabe8..bddd3da98b7 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -49,67 +49,9 @@ * assuming no other tool using it would run concurrently to clnors editing. */ #define BM_LNORSPACE_UPDATE _FLAG_MF -typedef struct BMEdgesCalcVectorsData { - /* Read-only data. */ - const float (*vcos)[3]; - - /* Read-write data, but no need to protect it, no concurrency to fear here. */ - float (*edgevec)[3]; -} BMEdgesCalcVectorsData; - -static void bm_edge_calc_vectors_cb(void *userdata, - MempoolIterData *mp_e, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - BMEdge *e = (BMEdge *)mp_e; - /* The edge vector will not be needed when the edge has no radial. */ - if (e->l != NULL) { - float(*edgevec)[3] = userdata; - float *e_diff = edgevec[BM_elem_index_get(e)]; - sub_v3_v3v3(e_diff, e->v2->co, e->v1->co); - normalize_v3(e_diff); - } -} - -static void bm_edge_calc_vectors_with_coords_cb(void *userdata, - MempoolIterData *mp_e, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - BMEdge *e = (BMEdge *)mp_e; - /* The edge vector will not be needed when the edge has no radial. */ - if (e->l != NULL) { - BMEdgesCalcVectorsData *data = userdata; - float *e_diff = data->edgevec[BM_elem_index_get(e)]; - sub_v3_v3v3( - e_diff, data->vcos[BM_elem_index_get(e->v2)], data->vcos[BM_elem_index_get(e->v1)]); - normalize_v3(e_diff); - } -} - -static void bm_mesh_edges_calc_vectors(BMesh *bm, float (*edgevec)[3], const float (*vcos)[3]) -{ - BM_mesh_elem_index_ensure(bm, BM_EDGE | (vcos ? BM_VERT : 0)); - - TaskParallelSettings settings; - BLI_parallel_mempool_settings_defaults(&settings); - settings.use_threading = bm->totedge >= BM_OMP_LIMIT; - - if (vcos == NULL) { - BM_iter_parallel(bm, BM_EDGES_OF_MESH, bm_edge_calc_vectors_cb, edgevec, &settings); - } - else { - BMEdgesCalcVectorsData data = { - .edgevec = edgevec, - .vcos = vcos, - }; - BM_iter_parallel(bm, BM_EDGES_OF_MESH, bm_edge_calc_vectors_with_coords_cb, &data, &settings); - } -} - typedef struct BMVertsCalcNormalsWithCoordsData { /* Read-only data. */ const float (*fnos)[3]; - const float (*edgevec)[3]; const float (*vcos)[3]; /* Write data. */ @@ -117,13 +59,12 @@ typedef struct BMVertsCalcNormalsWithCoordsData { } BMVertsCalcNormalsWithCoordsData; BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter, - const float (*edgevec)[3], + const float e1diff[3], + const float e2diff[3], const float f_no[3], float v_no[3]) { /* Calculate the dot product of the two edges that meet at the loop's vertex. */ - const float *e1diff = edgevec[BM_elem_index_get(l_iter->prev->e)]; - const float *e2diff = edgevec[BM_elem_index_get(l_iter->e)]; /* Edge vectors are calculated from e->v1 to e->v2, so adjust the dot product if one but not * both loops actually runs from from e->v2 to e->v1. */ float dotprod = dot_v3v3(e1diff, e2diff); @@ -132,25 +73,61 @@ BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter, } const float fac = saacos(-dotprod); /* NAN detection, otherwise this is a degenerated case, ignore that vertex in this case. */ - if (fac == fac) { /* NAN detection. */ + if (fac == fac) { madd_v3_v3fl(v_no, f_no, fac); } } -static void bm_vert_calc_normals_impl(const float (*edgevec)[3], BMVert *v) +static void bm_vert_calc_normals_impl(BMVert *v) { + /* Note on redundant unit-length edge-vector calculation: + * + * This functions calculates unit-length edge-vector for every loop edge + * in practice this means 2x `sqrt` calls per face-corner connected to each vertex. + * + * Previously (2.9x and older), the edge vectors were calculated and stored for reuse. + * However the overhead of did not perform well (~16% slower - single & multi-threaded) + * when compared with calculating the values as they are needed. + * + * For simple grid topologies this function calculates the edge-vectors 4x times. + * There is some room for improved performance by storing the edge-vectors for reuse locally + * in this function, reducing the number of redundant `sqrtf` in half (2x instead of 4x). + * so face loops that share an edge would not calculate it multiple times. + * From my tests the performance improvements are so small they're difficult to measure, + * the time saved removing `sqrtf` calls is lost on storing and looking up the information, + * even in the case of `BLI_smallhash.h` & small inline lookup tables. + * + * Further, local data structures would need to support cases where + * stack memory isn't sufficient - adding additional complexity for corner-cases + * (a vertex that has thousands of connected edges for example). + * Unless there are important use-cases that benefit from edge-vector caching, + * keep this simple and calculate ~4x as many edge-vectors. + * + * In conclusion, the cost of caching & looking up edge-vectors both globally or per-vertex + * doesn't save enough time to make it worthwhile. + * - Campbell. */ + float *v_no = v->no; zero_v3(v_no); + BMEdge *e_first = v->e; if (e_first != NULL) { + float e1diff[3], e2diff[3]; BMEdge *e_iter = e_first; do { BMLoop *l_first = e_iter->l; if (l_first != NULL) { + sub_v3_v3v3(e2diff, e_iter->v1->co, e_iter->v2->co); + normalize_v3(e2diff); + BMLoop *l_iter = l_first; do { if (l_iter->v == v) { - bm_vert_calc_normals_accum_loop(l_iter, edgevec, l_iter->f->no, v_no); + BMEdge *e_prev = l_iter->prev->e; + sub_v3_v3v3(e1diff, e_prev->v1->co, e_prev->v2->co); + normalize_v3(e1diff); + + bm_vert_calc_normals_accum_loop(l_iter, e1diff, e2diff, l_iter->f->no, v_no); } } while ((l_iter = l_iter->radial_next) != l_first); } @@ -164,32 +141,44 @@ static void bm_vert_calc_normals_impl(const float (*edgevec)[3], BMVert *v) normalize_v3_v3(v_no, v->co); } -static void bm_vert_calc_normals_cb(void *userdata, +static void bm_vert_calc_normals_cb(void *UNUSED(userdata), MempoolIterData *mp_v, const TaskParallelTLS *__restrict UNUSED(tls)) { - const float(*edgevec)[3] = userdata; BMVert *v = (BMVert *)mp_v; - bm_vert_calc_normals_impl(edgevec, v); + bm_vert_calc_normals_impl(v); } static void bm_vert_calc_normals_with_coords(BMVert *v, BMVertsCalcNormalsWithCoordsData *data) { + /* See #bm_vert_calc_normals_impl note on performance. */ float *v_no = data->vnos[BM_elem_index_get(v)]; zero_v3(v_no); /* Loop over edges. */ BMEdge *e_first = v->e; if (e_first != NULL) { + float e1diff[3], e2diff[3]; BMEdge *e_iter = e_first; do { BMLoop *l_first = e_iter->l; if (l_first != NULL) { + sub_v3_v3v3(e2diff, + data->vcos[BM_elem_index_get(e_iter->v1)], + data->vcos[BM_elem_index_get(e_iter->v2)]); + normalize_v3(e2diff); + BMLoop *l_iter = l_first; do { if (l_iter->v == v) { + BMEdge *e_prev = l_iter->prev->e; + sub_v3_v3v3(e1diff, + data->vcos[BM_elem_index_get(e_prev->v1)], + data->vcos[BM_elem_index_get(e_prev->v2)]); + normalize_v3(e1diff); + bm_vert_calc_normals_accum_loop( - l_iter, data->edgevec, data->fnos[BM_elem_index_get(l_iter->f)], v_no); + l_iter, e1diff, e2diff, data->fnos[BM_elem_index_get(l_iter->f)], v_no); } } while ((l_iter = l_iter->radial_next) != l_first); } @@ -213,24 +202,22 @@ static void bm_vert_calc_normals_with_coords_cb(void *userdata, } static void bm_mesh_verts_calc_normals(BMesh *bm, - const float (*edgevec)[3], const float (*fnos)[3], const float (*vcos)[3], float (*vnos)[3]) { - BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE) | ((vnos || vcos) ? BM_VERT : 0)); + BM_mesh_elem_index_ensure(bm, BM_FACE | ((vnos || vcos) ? BM_VERT : 0)); TaskParallelSettings settings; BLI_parallel_mempool_settings_defaults(&settings); settings.use_threading = bm->totvert >= BM_OMP_LIMIT; if (vcos == NULL) { - BM_iter_parallel(bm, BM_VERTS_OF_MESH, bm_vert_calc_normals_cb, (void *)edgevec, &settings); + BM_iter_parallel(bm, BM_VERTS_OF_MESH, bm_vert_calc_normals_cb, NULL, &settings); } else { BLI_assert(!ELEM(NULL, fnos, vnos)); BMVertsCalcNormalsWithCoordsData data = { - .edgevec = edgevec, .fnos = fnos, .vcos = vcos, .vnos = vnos, @@ -253,25 +240,27 @@ static void bm_face_calc_normals_cb(void *UNUSED(userdata), * * Updates the normals of a mesh. */ -void BM_mesh_normals_update(BMesh *bm) +void BM_mesh_normals_update_ex(BMesh *bm, const struct BMeshNormalsUpdate_Params *params) { - float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__); + if (params->face_normals) { + /* Calculate all face normals. */ + TaskParallelSettings settings; + BLI_parallel_mempool_settings_defaults(&settings); + settings.use_threading = bm->totedge >= BM_OMP_LIMIT; - /* Parallel mempool iteration does not allow generating indices inline anymore. */ - BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE)); - - /* Calculate all face normals. */ - TaskParallelSettings settings; - BLI_parallel_mempool_settings_defaults(&settings); - settings.use_threading = bm->totedge >= BM_OMP_LIMIT; - - BM_iter_parallel(bm, BM_FACES_OF_MESH, bm_face_calc_normals_cb, NULL, &settings); - - bm_mesh_edges_calc_vectors(bm, edgevec, NULL); + BM_iter_parallel(bm, BM_FACES_OF_MESH, bm_face_calc_normals_cb, NULL, &settings); + } /* Add weighted face normals to vertices, and normalize vert normals. */ - bm_mesh_verts_calc_normals(bm, edgevec, NULL, NULL, NULL); - MEM_freeN(edgevec); + bm_mesh_verts_calc_normals(bm, NULL, NULL, NULL); +} + +void BM_mesh_normals_update(BMesh *bm) +{ + BM_mesh_normals_update_ex(bm, + &(const struct BMeshNormalsUpdate_Params){ + .face_normals = true, + }); } /** \} */ @@ -287,99 +276,49 @@ static void bm_partial_faces_parallel_range_calc_normals_cb( BM_face_calc_normal(f, f->no); } -static void bm_partial_edges_parallel_range_calc_vectors_cb( - void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) -{ - BMEdge *e = ((BMEdge **)((void **)userdata)[0])[iter]; - float *r_edgevec = ((float(*)[3])((void **)userdata)[1])[iter]; - sub_v3_v3v3(r_edgevec, e->v1->co, e->v2->co); - normalize_v3(r_edgevec); -} - static void bm_partial_verts_parallel_range_calc_normal_cb( void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) { - BMVert *v = ((BMVert **)((void **)userdata)[0])[iter]; - const float(*edgevec)[3] = (const float(*)[3])((void **)userdata)[1]; - bm_vert_calc_normals_impl(edgevec, v); + BMVert *v = ((BMVert **)userdata)[iter]; + bm_vert_calc_normals_impl(v); } /** * A version of #BM_mesh_normals_update that updates a subset of geometry, * used to avoid the overhead of updating everything. */ -void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpinfo) +void BM_mesh_normals_update_with_partial_ex(BMesh *UNUSED(bm), + const BMPartialUpdate *bmpinfo, + const struct BMeshNormalsUpdate_Params *params) { BLI_assert(bmpinfo->params.do_normals); BMVert **verts = bmpinfo->verts; - BMEdge **edges = bmpinfo->edges; BMFace **faces = bmpinfo->faces; const int verts_len = bmpinfo->verts_len; - const int edges_len = bmpinfo->edges_len; const int faces_len = bmpinfo->faces_len; - float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * edges_len, __func__); - TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); /* Faces. */ - BLI_task_parallel_range( - 0, faces_len, faces, bm_partial_faces_parallel_range_calc_normals_cb, &settings); - - /* Temporarily override the edge indices, - * storing the correct indices in the case they're not dirty. - * - * \note in most cases indices are modified and #BMesh.elem_index_dirty is set. - * This is an exceptional case where indices are restored because the worst case downside - * of marking the edge indices dirty would require a full loop over all edges to - * correct the indices in other functions which need them to be valid. - * When moving a few vertices on a high poly mesh setting and restoring connected - * edges has very little overhead compared with restoring all edge indices. */ - int *edge_index_value = NULL; - if ((bm->elem_index_dirty & BM_EDGE) == 0) { - edge_index_value = MEM_mallocN(sizeof(*edge_index_value) * edges_len, __func__); - - for (int i = 0; i < edges_len; i++) { - BMEdge *e = edges[i]; - edge_index_value[i] = BM_elem_index_get(e); - BM_elem_index_set(e, i); /* set_dirty! (restore before this function exits). */ - } - } - else { - for (int i = 0; i < edges_len; i++) { - BMEdge *e = edges[i]; - BM_elem_index_set(e, i); /* set_dirty! (already dirty) */ - } - } - - { - /* Verts. */ - - /* Compute normalized direction vectors for each edge. - * Directions will be used for calculating the weights of the face normals on the vertex - * normals. */ - void *data[2] = {edges, edgevec}; + if (params->face_normals) { BLI_task_parallel_range( - 0, edges_len, data, bm_partial_edges_parallel_range_calc_vectors_cb, &settings); - - /* Calculate vertex normals. */ - data[0] = verts; - BLI_task_parallel_range( - 0, verts_len, data, bm_partial_verts_parallel_range_calc_normal_cb, &settings); + 0, faces_len, faces, bm_partial_faces_parallel_range_calc_normals_cb, &settings); } - if (edge_index_value != NULL) { - for (int i = 0; i < edges_len; i++) { - BMEdge *e = edges[i]; - BM_elem_index_set(e, edge_index_value[i]); /* set_ok (restore) */ - } - - MEM_freeN(edge_index_value); - } + /* Verts. */ + BLI_task_parallel_range( + 0, verts_len, verts, bm_partial_verts_parallel_range_calc_normal_cb, &settings); +} - MEM_freeN(edgevec); +void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpinfo) +{ + BM_mesh_normals_update_with_partial_ex(bm, + bmpinfo, + &(const struct BMeshNormalsUpdate_Params){ + .face_normals = true, + }); } /** \} */ @@ -399,16 +338,8 @@ void BM_verts_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], float (*vnos)[3]) { - float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__); - - /* Compute normalized direction vectors for each edge. - * Directions will be used for calculating the weights of the face normals on the vertex normals. - */ - bm_mesh_edges_calc_vectors(bm, edgevec, vcos); - /* Add weighted face normals to vertices, and normalize vert normals. */ - bm_mesh_verts_calc_normals(bm, edgevec, fnos, vcos, vnos); - MEM_freeN(edgevec); + bm_mesh_verts_calc_normals(bm, fnos, vcos, vnos); } /** \} */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.h b/source/blender/bmesh/intern/bmesh_mesh_normals.h index 41191340e9e..ecd627d4bfe 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.h +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.h @@ -22,7 +22,19 @@ #include "bmesh_class.h" +struct BMeshNormalsUpdate_Params { + /** + * When calculating tessellation as well as normals, tessellate & calculate face normals + * for improved performance. See #BMeshCalcTessellation_Params + */ + bool face_normals; +}; + +void BM_mesh_normals_update_ex(BMesh *bm, const struct BMeshNormalsUpdate_Params *param); void BM_mesh_normals_update(BMesh *bm); +void BM_mesh_normals_update_with_partial_ex(BMesh *bm, + const struct BMPartialUpdate *bmpinfo, + const struct BMeshNormalsUpdate_Params *param); void BM_mesh_normals_update_with_partial(BMesh *bm, const struct BMPartialUpdate *bmpinfo); void BM_verts_calc_normal_vcos(BMesh *bm, diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c index 7b01b61d4fa..267705aa7c7 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c @@ -79,20 +79,6 @@ BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo, return false; } -BLI_INLINE bool partial_elem_edge_ensure(BMPartialUpdate *bmpinfo, - BLI_bitmap *edges_tag, - BMEdge *e) -{ - const int i = BM_elem_index_get(e); - if (!BLI_BITMAP_TEST(edges_tag, i)) { - BLI_BITMAP_ENABLE(edges_tag, i); - GROW_ARRAY_AS_NEEDED(bmpinfo->edges, bmpinfo->edges_len_alloc, bmpinfo->edges_len); - bmpinfo->edges[bmpinfo->edges_len++] = e; - return true; - } - return false; -} - BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo, BLI_bitmap *faces_tag, BMFace *f) @@ -121,17 +107,15 @@ BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, /* Reserve more edges than vertices since it's common for a grid topology * to use around twice as many edges as vertices. */ const int default_verts_len_alloc = verts_len; - const int default_edges_len_alloc = min_ii(bm->totedge, verts_len * 2); const int default_faces_len_alloc = min_ii(bm->totface, verts_len); /* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags. * Further, walking over all geometry to clear the tags isn't so efficient. */ BLI_bitmap *verts_tag = NULL; - BLI_bitmap *edges_tag = NULL; BLI_bitmap *faces_tag = NULL; /* Set vert inline. */ - BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE)); + BM_mesh_elem_index_ensure(bm, BM_FACE); if (params->do_normals || params->do_tessellate) { /* - Extend to all vertices connected faces: @@ -197,29 +181,12 @@ BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, verts_tag = BLI_BITMAP_NEW((size_t)bm->totvert, __func__); } - /* Edges. */ - if (bmpinfo->edges == NULL) { - bmpinfo->edges_len_alloc = default_edges_len_alloc; - bmpinfo->edges = MEM_mallocN((sizeof(BMEdge *) * bmpinfo->edges_len_alloc), __func__); - edges_tag = BLI_BITMAP_NEW((size_t)bm->totedge, __func__); - } - for (int i = 0; i < bmpinfo->faces_len; i++) { BMFace *f = bmpinfo->faces[i]; BMLoop *l_iter, *l_first; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { - if (!partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v)) { - continue; - } - BMVert *v = l_iter->v; - BMEdge *e_first = v->e; - BMEdge *e_iter = e_first; - do { - if (e_iter->l) { - partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter); - } - } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v); } while ((l_iter = l_iter->next) != l_first); } } @@ -227,9 +194,6 @@ BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, if (verts_tag) { MEM_freeN(verts_tag); } - if (edges_tag) { - MEM_freeN(edges_tag); - } if (faces_tag) { MEM_freeN(faces_tag); } @@ -244,9 +208,6 @@ void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo) if (bmpinfo->verts) { MEM_freeN(bmpinfo->verts); } - if (bmpinfo->edges) { - MEM_freeN(bmpinfo->edges); - } if (bmpinfo->faces) { MEM_freeN(bmpinfo->faces); } diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.h b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h index b31ec127744..3dbfb985e92 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_partial_update.h +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h @@ -44,10 +44,8 @@ typedef struct BMPartialUpdate_Params { */ typedef struct BMPartialUpdate { BMVert **verts; - BMEdge **edges; BMFace **faces; int verts_len, verts_len_alloc; - int edges_len, edges_len_alloc; int faces_len, faces_len_alloc; /** Store the parameters used in creation so invalid use can be asserted. */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c index 7a95e52ce25..c9b027474e1 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -19,6 +19,8 @@ * * This file contains code for polygon tessellation * (creating triangles from polygons). + * + * \see mesh_tessellate.c for the #Mesh equivalent of this file. */ #include "DNA_meshdata_types.h" @@ -50,10 +52,21 @@ /** \name Default Mesh Tessellation * \{ */ -static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], - BMFace *efa, - MemArena **pf_arena_p) +/** + * \param face_normal: This will be optimized out as a constant. + */ +BLI_INLINE void bmesh_calc_tessellation_for_face_impl(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p, + const bool face_normal) { +#ifdef DEBUG + /* The face normal is used for projecting faces into 2D space for tessellation. + * Invalid normals may result in invalid tessellation. + * Either `face_normal` should be true or normals should be updated first. */ + BLI_assert(face_normal || BM_face_is_normal_valid(efa)); +#endif + switch (efa->len) { case 3: { /* `0 1 2` -> `0 1 2` */ @@ -62,7 +75,10 @@ static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); l_ptr[1] = l = l->next; l_ptr[2] = l->next; - return 1; + if (face_normal) { + normal_tri_v3(efa->no, l_ptr[0]->v->co, l_ptr[1]->v->co, l_ptr[2]->v->co); + } + break; } case 4: { /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ @@ -74,15 +90,27 @@ static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], (l_ptr_a[2] = l_ptr_b[1] = l = l->next); (l_ptr_b[2] = l->next); - if (UNLIKELY(is_quad_flip_v3_first_third_fast( - l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { + if (face_normal) { + normal_quad_v3( + efa->no, l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co); + } + + if (UNLIKELY(is_quad_flip_v3_first_third_fast_with_normal(l_ptr_a[0]->v->co, + l_ptr_a[1]->v->co, + l_ptr_a[2]->v->co, + l_ptr_b[2]->v->co, + efa->no))) { /* Flip out of degenerate 0-2 state. */ l_ptr_a[2] = l_ptr_b[2]; l_ptr_b[0] = l_ptr_a[1]; } - return 2; + break; } default: { + if (face_normal) { + BM_face_calc_normal(efa, efa->no); + } + BMLoop *l_iter, *l_first; BMLoop **l_arr; @@ -123,18 +151,34 @@ static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], } BLI_memarena_clear(pf_arena); - return tris_len; + break; } } } +static void bmesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p) +{ + bmesh_calc_tessellation_for_face_impl(looptris, efa, pf_arena_p, false); +} + +static void bmesh_calc_tessellation_for_face_with_normal(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p) +{ + bmesh_calc_tessellation_for_face_impl(looptris, efa, pf_arena_p, true); +} + /** * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh * \param looptris: * * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count */ -static void bm_mesh_calc_tessellation__single_threaded(BMesh *bm, BMLoop *(*looptris)[3]) +static void bm_mesh_calc_tessellation__single_threaded(BMesh *bm, + BMLoop *(*looptris)[3], + const char face_normals) { #ifndef NDEBUG const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); @@ -146,9 +190,20 @@ static void bm_mesh_calc_tessellation__single_threaded(BMesh *bm, BMLoop *(*loop MemArena *pf_arena = NULL; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BLI_assert(efa->len >= 3); - i += mesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena); + if (face_normals) { + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + BM_face_calc_normal(efa, efa->no); + bmesh_calc_tessellation_for_face_with_normal(looptris + i, efa, &pf_arena); + i += efa->len - 2; + } + } + else { + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + bmesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena); + i += efa->len - 2; + } } if (pf_arena) { @@ -163,20 +218,32 @@ struct TessellationUserTLS { MemArena *pf_arena; }; -static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata, - MempoolIterData *mp_f, - const TaskParallelTLS *__restrict tls) +static void bmesh_calc_tessellation_for_face_fn(void *__restrict userdata, + MempoolIterData *mp_f, + const TaskParallelTLS *__restrict tls) +{ + struct TessellationUserTLS *tls_data = tls->userdata_chunk; + BMLoop *(*looptris)[3] = userdata; + BMFace *f = (BMFace *)mp_f; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); + bmesh_calc_tessellation_for_face(looptris + offset, f, &tls_data->pf_arena); +} + +static void bmesh_calc_tessellation_for_face_with_normals_fn(void *__restrict userdata, + MempoolIterData *mp_f, + const TaskParallelTLS *__restrict tls) { struct TessellationUserTLS *tls_data = tls->userdata_chunk; BMLoop *(*looptris)[3] = userdata; BMFace *f = (BMFace *)mp_f; BMLoop *l = BM_FACE_FIRST_LOOP(f); const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); - mesh_calc_tessellation_for_face(looptris + offset, f, &tls_data->pf_arena); + bmesh_calc_tessellation_for_face_with_normal(looptris + offset, f, &tls_data->pf_arena); } -static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata), - void *__restrict tls_v) +static void bmesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata), + void *__restrict tls_v) { struct TessellationUserTLS *tls_data = tls_v; if (tls_data->pf_arena) { @@ -184,7 +251,9 @@ static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSE } } -static void bm_mesh_calc_tessellation__multi_threaded(BMesh *bm, BMLoop *(*looptris)[3]) +static void bm_mesh_calc_tessellation__multi_threaded(BMesh *bm, + BMLoop *(*looptris)[3], + const char face_normals) { BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE); @@ -193,20 +262,36 @@ static void bm_mesh_calc_tessellation__multi_threaded(BMesh *bm, BMLoop *(*loopt BLI_parallel_mempool_settings_defaults(&settings); settings.userdata_chunk = &tls_dummy; settings.userdata_chunk_size = sizeof(tls_dummy); - settings.func_free = mesh_calc_tessellation_for_face_free_fn; - BM_iter_parallel(bm, BM_FACES_OF_MESH, mesh_calc_tessellation_for_face_fn, looptris, &settings); + settings.func_free = bmesh_calc_tessellation_for_face_free_fn; + BM_iter_parallel(bm, + BM_FACES_OF_MESH, + face_normals ? bmesh_calc_tessellation_for_face_with_normals_fn : + bmesh_calc_tessellation_for_face_fn, + looptris, + &settings); } -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) +void BM_mesh_calc_tessellation_ex(BMesh *bm, + BMLoop *(*looptris)[3], + const struct BMeshCalcTessellation_Params *params) { if (bm->totface < BM_FACE_TESSELLATE_THREADED_LIMIT) { - bm_mesh_calc_tessellation__single_threaded(bm, looptris); + bm_mesh_calc_tessellation__single_threaded(bm, looptris, params->face_normals); } else { - bm_mesh_calc_tessellation__multi_threaded(bm, looptris); + bm_mesh_calc_tessellation__multi_threaded(bm, looptris, params->face_normals); } } +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) +{ + BM_mesh_calc_tessellation_ex(bm, + looptris, + &(const struct BMeshCalcTessellation_Params){ + .face_normals = false, + }); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -222,19 +307,30 @@ struct PartialTessellationUserTLS { MemArena *pf_arena; }; -static void mesh_calc_tessellation_for_face_partial_fn(void *__restrict userdata, - const int index, - const TaskParallelTLS *__restrict tls) +static void bmesh_calc_tessellation_for_face_partial_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + struct PartialTessellationUserTLS *tls_data = tls->userdata_chunk; + struct PartialTessellationUserData *data = userdata; + BMFace *f = data->faces[index]; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); + bmesh_calc_tessellation_for_face(data->looptris + offset, f, &tls_data->pf_arena); +} + +static void bmesh_calc_tessellation_for_face_partial_with_normals_fn( + void *__restrict userdata, const int index, const TaskParallelTLS *__restrict tls) { struct PartialTessellationUserTLS *tls_data = tls->userdata_chunk; struct PartialTessellationUserData *data = userdata; BMFace *f = data->faces[index]; BMLoop *l = BM_FACE_FIRST_LOOP(f); const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); - mesh_calc_tessellation_for_face(data->looptris + offset, f, &tls_data->pf_arena); + bmesh_calc_tessellation_for_face_with_normal(data->looptris + offset, f, &tls_data->pf_arena); } -static void mesh_calc_tessellation_for_face_partial_free_fn( +static void bmesh_calc_tessellation_for_face_partial_free_fn( const void *__restrict UNUSED(userdata), void *__restrict tls_v) { struct PartialTessellationUserTLS *tls_data = tls_v; @@ -243,8 +339,10 @@ static void mesh_calc_tessellation_for_face_partial_free_fn( } } -static void bm_mesh_calc_tessellation_with_partial__multi_threaded(BMLoop *(*looptris)[3], - const BMPartialUpdate *bmpinfo) +static void bm_mesh_calc_tessellation_with_partial__multi_threaded( + BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo, + const struct BMeshCalcTessellation_Params *params) { const int faces_len = bmpinfo->faces_len; BMFace **faces = bmpinfo->faces; @@ -259,25 +357,42 @@ static void bm_mesh_calc_tessellation_with_partial__multi_threaded(BMLoop *(*loo settings.use_threading = true; settings.userdata_chunk = &tls_dummy; settings.userdata_chunk_size = sizeof(tls_dummy); - settings.func_free = mesh_calc_tessellation_for_face_partial_free_fn; - - BLI_task_parallel_range( - 0, faces_len, &data, mesh_calc_tessellation_for_face_partial_fn, &settings); + settings.func_free = bmesh_calc_tessellation_for_face_partial_free_fn; + + BLI_task_parallel_range(0, + faces_len, + &data, + params->face_normals ? + bmesh_calc_tessellation_for_face_partial_with_normals_fn : + bmesh_calc_tessellation_for_face_partial_fn, + &settings); } -static void bm_mesh_calc_tessellation_with_partial__single_threaded(BMLoop *(*looptris)[3], - const BMPartialUpdate *bmpinfo) +static void bm_mesh_calc_tessellation_with_partial__single_threaded( + BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo, + const struct BMeshCalcTessellation_Params *params) { const int faces_len = bmpinfo->faces_len; BMFace **faces = bmpinfo->faces; MemArena *pf_arena = NULL; - for (int index = 0; index < faces_len; index++) { - BMFace *f = faces[index]; - BMLoop *l = BM_FACE_FIRST_LOOP(f); - const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); - mesh_calc_tessellation_for_face(looptris + offset, f, &pf_arena); + if (params->face_normals) { + for (int index = 0; index < faces_len; index++) { + BMFace *f = faces[index]; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); + bmesh_calc_tessellation_for_face_with_normal(looptris + offset, f, &pf_arena); + } + } + else { + for (int index = 0; index < faces_len; index++) { + BMFace *f = faces[index]; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); + bmesh_calc_tessellation_for_face(looptris + offset, f, &pf_arena); + } } if (pf_arena) { @@ -285,22 +400,30 @@ static void bm_mesh_calc_tessellation_with_partial__single_threaded(BMLoop *(*lo } } -void BM_mesh_calc_tessellation_with_partial(BMesh *bm, - BMLoop *(*looptris)[3], - const BMPartialUpdate *bmpinfo) +void BM_mesh_calc_tessellation_with_partial_ex(BMesh *bm, + BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo, + const struct BMeshCalcTessellation_Params *params) { BLI_assert(bmpinfo->params.do_tessellate); BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE); if (bmpinfo->faces_len < BM_FACE_TESSELLATE_THREADED_LIMIT) { - bm_mesh_calc_tessellation_with_partial__single_threaded(looptris, bmpinfo); + bm_mesh_calc_tessellation_with_partial__single_threaded(looptris, bmpinfo, params); } else { - bm_mesh_calc_tessellation_with_partial__multi_threaded(looptris, bmpinfo); + bm_mesh_calc_tessellation_with_partial__multi_threaded(looptris, bmpinfo, params); } } +void BM_mesh_calc_tessellation_with_partial(BMesh *bm, + BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo) +{ + BM_mesh_calc_tessellation_with_partial_ex(bm, looptris, bmpinfo, false); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -309,10 +432,10 @@ void BM_mesh_calc_tessellation_with_partial(BMesh *bm, * Avoid degenerate triangles. * \{ */ -static int mesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3], - BMFace *efa, - MemArena **pf_arena_p, - Heap **pf_heap_p) +static int bmesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p, + Heap **pf_heap_p) { switch (efa->len) { case 3: { @@ -442,7 +565,7 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BLI_assert(efa->len >= 3); - i += mesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap); + i += bmesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap); } if (pf_arena) { diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h index f68a91cb988..9a6a20d7568 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h @@ -22,9 +22,25 @@ struct BMPartialUpdate; +struct BMeshCalcTessellation_Params { + /** + * When calculating normals as well as tessellation, calculate normals after tessellation + * for improved performance. See #BMeshCalcTessellation_Params + */ + bool face_normals; +}; + +void BM_mesh_calc_tessellation_ex(BMesh *bm, + BMLoop *(*looptris)[3], + const struct BMeshCalcTessellation_Params *params); void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); + void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); +void BM_mesh_calc_tessellation_with_partial_ex(BMesh *bm, + BMLoop *(*looptris)[3], + const struct BMPartialUpdate *bmpinfo, + const struct BMeshCalcTessellation_Params *params); void BM_mesh_calc_tessellation_with_partial(BMesh *bm, BMLoop *(*looptris)[3], const struct BMPartialUpdate *bmpinfo); diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c index 76e32667804..548dba242bf 100644 --- a/source/blender/bmesh/intern/bmesh_mods.c +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -46,7 +46,7 @@ * </pre> * * This function can also collapse edges too - * in cases when it cant merge into faces. + * in cases when it can't merge into faces. * * \par Example: * <pre> diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index 03655bccf1c..da407abfb21 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -699,7 +699,7 @@ BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e_first) if (BM_edge_is_manifold(l_a->e)) { l_a = l_a->radial_next; } - /* this wont have changed from the previous loop */ + /* this won't have changed from the previous loop */ i++; } while (l_a != e_first->l); @@ -978,7 +978,7 @@ bool BM_vert_is_manifold(const BMVert *v) /* start at the boundary */ l_first = e_iter->l; boundary_num += 1; - /* >2 boundaries cant be manifold */ + /* >2 boundaries can't be manifold */ if (boundary_num == 3) { return false; } diff --git a/source/blender/bmesh/operators/bmo_bridge.c b/source/blender/bmesh/operators/bmo_bridge.c index 5713c17e146..0a6540c0e5e 100644 --- a/source/blender/bmesh/operators/bmo_bridge.c +++ b/source/blender/bmesh/operators/bmo_bridge.c @@ -236,7 +236,7 @@ static void bridge_loop_pair(BMesh *bm, if (UNLIKELY((len_squared_v3(el_dir) < eps) || ((fabsf(dot_a) < eps) && (fabsf(dot_b) < eps)))) { /* in this case there is no depth between the two loops, * eg: 2x 2d circles, one scaled smaller, - * in this case 'el_dir' cant be used, just ensure we have matching flipping. */ + * in this case 'el_dir' can't be used, just ensure we have matching flipping. */ if (dot_v3v3(BM_edgeloop_normal_get(el_store_a), BM_edgeloop_normal_get(el_store_b)) < 0.0f) { BM_edgeloop_flip(bm, el_store_b); } diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c index 0a08b340e87..ffdce943d9f 100644 --- a/source/blender/bmesh/operators/bmo_extrude.c +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -459,8 +459,10 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) } /* Allocate array to store possible vertices that will be dissolved. */ - int boundary_verts_len = BMO_slot_map_count(dupeop.slots_out, "boundary_map.out"); - dissolve_verts = MEM_mallocN((size_t)boundary_verts_len * sizeof(*dissolve_verts), __func__); + int boundary_edges_len = BMO_slot_map_count(dupeop.slots_out, "boundary_map.out"); + /* We do not know the real number of boundary vertices. */ + int boundary_verts_len_maybe = 2 * boundary_edges_len; + dissolve_verts = MEM_mallocN(boundary_verts_len_maybe * sizeof(*dissolve_verts), __func__); } BMO_slot_copy(&dupeop, slots_out, "geom.out", op, slots_out, "geom.out"); diff --git a/source/blender/bmesh/operators/bmo_fill_attribute.c b/source/blender/bmesh/operators/bmo_fill_attribute.c index 782fd98c2ea..e377fa6079b 100644 --- a/source/blender/bmesh/operators/bmo_fill_attribute.c +++ b/source/blender/bmesh/operators/bmo_fill_attribute.c @@ -154,8 +154,9 @@ void bmo_face_attribute_fill_exec(BMesh *bm, BMOperator *op) int face_tot; BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); - BMO_slot_buffer_hflag_enable( - bm, op->slots_in, "faces", BM_FACE, BM_ELEM_TAG, false); /* do inline */ + + /* do inline */ + BMO_slot_buffer_hflag_enable(bm, op->slots_in, "faces", BM_FACE, BM_ELEM_TAG, false); /* now we can copy adjacent data */ face_tot = bmesh_face_attribute_fill(bm, use_normals, use_data); diff --git a/source/blender/bmesh/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c index 3311ffefb0d..b5f5e5e308b 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -138,7 +138,7 @@ static int recalc_face_normals_find_index(BMesh *bm, * then the outer-most loop attached to that vertex. * * Important this is correctly detected, - * where casting a ray from the center wont hit any loops past this one. + * where casting a ray from the center won't hit any loops past this one. * Otherwise the result may be incorrect. */ for (i = 0; i < faces_len; i++) { diff --git a/source/blender/bmesh/operators/bmo_subdivide_edgering.c b/source/blender/bmesh/operators/bmo_subdivide_edgering.c index 38a27b811b0..16d7b79a028 100644 --- a/source/blender/bmesh/operators/bmo_subdivide_edgering.c +++ b/source/blender/bmesh/operators/bmo_subdivide_edgering.c @@ -244,7 +244,7 @@ static GSet *bm_edgering_pair_calc(BMesh *bm, ListBase *eloops_rim) el_store_other = BLI_ghash_lookup(vert_eloop_gh, v_other); - /* in rare cases we cant find a match */ + /* in rare cases we can't find a match */ if (el_store_other) { pair_test.first = el_store; pair_test.second = el_store_other; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index cef97f20a6a..d39fb5e81f1 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -7361,7 +7361,6 @@ static float geometry_collide_offset(BevelParams *bp, EdgeHalf *eb) static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea) { float no_collide_offset = bp->offset + 1e6; - float limit = no_collide_offset; if (bp->offset == 0.0f) { return no_collide_offset; } @@ -7373,8 +7372,7 @@ static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea) if (kab <= 0.0f) { return no_collide_offset; } - limit = la / kab; - return limit; + return la / kab; } /** diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c index e7d0fe6a0c6..8f03b86b859 100644 --- a/source/blender/bmesh/tools/bmesh_bisect_plane.c +++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c @@ -368,7 +368,7 @@ static void bm_face_bisect_verts( } } - /* Ideally wont happen, but it can for self intersecting faces. */ + /* Ideally won't happen, but it can for self-intersecting faces. */ // BLI_assert(found == true); /* In fact this simple test is good enough, test if the loops are adjacent. */ diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index 869856d0c5b..82878c7618a 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -1285,6 +1285,11 @@ static bool bm_decim_edge_collapse(BMesh *bm, * a vertex group is the usual source for this. * \param symmetry_axis: Axis of symmetry, -1 to disable mirror decimate. * \param symmetry_eps: Threshold when matching mirror verts. + * + * \note The caller is responsible for recalculating face and vertex normals. + * - Vertex normals are maintained while decimating, + * although they won't necessarily match the final recalculated normals. + * - Face normals are not maintained at all. */ void BM_mesh_decimate_collapse(BMesh *bm, const float factor, @@ -1367,7 +1372,7 @@ void BM_mesh_decimate_collapse(BMesh *bm, /* handy to detect corruptions elsewhere */ BLI_assert(BM_elem_index_get(e) < tot_edge_orig); - /* Under normal conditions wont be accessed again, + /* Under normal conditions won't be accessed again, * but NULL just in case so we don't use freed node. */ eheap_table[BM_elem_index_get(e)] = NULL; diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c index 710d7f79637..cc980a81aad 100644 --- a/source/blender/bmesh/tools/bmesh_intersect.c +++ b/source/blender/bmesh/tools/bmesh_intersect.c @@ -560,8 +560,8 @@ static void bm_isect_tri_tri( /* vert-vert * --------- */ { - /* first check in any verts are touching - * (any case where we wont create new verts) + /* first check if any verts are touching + * (any case where we won't create new verts) */ uint i_a; for (i_a = 0; i_a < 3; i_a++) { @@ -1606,7 +1606,7 @@ bool BM_mesh_intersect(BMesh *bm, for (node = s.vert_dissolve; node; node = node->next) { BMVert *v = node->link; if (BM_vert_is_edge_pair(v)) { - /* we wont create degenerate faces from this */ + /* we won't create degenerate faces from this */ bool ok = true; /* would we create a 2-sided-face? diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c index 561b00544b5..c538d5b17cd 100644 --- a/source/blender/bmesh/tools/bmesh_region_match.c +++ b/source/blender/bmesh/tools/bmesh_region_match.c @@ -553,7 +553,8 @@ static void bm_uuidwalk_pass_add(UUIDWalk *uuidwalk, static int bm_face_len_cmp(const void *v1, const void *v2) { - const BMFace *f1 = v1, *f2 = v2; + const BMFace *f1 = *((BMFace **)v1); + const BMFace *f2 = *((BMFace **)v2); if (f1->len > f2->len) { return 1; diff --git a/source/blender/bmesh/tools/bmesh_wireframe.c b/source/blender/bmesh/tools/bmesh_wireframe.c index 1c820db74f4..a3db93be033 100644 --- a/source/blender/bmesh/tools/bmesh_wireframe.c +++ b/source/blender/bmesh/tools/bmesh_wireframe.c @@ -63,7 +63,7 @@ static void bm_vert_boundary_tangent( float tvec_a[3], tvec_b[3]; /* get 2 boundary edges, there should only _be_ 2, - * in case there are more - results wont be valid of course */ + * in case there are more - results won't be valid of course */ BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) { if (BM_elem_flag_test(e_iter, BM_ELEM_TAG)) { if (e_a == NULL) { diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 5a52d216117..857cbf0beee 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -62,6 +62,24 @@ constexpr int COM_data_type_num_channels(const DataType datatype) constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value); constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color); +constexpr float COM_VALUE_ZERO[1] = {0.0f}; + +/** + * Utility to get data type for given number of channels. + */ +constexpr DataType COM_num_channels_data_type(const int num_channels) +{ + switch (num_channels) { + case 1: + return DataType::Value; + case 3: + return DataType::Vector; + case 4: + default: + return DataType::Color; + } +} + // configurable items // chunk size determination diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 8c30d3215d7..44d3f059374 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -18,10 +18,31 @@ #include "COM_MemoryBuffer.h" +#include "IMB_colormanagement.h" +#include "IMB_imbuf_types.h" #include "MEM_guardedalloc.h" +#define ASSERT_BUFFER_CONTAINS_AREA(buf, area) \ + BLI_assert(BLI_rcti_inside_rcti(&(buf)->get_rect(), &(area))) + +#define ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(buf, area, x, y) \ + BLI_assert((buf)->get_rect().xmin <= (x)); \ + BLI_assert((buf)->get_rect().ymin <= (y)); \ + BLI_assert((buf)->get_rect().xmax >= (x) + BLI_rcti_size_x(&(area))); \ + BLI_assert((buf)->get_rect().ymax >= (y) + BLI_rcti_size_y(&(area))) + +#define ASSERT_VALID_ELEM_SIZE(buf, channel_offset, elem_size) \ + BLI_assert((buf)->get_num_channels() <= (channel_offset) + (elem_size)) + namespace blender::compositor { +static rcti create_rect(const int width, const int height) +{ + rcti rect; + BLI_rcti_init(&rect, 0, width, 0, height); + return rect; +} + MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state) { m_rect = rect; @@ -30,6 +51,7 @@ MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBuf this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType()); this->m_buffer = (float *)MEM_mallocN_aligned( sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer"); + owns_data_ = true; this->m_state = state; this->m_datatype = memoryProxy->getDataType(); @@ -44,12 +66,44 @@ MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single this->m_num_channels = COM_data_type_num_channels(dataType); this->m_buffer = (float *)MEM_mallocN_aligned( sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer"); + owns_data_ = true; this->m_state = MemoryBufferState::Temporary; this->m_datatype = dataType; set_strides(); } +/** + * Construct MemoryBuffer from a float buffer. MemoryBuffer is not responsible for + * freeing it. + */ +MemoryBuffer::MemoryBuffer( + float *buffer, int num_channels, int width, int height, bool is_a_single_elem) + : MemoryBuffer(buffer, num_channels, create_rect(width, height), is_a_single_elem) +{ +} + +/** + * Construct MemoryBuffer from a float buffer area. MemoryBuffer is not responsible for + * freeing given buffer. + */ +MemoryBuffer::MemoryBuffer(float *buffer, + const int num_channels, + const rcti &rect, + const bool is_a_single_elem) +{ + m_rect = rect; + m_is_a_single_elem = is_a_single_elem; + m_memoryProxy = nullptr; + m_num_channels = num_channels; + m_datatype = COM_num_channels_data_type(num_channels); + m_buffer = buffer; + owns_data_ = false; + m_state = MemoryBufferState::Temporary; + + set_strides(); +} + MemoryBuffer::MemoryBuffer(const MemoryBuffer &src) : MemoryBuffer(src.m_datatype, src.m_rect, false) { @@ -112,31 +166,195 @@ float MemoryBuffer::get_max_value(const rcti &rect) const MemoryBuffer::~MemoryBuffer() { - if (this->m_buffer) { + if (this->m_buffer && owns_data_) { MEM_freeN(this->m_buffer); this->m_buffer = nullptr; } } -void MemoryBuffer::fill_from(const MemoryBuffer &src) +void MemoryBuffer::copy_from(const MemoryBuffer *src, const rcti &area) { - BLI_assert(!this->is_a_single_elem()); + copy_from(src, area, area.xmin, area.ymin); +} + +void MemoryBuffer::copy_from(const MemoryBuffer *src, + const rcti &area, + const int to_x, + const int to_y) +{ + BLI_assert(this->get_num_channels() == src->get_num_channels()); + copy_from(src, area, 0, src->get_num_channels(), to_x, to_y, 0); +} + +void MemoryBuffer::copy_from(const MemoryBuffer *src, + const rcti &area, + const int channel_offset, + const int elem_size, + const int to_channel_offset) +{ + copy_from(src, area, channel_offset, elem_size, area.xmin, area.ymin, to_channel_offset); +} + +void MemoryBuffer::copy_from(const MemoryBuffer *src, + const rcti &area, + const int channel_offset, + const int elem_size, + const int to_x, + const int to_y, + const int to_channel_offset) +{ + if (this->is_a_single_elem()) { + copy_single_elem_from(src, channel_offset, elem_size, to_channel_offset); + } + else if (!src->is_a_single_elem() && elem_size == src->get_num_channels() && + elem_size == this->get_num_channels()) { + BLI_assert(to_channel_offset == 0); + BLI_assert(channel_offset == 0); + copy_rows_from(src, area, to_x, to_y); + } + else { + copy_elems_from(src, area, channel_offset, elem_size, to_x, to_y, to_channel_offset); + } +} + +void MemoryBuffer::copy_from(const uchar *src, const rcti &area) +{ + copy_from(src, area, 0, this->get_num_channels(), this->get_num_channels(), 0); +} + +void MemoryBuffer::copy_from(const uchar *src, + const rcti &area, + const int channel_offset, + const int elem_size, + const int elem_stride, + const int to_channel_offset) +{ + copy_from( + src, area, channel_offset, elem_size, elem_stride, area.xmin, area.ymin, to_channel_offset); +} + +void MemoryBuffer::copy_from(const uchar *src, + const rcti &area, + const int channel_offset, + const int elem_size, + const int elem_stride, + const int to_x, + const int to_y, + const int to_channel_offset) +{ + ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y); + ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size); + + const int width = BLI_rcti_size_x(&area); + const int height = BLI_rcti_size_y(&area); + const int src_row_stride = width * elem_stride; + const uchar *const src_start = src + area.ymin * src_row_stride + channel_offset; + for (int y = 0; y < height; y++) { + const uchar *from_elem = src_start + y * src_row_stride; + float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset); + const float *row_end = to_elem + width * this->elem_stride; + while (to_elem < row_end) { + for (int i = 0; i < elem_size; i++) { + to_elem[i] = ((float)from_elem[i]) * (1.0f / 255.0f); + } + to_elem += this->elem_stride; + from_elem += elem_stride; + } + } +} + +static void colorspace_to_scene_linear(MemoryBuffer *buf, const rcti &area, ColorSpace *colorspace) +{ + const int width = BLI_rcti_size_x(&area); + const int height = BLI_rcti_size_y(&area); + float *out = buf->get_elem(area.xmin, area.ymin); + /* If area allows continuous memory do conversion in one step. Otherwise per row. */ + if (buf->getWidth() == width) { + IMB_colormanagement_colorspace_to_scene_linear( + out, width, height, buf->get_num_channels(), colorspace, false); + } + else { + for (int y = 0; y < height; y++) { + IMB_colormanagement_colorspace_to_scene_linear( + out, width, 1, buf->get_num_channels(), colorspace, false); + out += buf->row_stride; + } + } +} + +void MemoryBuffer::copy_from(const ImBuf *src, const rcti &area, const bool ensure_linear_space) +{ + copy_from(src, area, 0, this->get_num_channels(), 0, ensure_linear_space); +} - unsigned int otherY; - unsigned int minX = MAX2(this->m_rect.xmin, src.m_rect.xmin); - unsigned int maxX = MIN2(this->m_rect.xmax, src.m_rect.xmax); - unsigned int minY = MAX2(this->m_rect.ymin, src.m_rect.ymin); - unsigned int maxY = MIN2(this->m_rect.ymax, src.m_rect.ymax); - int offset; - int otherOffset; +void MemoryBuffer::copy_from(const ImBuf *src, + const rcti &area, + const int channel_offset, + const int elem_size, + const int to_channel_offset, + const bool ensure_linear_space) +{ + copy_from(src, + area, + channel_offset, + elem_size, + area.xmin, + area.ymin, + to_channel_offset, + ensure_linear_space); +} - for (otherY = minY; otherY < maxY; otherY++) { - otherOffset = src.get_coords_offset(minX, otherY); - offset = this->get_coords_offset(minX, otherY); - memcpy(&this->m_buffer[offset], - &src.m_buffer[otherOffset], - (maxX - minX) * this->m_num_channels * sizeof(float)); +void MemoryBuffer::copy_from(const ImBuf *src, + const rcti &area, + const int channel_offset, + const int elem_size, + const int to_x, + const int to_y, + const int to_channel_offset, + const bool ensure_linear_space) +{ + if (src->rect_float) { + const MemoryBuffer mem_buf(src->rect_float, src->channels, src->x, src->y, false); + copy_from(&mem_buf, area, channel_offset, elem_size, to_x, to_y, to_channel_offset); } + else if (src->rect) { + const uchar *uc_buf = (uchar *)src->rect; + const int elem_stride = src->channels; + copy_from(uc_buf, area, channel_offset, elem_size, elem_stride, to_x, to_y, to_channel_offset); + if (ensure_linear_space) { + colorspace_to_scene_linear(this, area, src->rect_colorspace); + } + } + else { + /* Empty ImBuf source. Fill destination with empty values. */ + const float *zero_elem = new float[elem_size]{0}; + fill(area, to_channel_offset, zero_elem, elem_size); + delete[] zero_elem; + } +} + +void MemoryBuffer::fill(const rcti &area, const float *value) +{ + fill(area, 0, value, this->get_num_channels()); +} + +void MemoryBuffer::fill(const rcti &area, + const int channel_offset, + const float *value, + const int value_size) +{ + const MemoryBuffer single_elem(const_cast<float *>(value), value_size, this->get_rect(), true); + copy_from(&single_elem, area, 0, value_size, area.xmin, area.ymin, channel_offset); +} + +void MemoryBuffer::fill_from(const MemoryBuffer &src) +{ + rcti overlap; + overlap.xmin = MAX2(this->m_rect.xmin, src.m_rect.xmin); + overlap.xmax = MIN2(this->m_rect.xmax, src.m_rect.xmax); + overlap.ymin = MAX2(this->m_rect.ymin, src.m_rect.ymin); + overlap.ymax = MIN2(this->m_rect.ymax, src.m_rect.ymax); + copy_from(&src, overlap); } void MemoryBuffer::writePixel(int x, int y, const float color[4]) @@ -196,4 +414,70 @@ void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivat } } +void MemoryBuffer::copy_single_elem_from(const MemoryBuffer *src, + const int channel_offset, + const int elem_size, + const int to_channel_offset) +{ + ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size); + ASSERT_VALID_ELEM_SIZE(src, channel_offset, elem_size); + BLI_assert(this->is_a_single_elem()); + + float *to_elem = &this->get_value( + this->get_rect().xmin, this->get_rect().ymin, to_channel_offset); + const float *from_elem = &src->get_value( + src->get_rect().xmin, src->get_rect().ymin, channel_offset); + const int elem_bytes = elem_size * sizeof(float); + memcpy(to_elem, from_elem, elem_bytes); +} + +void MemoryBuffer::copy_rows_from(const MemoryBuffer *src, + const rcti &area, + const int to_x, + const int to_y) +{ + ASSERT_BUFFER_CONTAINS_AREA(src, area); + ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y); + BLI_assert(this->get_num_channels() == src->get_num_channels()); + BLI_assert(!this->is_a_single_elem()); + BLI_assert(!src->is_a_single_elem()); + + const int width = BLI_rcti_size_x(&area); + const int height = BLI_rcti_size_y(&area); + const int row_bytes = this->get_num_channels() * width * sizeof(float); + for (int y = 0; y < height; y++) { + float *to_row = this->get_elem(to_x, to_y + y); + const float *from_row = src->get_elem(area.xmin, area.ymin + y); + memcpy(to_row, from_row, row_bytes); + } +} + +void MemoryBuffer::copy_elems_from(const MemoryBuffer *src, + const rcti &area, + const int channel_offset, + const int elem_size, + const int to_x, + const int to_y, + const int to_channel_offset) +{ + ASSERT_BUFFER_CONTAINS_AREA(src, area); + ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y); + ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size); + ASSERT_VALID_ELEM_SIZE(src, channel_offset, elem_size); + + const int width = BLI_rcti_size_x(&area); + const int height = BLI_rcti_size_y(&area); + const int elem_bytes = elem_size * sizeof(float); + for (int y = 0; y < height; y++) { + float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset); + const float *from_elem = &src->get_value(area.xmin, area.ymin + y, channel_offset); + const float *row_end = to_elem + width * this->elem_stride; + while (to_elem < row_end) { + memcpy(to_elem, from_elem, elem_bytes); + to_elem += this->elem_stride; + from_elem += src->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 97b220508e0..575bb2fe383 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -106,6 +106,11 @@ class MemoryBuffer { */ bool m_is_a_single_elem; + /** + * Whether MemoryBuffer owns buffer data. + */ + bool owns_data_; + public: /** * \brief construct new temporarily MemoryBuffer for an area @@ -117,6 +122,11 @@ class MemoryBuffer { */ MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false); + MemoryBuffer( + float *buffer, int num_channels, int width, int height, bool is_a_single_elem = false); + + MemoryBuffer(float *buffer, int num_channels, const rcti &rect, bool is_a_single_elem = false); + /** * Copy constructor */ @@ -223,7 +233,7 @@ class MemoryBuffer { return is_a_single_elem() ? 1 : getHeight(); } - uint8_t get_num_channels() + uint8_t get_num_channels() const { return this->m_num_channels; } @@ -404,11 +414,58 @@ class MemoryBuffer { return this->m_state == MemoryBufferState::Temporary; } + void copy_from(const MemoryBuffer *src, const rcti &area); + void copy_from(const MemoryBuffer *src, const rcti &area, int to_x, int to_y); + void copy_from(const MemoryBuffer *src, + const rcti &area, + int channel_offset, + int elem_size, + int to_channel_offset); + void copy_from(const MemoryBuffer *src, + const rcti &area, + int channel_offset, + int elem_size, + int to_x, + int to_y, + int to_channel_offset); + void copy_from(const uchar *src, const rcti &area); + void copy_from(const uchar *src, + const rcti &area, + int channel_offset, + int elem_size, + int elem_stride, + int to_channel_offset); + void copy_from(const uchar *src, + const rcti &area, + int channel_offset, + int elem_size, + int elem_stride, + int to_x, + int to_y, + int to_channel_offset); + void copy_from(const struct ImBuf *src, const rcti &area, bool ensure_linear_space = false); + void copy_from(const struct ImBuf *src, + const rcti &area, + int channel_offset, + int elem_size, + int to_channel_offset, + bool ensure_linear_space = false); + void copy_from(const struct ImBuf *src, + const rcti &src_area, + int channel_offset, + int elem_size, + int to_x, + int to_y, + int to_channel_offset, + bool ensure_linear_space = false); + + void fill(const rcti &area, const float *value); + void fill(const rcti &area, int channel_offset, const float *value, int value_size); /** * \brief add the content from otherBuffer to this MemoryBuffer * \param otherBuffer: source buffer * - * \note take care when running this on a new buffer since it wont fill in + * \note take care when running this on a new buffer since it won't fill in * uninitialized values in areas where the buffers don't overlap. */ void fill_from(const MemoryBuffer &src); @@ -452,6 +509,22 @@ class MemoryBuffer { return get_memory_width() * get_memory_height(); } + void copy_single_elem_from(const MemoryBuffer *src, + int channel_offset, + int elem_size, + const int to_channel_offset); + void copy_rows_from(const MemoryBuffer *src, + const rcti &src_area, + const int to_x, + const int to_y); + void copy_elems_from(const MemoryBuffer *src, + const rcti &area, + const int channel_offset, + const int elem_size, + const int to_x, + const int to_y, + const int to_channel_offset); + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer") #endif diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc index c54c2edccb0..e6e98d69b36 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc @@ -5,21 +5,22 @@ namespace blender::compositor { MultiThreadedOperation::MultiThreadedOperation() { - m_num_passes = 1; + num_passes_ = 1; + current_pass_ = 0; flags.is_fullframe_operation = true; } void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output, - const rcti &output_area, - blender::Span<MemoryBuffer *> inputs, + const rcti &area, + Span<MemoryBuffer *> inputs, ExecutionSystem &exec_system) { - for (int current_pass = 0; current_pass < m_num_passes; current_pass++) { - update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass); - exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) { - update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass); + for (current_pass_ = 0; current_pass_ < num_passes_; current_pass_++) { + update_memory_buffer_started(output, area, inputs); + exec_system.execute_work(area, [=](const rcti &split_rect) { + update_memory_buffer_partial(output, split_rect, inputs); }); - update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass); + update_memory_buffer_finished(output, area, inputs); } } diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h index 97c5fba4ead..ad09c4df089 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedOperation.h +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h @@ -27,7 +27,11 @@ class MultiThreadedOperation : public NodeOperation { /** * Number of execution passes. */ - int m_num_passes; + int num_passes_; + /** + * Current execution pass. + */ + int current_pass_; protected: MultiThreadedOperation(); @@ -36,37 +40,31 @@ class MultiThreadedOperation : public NodeOperation { * Called before an update memory buffer pass is executed. Single-threaded calls. */ virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output), - const rcti &UNUSED(output_rect), - blender::Span<MemoryBuffer *> UNUSED(inputs), - ExecutionSystem &UNUSED(exec_system), - int UNUSED(current_pass)) + const rcti &UNUSED(area), + Span<MemoryBuffer *> UNUSED(inputs)) { } /** - * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls. + * Executes operation updating a memory buffer area. Multi-threaded calls. */ virtual void update_memory_buffer_partial(MemoryBuffer *output, - const rcti &output_rect, - blender::Span<MemoryBuffer *> inputs, - ExecutionSystem &exec_system, - int current_pass) = 0; + const rcti &area, + Span<MemoryBuffer *> inputs) = 0; /** * Called after an update memory buffer pass is executed. Single-threaded calls. */ virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output), - const rcti &UNUSED(output_rect), - blender::Span<MemoryBuffer *> UNUSED(inputs), - ExecutionSystem &UNUSED(exec_system), - int UNUSED(current_pass)) + const rcti &UNUSED(area), + Span<MemoryBuffer *> UNUSED(inputs)) { } private: void update_memory_buffer(MemoryBuffer *output, - const rcti &output_area, - blender::Span<MemoryBuffer *> inputs, + const rcti &area, + Span<MemoryBuffer *> inputs, ExecutionSystem &exec_system) override; }; diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 83de8a751c4..b943ab6af7f 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -188,12 +188,12 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input, * caller must clamp it. * TODO: See if it's possible to use parameter overloading (input_id for example). * - * \param input_op_idx: Input operation index for which we want to calculate the area being read. + * \param input_idx: Input operation index for which we want to calculate the area being read. * \param output_area: Area being rendered by this operation. * \param r_input_area: Returned input operation area that needs to be read in order to render * given output area. */ -void NodeOperation::get_area_of_interest(const int input_op_idx, +void NodeOperation::get_area_of_interest(const int input_idx, const rcti &output_area, rcti &r_input_area) { @@ -203,7 +203,7 @@ void NodeOperation::get_area_of_interest(const int input_op_idx, else { /* Non full-frame operations never implement this method. To ensure correctness assume * whole area is used. */ - NodeOperation *input_op = getInputOperation(input_op_idx); + NodeOperation *input_op = getInputOperation(input_idx); BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight()); } } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index 5c4d6dd19ba..5ae0aae67d5 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -578,7 +578,7 @@ class NodeOperation { * Executes operation updating output memory buffer. Single-threaded calls. */ virtual void update_memory_buffer(MemoryBuffer *UNUSED(output), - const rcti &UNUSED(output_area), + const rcti &UNUSED(area), Span<MemoryBuffer *> UNUSED(inputs), ExecutionSystem &UNUSED(exec_system)) { @@ -587,7 +587,7 @@ class NodeOperation { /** * Get input operation area being read by this operation on rendering given output area. */ - virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area); + virtual void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area); void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area); /** \} */ diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index cd0139fd18e..157ded943d6 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -411,8 +411,7 @@ static void threading_model_task_schedule(WorkPackage *package) static void threading_model_task_start() { BLI_thread_local_create(g_thread_device); - g_work_scheduler.task.pool = BLI_task_pool_create( - nullptr, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + g_work_scheduler.task.pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH); } static void threading_model_task_finish() diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.h b/source/blender/compositor/nodes/COM_AntiAliasingNode.h index d4a6d0d26dc..7d3dd750864 100644 --- a/source/blender/compositor/nodes/COM_AntiAliasingNode.h +++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.h @@ -25,8 +25,8 @@ namespace blender::compositor { /** - * @brief AntiAliasingNode - * @ingroup Node + * \brief AntiAliasingNode + * \ingroup Node */ class AntiAliasingNode : public Node { public: diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc index f0bfda0f40e..20476144efa 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cc +++ b/source/blender/compositor/nodes/COM_ImageNode.cc @@ -168,7 +168,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, if (index == 0 && operation) { converter.addPreview(operation->getOutputSocket()); } - if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) { + if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && !(bnodeSocket->flag & SOCK_UNAVAIL)) { for (NodeOutput *alphaSocket : getOutputSockets()) { bNodeSocket *bnodeAlphaSocket = alphaSocket->getbNodeSocket(); if (!STREQ(bnodeAlphaSocket->name, "Alpha")) { diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc index 23d6f4b80c7..deccbb28f49 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc @@ -202,4 +202,72 @@ void *AntiAliasOperation::initializeTileData(rcti *rect) return getInputOperation(0)->initializeTileData(rect); } +void AntiAliasOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmax = output_area.xmax + 1; + r_input_area.xmin = output_area.xmin - 1; + r_input_area.ymax = output_area.ymax + 1; + r_input_area.ymin = output_area.ymin - 1; +} + +void AntiAliasOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[0]; + const rcti &input_area = input->get_rect(); + float ninepix[9]; + for (int y = area.ymin; y < area.ymax; y++) { + float *out = output->get_elem(area.xmin, y); + const float *row_curr = input->get_elem(area.xmin, y); + const float *row_prev = row_curr - input->row_stride; + const float *row_next = row_curr + input->row_stride; + int x_offset = 0; + for (int x = area.xmin; x < area.xmax; + x++, out += output->elem_stride, x_offset += input->elem_stride) { + if (x == input_area.xmin || x == input_area.xmax - 1 || y == input_area.xmin || + y == input_area.ymax - 1) { + out[0] = row_curr[x_offset]; + continue; + } + + if (extrapolate9(&ninepix[0], + &ninepix[1], + &ninepix[2], + &ninepix[3], + &ninepix[4], + &ninepix[5], + &ninepix[6], + &ninepix[7], + &ninepix[8], + &row_prev[x_offset - input->elem_stride], + &row_prev[x_offset], + &row_prev[x_offset + input->elem_stride], + &row_curr[x_offset - input->elem_stride], + &row_curr[x_offset], + &row_curr[x_offset + input->elem_stride], + &row_next[x_offset - input->elem_stride], + &row_next[x_offset], + &row_next[x_offset + input->elem_stride])) { + /* Some rounding magic to make weighting correct with the + * original coefficients. */ + unsigned char result = ((3 * ninepix[0] + 5 * ninepix[1] + 3 * ninepix[2] + + 5 * ninepix[3] + 6 * ninepix[4] + 5 * ninepix[5] + + 3 * ninepix[6] + 5 * ninepix[7] + 3 * ninepix[8]) * + 255.0f + + 19.0f) / + 38.0f; + out[0] = result / 255.0f; + } + else { + out[0] = row_curr[x_offset]; + } + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.h b/source/blender/compositor/operations/COM_AntiAliasOperation.h index fc9102b5b4c..b5048248425 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.h +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" namespace blender::compositor { @@ -28,7 +28,7 @@ namespace blender::compositor { * it only supports anti aliasing on BW buffers. * \ingroup operation */ -class AntiAliasOperation : public NodeOperation { +class AntiAliasOperation : public MultiThreadedOperation { protected: /** * \brief Cached reference to the reader @@ -57,6 +57,12 @@ class AntiAliasOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc index 1bb247e9bc5..38f8b7e075f 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.cc +++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc @@ -42,4 +42,22 @@ void IDMaskOperation::executePixel(float output[4], int x, int y, void *data) output[0] = (roundf(buffer[buffer_index]) == this->m_objectIndex) ? 1.0f : 0.0f; } +void IDMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[0]; + const int width = BLI_rcti_size_x(&area); + for (int y = area.ymin; y < area.ymax; y++) { + float *out = output->get_elem(area.xmin, y); + const float *in = input->get_elem(area.xmin, y); + const float *row_end = out + width * output->elem_stride; + while (out < row_end) { + out[0] = (roundf(in[0]) == m_objectIndex) ? 1.0f : 0.0f; + in += input->elem_stride; + out += output->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.h b/source/blender/compositor/operations/COM_IDMaskOperation.h index 79b7e53b67c..c2e13641b46 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.h +++ b/source/blender/compositor/operations/COM_IDMaskOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class IDMaskOperation : public NodeOperation { +class IDMaskOperation : public MultiThreadedOperation { private: float m_objectIndex; @@ -36,6 +36,10 @@ class IDMaskOperation : public NodeOperation { { this->m_objectIndex = objectIndex; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc index a1d401d4499..e78d389410f 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cc +++ b/source/blender/compositor/operations/COM_ImageOperation.cc @@ -44,6 +44,7 @@ BaseImageOperation::BaseImageOperation() this->m_imageheight = 0; this->m_framenumber = 0; this->m_depthBuffer = nullptr; + depth_buffer_ = nullptr; this->m_numberOfChannels = 0; this->m_rd = nullptr; this->m_viewName = nullptr; @@ -91,6 +92,9 @@ void BaseImageOperation::initExecution() this->m_imageFloatBuffer = stackbuf->rect_float; this->m_imageByteBuffer = stackbuf->rect; this->m_depthBuffer = stackbuf->zbuf_float; + if (stackbuf->zbuf_float) { + depth_buffer_ = new MemoryBuffer(stackbuf->zbuf_float, 1, stackbuf->x, stackbuf->y); + } this->m_imagewidth = stackbuf->x; this->m_imageheight = stackbuf->y; this->m_numberOfChannels = stackbuf->channels; @@ -102,6 +106,10 @@ void BaseImageOperation::deinitExecution() this->m_imageFloatBuffer = nullptr; this->m_imageByteBuffer = nullptr; BKE_image_release_ibuf(this->m_image, this->m_buffer, nullptr); + if (depth_buffer_) { + delete depth_buffer_; + depth_buffer_ = nullptr; + } } void BaseImageOperation::determineResolution(unsigned int resolution[2], @@ -170,6 +178,13 @@ void ImageOperation::executePixelSampled(float output[4], float x, float y, Pixe } } +void ImageOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + output->copy_from(m_buffer, area, true); +} + void ImageAlphaOperation::executePixelSampled(float output[4], float x, float y, @@ -187,6 +202,13 @@ void ImageAlphaOperation::executePixelSampled(float output[4], } } +void ImageAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + output->copy_from(m_buffer, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); +} + void ImageDepthOperation::executePixelSampled(float output[4], float x, float y, @@ -206,4 +228,16 @@ void ImageDepthOperation::executePixelSampled(float output[4], } } +void ImageDepthOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + if (depth_buffer_) { + output->copy_from(depth_buffer_, area); + } + else { + output->fill(area, COM_VALUE_ZERO); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index 58373663db5..f8b4239c9f8 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -21,7 +21,7 @@ #include "BKE_image.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "MEM_guardedalloc.h" #include "RE_pipeline.h" @@ -32,14 +32,17 @@ namespace blender::compositor { /** * \brief Base class for all image operations */ -class BaseImageOperation : public NodeOperation { +class BaseImageOperation : public MultiThreadedOperation { protected: ImBuf *m_buffer; Image *m_image; ImageUser *m_imageUser; + /* TODO: Remove raw buffers when removing Tiled implementation. */ float *m_imageFloatBuffer; unsigned int *m_imageByteBuffer; float *m_depthBuffer; + + MemoryBuffer *depth_buffer_; int m_imageheight; int m_imagewidth; int m_framenumber; @@ -87,6 +90,10 @@ class ImageOperation : public BaseImageOperation { */ ImageOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class ImageAlphaOperation : public BaseImageOperation { public: @@ -95,6 +102,10 @@ class ImageAlphaOperation : public BaseImageOperation { */ ImageAlphaOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class ImageDepthOperation : public BaseImageOperation { public: @@ -103,6 +114,10 @@ class ImageDepthOperation : public BaseImageOperation { */ ImageDepthOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc index 647e93225e5..3a5de944a00 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc @@ -51,6 +51,13 @@ ImBuf *MultilayerBaseOperation::getImBuf() return nullptr; } +void MultilayerBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + output->copy_from(m_buffer, area); +} + std::unique_ptr<MetaData> MultilayerColorOperation::getMetaData() { BLI_assert(this->m_buffer); diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h index 6e6062cf854..a682ca1941c 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h @@ -37,6 +37,10 @@ class MultilayerBaseOperation : public BaseImageOperation { * Constructor */ MultilayerBaseOperation(RenderLayer *render_layer, RenderPass *render_pass, int view); + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class MultilayerColorOperation : public MultilayerBaseOperation { diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index dbe45fa60db..79dee33e266 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -24,6 +24,7 @@ SetColorOperation::SetColorOperation() { this->addOutputSocket(DataType::Color); flags.is_set_operation = true; + flags.is_fullframe_operation = true; } void SetColorOperation::executePixelSampled(float output[4], @@ -41,4 +42,14 @@ void SetColorOperation::determineResolution(unsigned int resolution[2], resolution[1] = preferredResolution[1]; } +void SetColorOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system)) +{ + BLI_assert(output->is_a_single_elem()); + float *out_elem = output->get_elem(area.xmin, area.ymin); + copy_v4_v4(out_elem, m_color); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index 4b9b80013d4..2e22ef60ba4 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -80,6 +80,11 @@ class SetColorOperation : public NodeOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; + + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index ef43cf64653..359647c8fe3 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -24,6 +24,7 @@ SetValueOperation::SetValueOperation() { this->addOutputSocket(DataType::Value); flags.is_set_operation = true; + flags.is_fullframe_operation = true; } void SetValueOperation::executePixelSampled(float output[4], @@ -41,4 +42,14 @@ void SetValueOperation::determineResolution(unsigned int resolution[2], resolution[1] = preferredResolution[1]; } +void SetValueOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system)) +{ + BLI_assert(output->is_a_single_elem()); + float *out_elem = output->get_elem(area.xmin, area.ymin); + *out_elem = m_value; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index 5383f3b5fd3..acde5aa03b7 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -51,6 +51,10 @@ class SetValueOperation : public NodeOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system) override; }; } // namespace blender::compositor diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c index f4f510891e0..7f1d90f20ea 100644 --- a/source/blender/datatoc/datatoc_icon.c +++ b/source/blender/datatoc/datatoc_icon.c @@ -66,9 +66,9 @@ static bool path_test_extension(const char *str, const char *ext) return !(a == 0 || b == 0 || b >= a) && (strcmp(ext, str + a - b) == 0); } -static void endian_switch_uint32(unsigned int *val) +static void endian_switch_uint32(uint *val) { - unsigned int tval = *val; + uint tval = *val; *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24)); } @@ -96,10 +96,7 @@ static const char *path_basename(const char *path) /* -------------------------------------------------------------------- */ /* Write a PNG from RGBA pixels */ -static bool write_png(const char *name, - const unsigned int *pixels, - const int width, - const int height) +static bool write_png(const char *name, const uint *pixels, const int width, const int height) { png_structp png_ptr; png_infop info_ptr; @@ -199,9 +196,9 @@ static bool write_png(const char *name, /* Merge icon-data from files */ struct IconHead { - unsigned int icon_w, icon_h; - unsigned int orig_x, orig_y; - unsigned int canvas_w, canvas_h; + uint icon_w, icon_h; + uint orig_x, orig_y; + uint canvas_w, canvas_h; }; struct IconInfo { @@ -289,10 +286,10 @@ static bool icon_decode_head(FILE *f_src, struct IconHead *r_head) return false; } -static bool icon_decode(FILE *f_src, struct IconHead *r_head, unsigned int **r_pixels) +static bool icon_decode(FILE *f_src, struct IconHead *r_head, uint **r_pixels) { - unsigned int *pixels; - unsigned int pixels_size; + uint *pixels; + uint pixels_size; if (!icon_decode_head(f_src, r_head)) { printf("%s: failed to read header\n", __func__); @@ -316,7 +313,7 @@ static bool icon_decode(FILE *f_src, struct IconHead *r_head, unsigned int **r_p return true; } -static bool icon_read(const char *file_src, struct IconHead *r_head, unsigned int **r_pixels) +static bool icon_read(const char *file_src, struct IconHead *r_head, uint **r_pixels) { FILE *f_src; bool success; @@ -335,18 +332,18 @@ static bool icon_read(const char *file_src, struct IconHead *r_head, unsigned in static bool icon_merge(struct IconMergeContext *context, const char *file_src, - unsigned int **r_pixels_canvas, - unsigned int *r_canvas_w, - unsigned int *r_canvas_h) + uint32_t **r_pixels_canvas, + uint *r_canvas_w, + uint *r_canvas_h) { struct IconHead head; - unsigned int *pixels; + uint *pixels; - unsigned int x, y; + uint x, y; /* canvas */ - unsigned int *pixels_canvas; - unsigned int canvas_w, canvas_h; + uint32_t *pixels_canvas; + uint canvas_w, canvas_h; if (!icon_read(file_src, &head, &pixels)) { return false; @@ -365,7 +362,7 @@ static bool icon_merge(struct IconMergeContext *context, /* init once */ *r_canvas_w = head.canvas_w; *r_canvas_h = head.canvas_h; - *r_pixels_canvas = calloc(1, (head.canvas_w * head.canvas_h) * sizeof(const unsigned char[4])); + *r_pixels_canvas = calloc(1, (head.canvas_w * head.canvas_h) * sizeof(uint32_t)); } canvas_w = *r_canvas_w; @@ -377,9 +374,9 @@ static bool icon_merge(struct IconMergeContext *context, for (x = 0; x < head.icon_w; x++) { for (y = 0; y < head.icon_h; y++) { - unsigned int pixel; - unsigned int dst_x, dst_y; - unsigned int pixel_xy_dst; + uint pixel; + uint dst_x, dst_y; + uint pixel_xy_dst; /* get pixel */ pixel = pixels[(y * head.icon_w) + x]; @@ -413,8 +410,8 @@ static bool icondir_to_png(const char *path_src, const char *file_dst) struct IconMergeContext context; - unsigned int *pixels_canvas = NULL; - unsigned int canvas_w = 0, canvas_h = 0; + uint32_t *pixels_canvas = NULL; + uint canvas_w = 0, canvas_h = 0; icon_merge_context_init(&context); diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index 740124f6113..27441c9a7ae 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -109,11 +109,11 @@ void DEG_free_node_types(void); /* Update Tagging -------------------------------- */ -/* Update dependency graph when visible scenes/layers changes. */ -void DEG_graph_on_visible_update(struct Main *bmain, Depsgraph *depsgraph, const bool do_time); +/* Tag dependency graph for updates when visible scenes/layers changes. */ +void DEG_graph_tag_on_visible_update(Depsgraph *depsgraph, const bool do_time); -/* Update all dependency graphs when visible scenes/layers changes. */ -void DEG_on_visible_update(struct Main *bmain, const bool do_time); +/* Tag all dependency graphs for update when visible scenes/layers changes. */ +void DEG_tag_on_visible_update(struct Main *bmain, const bool do_time); /* NOTE: Will return NULL if the flag is not known, allowing to gracefully handle situations * when recalc flag has been removed. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index ae530cc010e..3ab278b0c4c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -450,6 +450,22 @@ void DepsgraphNodeBuilder::update_invalid_cow_pointers() /* Node/ID already tagged for COW flush, no need to check it. */ continue; } + if ((id_node->id_cow->flag & LIB_EMBEDDED_DATA) != 0) { + /* For now, we assume embedded data are managed by their owner IDs and do not need to be + * checked here. + * + * NOTE: This exception somewhat weak, and ideally should not be needed. Currently however, + * embedded data are handled as full local (private) data of their owner IDs in part of + * Blender (like read/write code, including undo/redo), while depsgraph generally treat them + * as regular independent IDs. This leads to inconsistencies that can lead to bad level + * memory accesses. + * + * E.g. when undoing creation/deletion of a collection directly child of a scene's master + * collection, the scene itself is re-read in place, but its master collection becomes a + * completely new different pointer, and the existing COW of the old master collection in the + * matching deg node is therefore pointing to fully invalid (freed) memory. */ + continue; + } BKE_library_foreach_ID_link(nullptr, id_node->id_cow, deg::foreach_id_cow_detect_need_for_update_callback, 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 32c36d78250..e4591e2e994 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -421,7 +421,7 @@ void DepsgraphRelationBuilder::build_rig(Object *object) if (prev) { OperationCode opcode = OperationCode::BONE_DONE; /* Inheriting parent roll requires access to prev handle's B-Bone properties. */ - if ((pchan->bone->flag & BONE_ADD_PARENT_END_ROLL) != 0 && + if ((pchan->bone->bbone_flag & BBONE_ADD_PARENT_END_ROLL) != 0 && check_pchan_has_bbone_segments(object, prev)) { opcode = OperationCode::BONE_SEGMENTS; } diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc index 10bc7213061..28e4c973c1e 100644 --- a/source/blender/depsgraph/intern/builder/pipeline.cc +++ b/source/blender/depsgraph/intern/builder/pipeline.cc @@ -98,7 +98,7 @@ void AbstractBuilderPipeline::build_step_finalize() deg_graph_->scene_cow = (Scene *)deg_graph_->get_cow_id(°_graph_->scene->id); /* Flush visibility layer and re-schedule nodes for update. */ deg_graph_build_finalize(bmain_, deg_graph_); - DEG_graph_on_visible_update(bmain_, reinterpret_cast<::Depsgraph *>(deg_graph_), false); + DEG_graph_tag_on_visible_update(reinterpret_cast<::Depsgraph *>(deg_graph_), false); #if 0 if (!DEG_debug_consistency_check(deg_graph_)) { printf("Consistency validation failed, ABORTING!\n"); diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 8e1ab23fae0..6fe7d5f5d8b 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -62,6 +62,8 @@ namespace blender::deg { Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode) : time_source(nullptr), need_update(true), + need_visibility_update(true), + need_visibility_time_update(false), bmain(bmain), scene(scene), view_layer(view_layer), diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index b87ce94709a..ff536c19c05 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -108,6 +108,11 @@ struct Depsgraph { /* Indicates whether relations needs to be updated. */ bool need_update; + /* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with + * an optional tag for their animation (time) update. */ + bool need_visibility_update; + bool need_visibility_time_update; + /* Indicates which ID types were updated. */ char id_type_updated[INDEX_ID_MAX]; diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 204143d7cbd..78c5a0c7a13 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -500,8 +500,23 @@ void deg_graph_node_tag_zero(Main *bmain, deg_graph_id_tag_legacy_compat(bmain, graph, id, (IDRecalcFlag)0, update_source); } -void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph, const bool do_time) +void graph_tag_on_visible_update(Depsgraph *graph, const bool do_time) { + graph->need_visibility_update = true; + graph->need_visibility_time_update |= do_time; +} + +} /* namespace */ + +void graph_tag_ids_for_visible_update(Depsgraph *graph) +{ + if (!graph->need_visibility_update) { + return; + } + + const bool do_time = graph->need_visibility_time_update; + Main *bmain = graph->bmain; + /* NOTE: It is possible to have this function called with `do_time=false` first and later (prior * to evaluation though) with `do_time=true`. This means early output checks should be aware of * this. */ @@ -559,9 +574,10 @@ void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph, const bool do_ti * dependency graph. */ id_node->previously_visible_components_mask = id_node->visible_components_mask; } -} -} /* namespace */ + graph->need_visibility_update = false; + graph->need_visibility_time_update = false; +} NodeType geometry_tag_to_component(const ID *id) { @@ -804,16 +820,16 @@ void DEG_id_type_tag(Main *bmain, short id_type) } /* Update dependency graph when visible scenes/layers changes. */ -void DEG_graph_on_visible_update(Main *bmain, Depsgraph *depsgraph, const bool do_time) +void DEG_graph_tag_on_visible_update(Depsgraph *depsgraph, const bool do_time) { deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph; - deg::deg_graph_on_visible_update(bmain, graph, do_time); + deg::graph_tag_on_visible_update(graph, do_time); } -void DEG_on_visible_update(Main *bmain, const bool do_time) +void DEG_tag_on_visible_update(Main *bmain, const bool do_time) { for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) { - DEG_graph_on_visible_update(bmain, reinterpret_cast<::Depsgraph *>(depsgraph), do_time); + deg::graph_tag_on_visible_update(depsgraph, do_time); } } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.h b/source/blender/depsgraph/intern/depsgraph_tag.h index 68b6a164be4..70504840fef 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.h +++ b/source/blender/depsgraph/intern/depsgraph_tag.h @@ -41,5 +41,9 @@ void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source); void graph_id_tag_update( Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source); +/* Tag IDs of the graph for the visibility update tags. + * Will do nothing if the graph is not tagged for visibility update. */ +void graph_tag_ids_for_visible_update(Depsgraph *graph); + } // namespace deg } // namespace blender diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 2107e075139..6f35143e28f 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -45,6 +45,7 @@ #include "intern/depsgraph.h" #include "intern/depsgraph_relation.h" +#include "intern/depsgraph_tag.h" #include "intern/eval/deg_eval_copy_on_write.h" #include "intern/eval/deg_eval_flush.h" #include "intern/eval/deg_eval_stats.h" @@ -353,8 +354,7 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) return BLI_task_pool_create_no_threads(state); } - /* TODO: Disable task isolation. */ - return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH); } /** @@ -366,6 +366,8 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) */ void deg_evaluate_on_refresh(Depsgraph *graph) { + graph_tag_ids_for_visible_update(graph); + /* Nothing to update, early out. */ if (graph->entry_tags.is_empty()) { return; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index e5d7bd70214..0d367762b00 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -320,7 +320,6 @@ bool id_copy_inplace_no_main(const ID *id, ID *newid) * is already allocated. */ bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene) { - const ID *id_for_copy = &scene->id; if (G.debug & G_DEBUG_DEPSGRAPH_UUID) { SEQ_relations_check_uuids_unique_and_report(scene); @@ -328,9 +327,10 @@ bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene) #ifdef NESTED_ID_NASTY_WORKAROUND NestedIDHackTempStorage id_hack_storage; - id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, &scene->id); + const ID *id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, &scene->id); +#else + const ID *id_for_copy = &scene->id; #endif - bool result = (BKE_id_copy_ex(nullptr, id_for_copy, (ID **)&new_scene, diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 3938242eb6e..4f68c3fdc7e 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -85,6 +85,7 @@ set(SRC intern/draw_manager_text.c intern/draw_manager_texture.c intern/draw_select_buffer.c + intern/draw_shader.c intern/draw_view.c engines/basic/basic_engine.c engines/image/image_engine.c @@ -172,6 +173,7 @@ set(SRC intern/DRW_render.h intern/draw_cache.h intern/draw_cache_extract.h + intern/draw_cache_extract_mesh_private.h intern/draw_cache_impl.h intern/draw_cache_inline.h intern/draw_color_management.h @@ -184,6 +186,7 @@ set(SRC intern/draw_manager_testing.h intern/draw_manager_text.h intern/draw_view.h + intern/draw_shader.h intern/smaa_textures.h engines/basic/basic_engine.h engines/eevee/eevee_engine.h diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c index 21d55357a2a..315133186da 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -397,7 +397,7 @@ static void gpencil_vfx_shadow(ShadowShaderFxData *fx, Object *ob, gpIterVfxData unit_m4(uv_mat); zero_v2(wave_ofs); - /* We reset the uv_mat so we need to account for the rotation in the */ + /* Reset the `uv_mat` to account for rotation in the Y-axis (Shadow-V parameter). */ copy_v2_fl2(tmp, 0.0f, blur_size[1]); rotate_v2_v2fl(blur_dir, tmp, -fx->rotation); mul_v2_v2(blur_dir, vp_size_inv); diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index fbad60ff4ab..e6c3248b6c4 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -1167,21 +1167,28 @@ static void ebone_spline_preview(EditBone *ebone, const float result_array[MAX_B param.roll1 = ebone->roll1; param.roll2 = ebone->roll2; - if (prev && (ebone->flag & BONE_ADD_PARENT_END_ROLL)) { + if (prev && (ebone->bbone_flag & BBONE_ADD_PARENT_END_ROLL)) { param.roll1 += prev->roll2; } - param.scale_in_x = ebone->scale_in_x; - param.scale_in_y = ebone->scale_in_y; - - param.scale_out_x = ebone->scale_out_x; - param.scale_out_y = ebone->scale_out_y; + copy_v3_v3(param.scale_in, ebone->scale_in); + copy_v3_v3(param.scale_out, ebone->scale_out); param.curve_in_x = ebone->curve_in_x; - param.curve_in_y = ebone->curve_in_y; + param.curve_in_z = ebone->curve_in_z; param.curve_out_x = ebone->curve_out_x; - param.curve_out_y = ebone->curve_out_y; + param.curve_out_z = ebone->curve_out_z; + + if (ebone->bbone_flag & BBONE_SCALE_EASING) { + param.ease1 *= param.scale_in[1]; + param.curve_in_x *= param.scale_in[1]; + param.curve_in_z *= param.scale_in[1]; + + param.ease2 *= param.scale_out[1]; + param.curve_out_x *= param.scale_out[1]; + param.curve_out_z *= param.scale_out[1]; + } ebone->segments = BKE_pchan_bbone_spline_compute(¶m, false, (Mat4 *)result_array); } @@ -2011,7 +2018,7 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) ((draw_ctx->object_mode == OB_MODE_OBJECT) && (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) || /* Allow selection when in weight-paint mode - * (selection code ensures this wont become active). */ + * (selection code ensures this won't become active). */ ((draw_ctx->object_mode & OB_MODE_ALL_WEIGHT_PAINT) && (draw_ctx->object_pose != NULL))))) && DRW_state_is_select(); @@ -2063,9 +2070,8 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) set_pchan_colorset(ctx, ob, pchan); } - int boneflag = bone->flag; /* catch exception for bone with hidden parent */ - boneflag = bone->flag; + int boneflag = bone->flag; if ((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { boneflag &= ~BONE_CONNECTED; } diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 19f822e3f68..81b07b49784 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -324,7 +324,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) !is_select; const bool draw_fade = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FADE_INACTIVE) && overlay_should_fade_object(ob, draw_ctx->obact); - const bool draw_mode_transfer = draw_surface && (pd->overlay.flag & V3D_OVERLAY_MODE_TRANSFER); + const bool draw_mode_transfer = draw_surface; const bool draw_bones = (pd->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0; const bool draw_wires = draw_surface && has_surface && (pd->wireframe_mode || !pd->hide_overlays); diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 304cae1d2dc..5fc6629e804 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -83,8 +83,9 @@ typedef enum eMRDataType { MR_DATA_LOOPTRI = 1 << 3, /** Force loop normals calculation. */ MR_DATA_TAN_LOOP_NOR = 1 << 4, + MR_DATA_MAT_OFFSETS = 1 << 5, } eMRDataType; -ENUM_OPERATORS(eMRDataType, MR_DATA_TAN_LOOP_NOR) +ENUM_OPERATORS(eMRDataType, MR_DATA_MAT_OFFSETS) #ifdef __cplusplus extern "C" { @@ -160,46 +161,19 @@ typedef struct MeshBufferCache { * - Loose geometry. */ typedef struct MeshBufferExtractionCache { - int edge_loose_len; - int vert_loose_len; - int *lverts; - int *ledges; -} MeshBufferExtractionCache; + struct { + int edge_len; + int vert_len; + int *verts; + int *edges; + } loose_geom; -typedef enum DRWBatchFlag { - MBC_SURFACE = (1 << 0), - MBC_SURFACE_WEIGHTS = (1 << 1), - MBC_EDIT_TRIANGLES = (1 << 2), - MBC_EDIT_VERTICES = (1 << 3), - MBC_EDIT_EDGES = (1 << 4), - MBC_EDIT_VNOR = (1 << 5), - MBC_EDIT_LNOR = (1 << 6), - MBC_EDIT_FACEDOTS = (1 << 7), - MBC_EDIT_MESH_ANALYSIS = (1 << 8), - MBC_EDITUV_FACES_STRETCH_AREA = (1 << 9), - MBC_EDITUV_FACES_STRETCH_ANGLE = (1 << 10), - MBC_EDITUV_FACES = (1 << 11), - MBC_EDITUV_EDGES = (1 << 12), - MBC_EDITUV_VERTS = (1 << 13), - MBC_EDITUV_FACEDOTS = (1 << 14), - MBC_EDIT_SELECTION_VERTS = (1 << 15), - MBC_EDIT_SELECTION_EDGES = (1 << 16), - MBC_EDIT_SELECTION_FACES = (1 << 17), - MBC_EDIT_SELECTION_FACEDOTS = (1 << 18), - MBC_ALL_VERTS = (1 << 19), - MBC_ALL_EDGES = (1 << 20), - MBC_LOOSE_EDGES = (1 << 21), - MBC_EDGE_DETECTION = (1 << 22), - MBC_WIRE_EDGES = (1 << 23), - MBC_WIRE_LOOPS = (1 << 24), - MBC_WIRE_LOOPS_UVS = (1 << 25), - MBC_SKIN_ROOTS = (1 << 26), - MBC_SCULPT_OVERLAYS = (1 << 27), -} DRWBatchFlag; + struct { + int *tri; + int visible_tri_len; + } mat_offsets; -#define MBC_EDITUV \ - (MBC_EDITUV_FACES_STRETCH_AREA | MBC_EDITUV_FACES_STRETCH_ANGLE | MBC_EDITUV_FACES | \ - MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS | MBC_WIRE_LOOPS_UVS) +} MeshBufferExtractionCache; #define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \ for (MeshBufferCache *mbc = &batch_cache->final; \ @@ -253,8 +227,8 @@ typedef struct MeshBatchCache { GPUBatch **surface_per_mat; - DRWBatchFlag batch_requested; - DRWBatchFlag batch_ready; + uint32_t batch_requested; /* DRWBatchFlag */ + uint32_t batch_ready; /* DRWBatchFlag */ /* settings to determine if cache is invalid */ int edge_len; @@ -288,6 +262,48 @@ typedef struct MeshBatchCache { #define MBC_VBO_LEN (sizeof(((MeshBufferCache){0}).vbo) / sizeof(void *)) #define MBC_IBO_LEN (sizeof(((MeshBufferCache){0}).ibo) / sizeof(void *)) +#define MBC_BATCH_INDEX(batch_name) \ + ((offsetof(MeshBatchCache, batch_name) - offsetof(MeshBatchCache, batch.surface)) / \ + sizeof(void *)) + +typedef enum DRWBatchFlag { + MBC_SURFACE = (1u << MBC_BATCH_INDEX(batch.surface)), + MBC_SURFACE_WEIGHTS = (1u << MBC_BATCH_INDEX(batch.surface_weights)), + MBC_EDIT_TRIANGLES = (1u << MBC_BATCH_INDEX(batch.edit_triangles)), + MBC_EDIT_VERTICES = (1u << MBC_BATCH_INDEX(batch.edit_vertices)), + MBC_EDIT_EDGES = (1u << MBC_BATCH_INDEX(batch.edit_edges)), + MBC_EDIT_VNOR = (1u << MBC_BATCH_INDEX(batch.edit_vnor)), + MBC_EDIT_LNOR = (1u << MBC_BATCH_INDEX(batch.edit_lnor)), + MBC_EDIT_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edit_fdots)), + MBC_EDIT_MESH_ANALYSIS = (1u << MBC_BATCH_INDEX(batch.edit_mesh_analysis)), + MBC_SKIN_ROOTS = (1u << MBC_BATCH_INDEX(batch.edit_skin_roots)), + MBC_EDITUV_FACES_STRETCH_AREA = (1u << MBC_BATCH_INDEX(batch.edituv_faces_stretch_area)), + MBC_EDITUV_FACES_STRETCH_ANGLE = (1u << MBC_BATCH_INDEX(batch.edituv_faces_stretch_angle)), + MBC_EDITUV_FACES = (1u << MBC_BATCH_INDEX(batch.edituv_faces)), + MBC_EDITUV_EDGES = (1u << MBC_BATCH_INDEX(batch.edituv_edges)), + MBC_EDITUV_VERTS = (1u << MBC_BATCH_INDEX(batch.edituv_verts)), + MBC_EDITUV_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edituv_fdots)), + MBC_EDIT_SELECTION_VERTS = (1u << MBC_BATCH_INDEX(batch.edit_selection_verts)), + MBC_EDIT_SELECTION_EDGES = (1u << MBC_BATCH_INDEX(batch.edit_selection_edges)), + MBC_EDIT_SELECTION_FACES = (1u << MBC_BATCH_INDEX(batch.edit_selection_faces)), + MBC_EDIT_SELECTION_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edit_selection_fdots)), + MBC_ALL_VERTS = (1u << MBC_BATCH_INDEX(batch.all_verts)), + MBC_ALL_EDGES = (1u << MBC_BATCH_INDEX(batch.all_edges)), + MBC_LOOSE_EDGES = (1u << MBC_BATCH_INDEX(batch.loose_edges)), + MBC_EDGE_DETECTION = (1u << MBC_BATCH_INDEX(batch.edge_detection)), + MBC_WIRE_EDGES = (1u << MBC_BATCH_INDEX(batch.wire_edges)), + MBC_WIRE_LOOPS = (1u << MBC_BATCH_INDEX(batch.wire_loops)), + MBC_WIRE_LOOPS_UVS = (1u << MBC_BATCH_INDEX(batch.wire_loops_uvs)), + MBC_SCULPT_OVERLAYS = (1u << MBC_BATCH_INDEX(batch.sculpt_overlays)), +} DRWBatchFlag; + +BLI_STATIC_ASSERT(MBC_BATCH_INDEX(surface_per_mat) < 32, + "Number of batches exceeded the limit of bit fields"); + +#define MBC_EDITUV \ + (MBC_EDITUV_FACES_STRETCH_AREA | MBC_EDITUV_FACES_STRETCH_ANGLE | MBC_EDITUV_FACES | \ + MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS | MBC_WIRE_LOOPS_UVS) + void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache *mbc, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 6b5877e6759..e577069f000 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -50,8 +50,6 @@ # include "PIL_time_utildefines.h" #endif -#define CHUNK_SIZE 8192 - namespace blender::draw { /* ---------------------------------------------------------------------- */ @@ -65,26 +63,12 @@ struct ExtractorRunData { const MeshExtract *extractor; /* During iteration the VBO/IBO that is being build. */ void *buffer = nullptr; - /* User data during iteration. Created in MeshExtract.init and passed along to other MeshExtract - * functions. */ - void *user_data = nullptr; - std::optional<Array<void *>> task_user_datas; + uint32_t data_offset = 0; ExtractorRunData(const MeshExtract *extractor) : extractor(extractor) { } - void init_task_user_datas(const TaskLen task_len) - { - task_user_datas = Array<void *>(task_len); - } - - void *&operator[](const TaskId task_id) - { - BLI_assert(task_user_datas); - return (*task_user_datas)[task_id]; - } - #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunData") #endif @@ -140,7 +124,7 @@ class ExtractorRunDatas : public Vector<ExtractorRunData> { return iter_type; } - const uint iter_types_len() const + uint iter_types_len() const { const eMRIterType iter_type = iter_types(); uint bits = static_cast<uint>(iter_type); @@ -157,6 +141,16 @@ class ExtractorRunDatas : public Vector<ExtractorRunData> { return data_type; } + size_t data_size_total() + { + size_t data_size = 0; + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + data_size += extractor->data_size; + } + return data_size; + } + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunDatas") #endif @@ -165,446 +159,334 @@ class ExtractorRunDatas : public Vector<ExtractorRunData> { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract +/** \name ExtractTaskData + * \{ */ +struct ExtractTaskData { + const MeshRenderData *mr = nullptr; + MeshBatchCache *cache = nullptr; + ExtractorRunDatas *extractors = nullptr; + MeshBufferCache *mbc = nullptr; + + eMRIterType iter_type; + bool use_threading = false; + + ExtractTaskData(const MeshRenderData *mr, + struct MeshBatchCache *cache, + ExtractorRunDatas *extractors, + MeshBufferCache *mbc, + const bool use_threading) + : mr(mr), cache(cache), extractors(extractors), mbc(mbc), use_threading(use_threading) + { + iter_type = extractors->iter_types(); + }; + + ExtractTaskData(const ExtractTaskData &src) = default; + + ~ExtractTaskData() + { + delete extractors; + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData") +#endif +}; + +static void extract_task_data_free(void *data) +{ + ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data); + delete task_data; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Init and Finish * \{ */ BLI_INLINE void extract_init(const MeshRenderData *mr, struct MeshBatchCache *cache, ExtractorRunDatas &extractors, - MeshBufferCache *mbc) + MeshBufferCache *mbc, + void *data_stack) { - /* Multi thread. */ + uint32_t data_offset = 0; for (ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; run_data.buffer = mesh_extract_buffer_get(extractor, mbc); - run_data.user_data = extractor->init(mr, cache, run_data.buffer); + run_data.data_offset = data_offset; + extractor->init(mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, data_offset)); + data_offset += (uint32_t)extractor->data_size; } } -BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, - const ExtractTriBMesh_Params *params, - const ExtractorRunDatas &all_extractors, - const TaskId task_id) +BLI_INLINE void extract_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + const ExtractorRunDatas &extractors, + void *data_stack) { - ExtractorRunDatas extractors; - all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); - - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) - { - for (ExtractorRunData &run_data : extractors) { - run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data[task_id]); + for (const ExtractorRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (extractor->finish) { + extractor->finish( + mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, run_data.data_offset)); } } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } -BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, - const ExtractTriMesh_Params *params, - const ExtractorRunDatas &all_extractors, - const TaskId task_id) -{ +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract In Parallel Ranges + * \{ */ + +struct ExtractorIterData { ExtractorRunDatas extractors; - all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); + const MeshRenderData *mr = nullptr; + const void *elems = nullptr; + const int *loose_elems = nullptr; - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) - { - for (ExtractorRunData &run_data : extractors) { - run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data[task_id]); +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData") +#endif +}; + +static void extract_task_reduce(const void *__restrict userdata, + void *__restrict chunk_to, + void *__restrict chunk_from) +{ + const ExtractorIterData *data = static_cast<const ExtractorIterData *>(userdata); + for (const ExtractorRunData &run_data : data->extractors) { + const MeshExtract *extractor = run_data.extractor; + if (extractor->task_reduce) { + extractor->task_reduce(POINTER_OFFSET(chunk_to, run_data.data_offset), + POINTER_OFFSET(chunk_from, run_data.data_offset)); } } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } -BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, - const ExtractorRunDatas &all_extractors, - const TaskId task_id) +static void extract_range_iter_looptri_bm(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - ExtractorRunDatas extractors; - all_extractors.filter_into(extractors, MR_ITER_POLY); - - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - for (ExtractorRunData &run_data : extractors) { - run_data.extractor->iter_poly_bm(mr, f, f_index, run_data[task_id]); - } + const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata); + void *extract_data = tls->userdata_chunk; + const MeshRenderData *mr = data->mr; + BMLoop **elt = ((BMLoop * (*)[3]) data->elems)[iter]; + for (const ExtractorRunData &run_data : data->extractors) { + run_data.extractor->iter_looptri_bm( + mr, elt, iter, POINTER_OFFSET(extract_data, run_data.data_offset)); } - EXTRACT_POLY_FOREACH_BM_END; } -BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, - const ExtractorRunDatas &all_extractors, - const TaskId task_id) +static void extract_range_iter_looptri_mesh(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - ExtractorRunDatas extractors; - all_extractors.filter_into(extractors, MR_ITER_POLY); + void *extract_data = tls->userdata_chunk; - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - for (ExtractorRunData &run_data : extractors) { - run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data[task_id]); - } + const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata); + const MeshRenderData *mr = data->mr; + const MLoopTri *mlt = &((const MLoopTri *)data->elems)[iter]; + for (const ExtractorRunData &run_data : data->extractors) { + run_data.extractor->iter_looptri_mesh( + mr, mlt, iter, POINTER_OFFSET(extract_data, run_data.data_offset)); } - EXTRACT_POLY_FOREACH_MESH_END; } -BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, - const ExtractorRunDatas &all_extractors, - const TaskId task_id) +static void extract_range_iter_poly_bm(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - ExtractorRunDatas extractors; - all_extractors.filter_into(extractors, MR_ITER_LEDGE); + void *extract_data = tls->userdata_chunk; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - for (ExtractorRunData &run_data : extractors) { - run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data[task_id]); - } + const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata); + const MeshRenderData *mr = data->mr; + const BMFace *f = ((const BMFace **)data->elems)[iter]; + for (const ExtractorRunData &run_data : data->extractors) { + run_data.extractor->iter_poly_bm( + mr, f, iter, POINTER_OFFSET(extract_data, run_data.data_offset)); } - EXTRACT_LEDGE_FOREACH_BM_END; } -BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, - const ExtractorRunDatas &all_extractors, - const TaskId task_id) +static void extract_range_iter_poly_mesh(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - ExtractorRunDatas extractors; - all_extractors.filter_into(extractors, MR_ITER_LEDGE); + void *extract_data = tls->userdata_chunk; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - for (ExtractorRunData &run_data : extractors) { - run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data[task_id]); - } + const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata); + const MeshRenderData *mr = data->mr; + const MPoly *mp = &((const MPoly *)data->elems)[iter]; + for (const ExtractorRunData &run_data : data->extractors) { + run_data.extractor->iter_poly_mesh( + mr, mp, iter, POINTER_OFFSET(extract_data, run_data.data_offset)); } - EXTRACT_LEDGE_FOREACH_MESH_END; } -BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, - const ExtractorRunDatas &all_extractors, - const TaskId task_id) +static void extract_range_iter_ledge_bm(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - ExtractorRunDatas extractors; - all_extractors.filter_into(extractors, MR_ITER_LVERT); + void *extract_data = tls->userdata_chunk; - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - for (ExtractorRunData &run_data : extractors) { - run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data[task_id]); - } + const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata); + const MeshRenderData *mr = data->mr; + const int ledge_index = data->loose_elems[iter]; + const BMEdge *eed = ((const BMEdge **)data->elems)[ledge_index]; + for (const ExtractorRunData &run_data : data->extractors) { + run_data.extractor->iter_ledge_bm( + mr, eed, iter, POINTER_OFFSET(extract_data, run_data.data_offset)); } - EXTRACT_LVERT_FOREACH_BM_END; } -BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, - const ExtractorRunDatas &all_extractors, - const TaskId task_id) +static void extract_range_iter_ledge_mesh(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - ExtractorRunDatas extractors; - all_extractors.filter_into(extractors, MR_ITER_LVERT); + void *extract_data = tls->userdata_chunk; - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - for (ExtractorRunData &run_data : extractors) { - run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data[task_id]); - } + const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata); + const MeshRenderData *mr = data->mr; + const int ledge_index = data->loose_elems[iter]; + const MEdge *med = &((const MEdge *)data->elems)[ledge_index]; + for (const ExtractorRunData &run_data : data->extractors) { + run_data.extractor->iter_ledge_mesh( + mr, med, iter, POINTER_OFFSET(extract_data, run_data.data_offset)); } - EXTRACT_LVERT_FOREACH_MESH_END; } -BLI_INLINE void extract_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - const ExtractorRunDatas &extractors) +static void extract_range_iter_lvert_bm(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - for (const ExtractorRunData &run_data : extractors) { - const MeshExtract *extractor = run_data.extractor; - if (extractor->finish) { - extractor->finish(mr, cache, run_data.buffer, run_data.user_data); - } + void *extract_data = tls->userdata_chunk; + + const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata); + const MeshRenderData *mr = data->mr; + const int lvert_index = data->loose_elems[iter]; + const BMVert *eve = ((const BMVert **)data->elems)[lvert_index]; + for (const ExtractorRunData &run_data : data->extractors) { + run_data.extractor->iter_lvert_bm( + mr, eve, iter, POINTER_OFFSET(extract_data, run_data.data_offset)); } } -BLI_INLINE void extract_task_init(ExtractorRunDatas &extractors, const TaskLen task_len) +static void extract_range_iter_lvert_mesh(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - for (ExtractorRunData &run_data : extractors) { - run_data.init_task_user_datas(task_len); - const MeshExtract *extractor = run_data.extractor; - for (TaskId task_id = 0; task_id < task_len; task_id++) { - void *user_task_data = run_data.user_data; - if (extractor->task_init) { - user_task_data = extractor->task_init(run_data.user_data); - } - run_data[task_id] = user_task_data; - } + void *extract_data = tls->userdata_chunk; + + const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata); + const MeshRenderData *mr = data->mr; + const int lvert_index = data->loose_elems[iter]; + const MVert *mv = &((const MVert *)data->elems)[lvert_index]; + for (const ExtractorRunData &run_data : data->extractors) { + run_data.extractor->iter_lvert_mesh( + mr, mv, iter, POINTER_OFFSET(extract_data, run_data.data_offset)); } } -BLI_INLINE void extract_task_finish(ExtractorRunDatas &extractors, const TaskLen task_len) +BLI_INLINE void extract_task_range_run_iter(const MeshRenderData *mr, + ExtractorRunDatas *extractors, + const eMRIterType iter_type, + bool is_mesh, + TaskParallelSettings *settings) { - for (ExtractorRunData &run_data : extractors) { - const MeshExtract *extractor = run_data.extractor; - if (extractor->task_finish) { - for (TaskId task_id = 0; task_id < task_len; task_id++) { - void *task_user_data = run_data[task_id]; - extractor->task_finish(run_data.user_data, task_user_data); - run_data[task_id] = nullptr; - } - } + ExtractorIterData range_data; + range_data.mr = mr; + + TaskParallelRangeFunc func; + int stop; + switch (iter_type) { + case MR_ITER_LOOPTRI: + range_data.elems = is_mesh ? mr->mlooptri : (void *)mr->edit_bmesh->looptris; + func = is_mesh ? extract_range_iter_looptri_mesh : extract_range_iter_looptri_bm; + stop = mr->tri_len; + break; + case MR_ITER_POLY: + range_data.elems = is_mesh ? mr->mpoly : (void *)mr->bm->ftable; + func = is_mesh ? extract_range_iter_poly_mesh : extract_range_iter_poly_bm; + stop = mr->poly_len; + break; + case MR_ITER_LEDGE: + range_data.loose_elems = mr->ledges; + range_data.elems = is_mesh ? mr->medge : (void *)mr->bm->etable; + func = is_mesh ? extract_range_iter_ledge_mesh : extract_range_iter_ledge_bm; + stop = mr->edge_loose_len; + break; + case MR_ITER_LVERT: + range_data.loose_elems = mr->lverts; + range_data.elems = is_mesh ? mr->mvert : (void *)mr->bm->vtable; + func = is_mesh ? extract_range_iter_lvert_mesh : extract_range_iter_lvert_bm; + stop = mr->vert_loose_len; + break; + default: + BLI_assert(false); + return; } + + extractors->filter_into(range_data.extractors, iter_type); + BLI_task_parallel_range(0, stop, &range_data, func, settings); } -/* Single Thread. */ -BLI_INLINE void extract_run_single_threaded(const MeshRenderData *mr, - struct MeshBatchCache *cache, - ExtractorRunDatas &extractors, - eMRIterType iter_type, - MeshBufferCache *mbc) +static void extract_task_range_run(void *__restrict taskdata) { - const TaskLen task_len = 1; - const TaskId task_id = 0; + ExtractTaskData *data = (ExtractTaskData *)taskdata; + const eMRIterType iter_type = data->iter_type; + const bool is_mesh = data->mr->extract_type != MR_EXTRACT_BMESH; + + size_t userdata_chunk_size = data->extractors->data_size_total(); + void *userdata_chunk = MEM_callocN(userdata_chunk_size, __func__); + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = data->use_threading; + settings.userdata_chunk = userdata_chunk; + settings.userdata_chunk_size = userdata_chunk_size; + settings.func_reduce = extract_task_reduce; + settings.min_iter_per_thread = MIN_RANGE_LEN; - extract_init(mr, cache, extractors, mbc); - extract_task_init(extractors, task_len); + extract_init(data->mr, data->cache, *data->extractors, data->mbc, userdata_chunk); - bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; if (iter_type & MR_ITER_LOOPTRI) { - if (is_mesh) { - ExtractTriMesh_Params params; - params.mlooptri = mr->mlooptri; - params.tri_range[0] = 0; - params.tri_range[1] = mr->tri_len; - extract_iter_looptri_mesh(mr, ¶ms, extractors, task_id); - } - else { - ExtractTriBMesh_Params params; - params.looptris = mr->edit_bmesh->looptris; - params.tri_range[0] = 0; - params.tri_range[1] = mr->tri_len; - extract_iter_looptri_bm(mr, ¶ms, extractors, task_id); - } + extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LOOPTRI, is_mesh, &settings); } if (iter_type & MR_ITER_POLY) { - if (is_mesh) { - ExtractPolyMesh_Params params; - params.poly_range[0] = 0; - params.poly_range[1] = mr->poly_len; - extract_iter_poly_mesh(mr, ¶ms, extractors, task_id); - } - else { - ExtractPolyBMesh_Params params; - params.poly_range[0] = 0; - params.poly_range[1] = mr->poly_len; - extract_iter_poly_bm(mr, ¶ms, extractors, task_id); - } + extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_POLY, is_mesh, &settings); } if (iter_type & MR_ITER_LEDGE) { - if (is_mesh) { - ExtractLEdgeMesh_Params params; - params.ledge = mr->ledges; - params.ledge_range[0] = 0; - params.ledge_range[1] = mr->edge_loose_len; - extract_iter_ledge_mesh(mr, ¶ms, extractors, task_id); - } - else { - ExtractLEdgeBMesh_Params params; - params.ledge = mr->ledges; - params.ledge_range[0] = 0; - params.ledge_range[1] = mr->edge_loose_len; - extract_iter_ledge_bm(mr, ¶ms, extractors, task_id); - } + extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LEDGE, is_mesh, &settings); } if (iter_type & MR_ITER_LVERT) { - if (is_mesh) { - ExtractLVertMesh_Params params; - params.lvert = mr->lverts; - params.lvert_range[0] = 0; - params.lvert_range[1] = mr->vert_loose_len; - extract_iter_lvert_mesh(mr, ¶ms, extractors, task_id); - } - else { - ExtractLVertBMesh_Params params; - params.lvert = mr->lverts; - params.lvert_range[0] = 0; - params.lvert_range[1] = mr->vert_loose_len; - extract_iter_lvert_bm(mr, ¶ms, extractors, task_id); - } + extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LVERT, is_mesh, &settings); } - extract_task_finish(extractors, task_len); - extract_finish(mr, cache, extractors); + + extract_finish(data->mr, data->cache, *data->extractors, userdata_chunk); + MEM_freeN(userdata_chunk); } /** \} */ /* ---------------------------------------------------------------------- */ -/** \name ExtractTaskData +/** \name Extract In Parallel Ranges * \{ */ -struct ExtractTaskData { - const MeshRenderData *mr = nullptr; - MeshBatchCache *cache = nullptr; - /* #UserData is shared between the iterations as it holds counters to detect if the - * extraction is finished. To make sure the duplication of the user_data does not create a new - * instance of the counters we allocate the user_data in its own container. - * - * This structure makes sure that when extract_init is called, that the user data of all - * iterations are updated. */ - - ExtractorRunDatas *extractors = nullptr; - MeshBufferCache *mbc = nullptr; - int32_t *task_counter = nullptr; - - /* Total number of tasks that are created for multi threaded extraction. - * (= 1 for single threaded extractors). */ - uint task_len; - /* Task id of the extraction task. Must never exceed task_len. (= 0 for single threaded - * extractors). */ - uint task_id = 0; - - eMRIterType iter_type; - int start = 0; - int end = INT_MAX; - /** Decremented each time a task is finished. */ - - ExtractTaskData(const MeshRenderData *mr, - struct MeshBatchCache *cache, - ExtractorRunDatas *extractors, - MeshBufferCache *mbc, - int32_t *task_counter, - const uint task_len) - : mr(mr), - cache(cache), - extractors(extractors), - mbc(mbc), - task_counter(task_counter), - task_len(task_len) - { - iter_type = extractors->iter_types(); - }; - - ExtractTaskData(const ExtractTaskData &src) = default; - - ~ExtractTaskData() - { - delete extractors; - } - -#ifdef WITH_CXX_GUARDEDALLOC - MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData") -#endif -}; - -static void extract_task_data_free(void *data) -{ - ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data); - delete task_data; -} - -static void extract_task_data_free_ex(void *data) -{ - ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data); - task_data->extractors = nullptr; - delete task_data; -} - -BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, - const eMRIterType iter_type, - int start, - int end, - ExtractorRunDatas &extractors, - const TaskId task_id) -{ - switch (mr->extract_type) { - case MR_EXTRACT_BMESH: - if (iter_type & MR_ITER_LOOPTRI) { - ExtractTriBMesh_Params params; - params.looptris = mr->edit_bmesh->looptris; - params.tri_range[0] = start; - params.tri_range[1] = min_ii(mr->tri_len, end); - extract_iter_looptri_bm(mr, ¶ms, extractors, task_id); - } - if (iter_type & MR_ITER_POLY) { - ExtractPolyBMesh_Params params; - params.poly_range[0] = start; - params.poly_range[1] = min_ii(mr->poly_len, end); - extract_iter_poly_bm(mr, ¶ms, extractors, task_id); - } - if (iter_type & MR_ITER_LEDGE) { - ExtractLEdgeBMesh_Params params; - params.ledge = mr->ledges; - params.ledge_range[0] = start; - params.ledge_range[1] = min_ii(mr->edge_loose_len, end); - extract_iter_ledge_bm(mr, ¶ms, extractors, task_id); - } - if (iter_type & MR_ITER_LVERT) { - ExtractLVertBMesh_Params params; - params.lvert = mr->lverts; - params.lvert_range[0] = start; - params.lvert_range[1] = min_ii(mr->vert_loose_len, end); - extract_iter_lvert_bm(mr, ¶ms, extractors, task_id); - } - break; - case MR_EXTRACT_MAPPED: - case MR_EXTRACT_MESH: - if (iter_type & MR_ITER_LOOPTRI) { - ExtractTriMesh_Params params; - params.mlooptri = mr->mlooptri; - params.tri_range[0] = start; - params.tri_range[1] = min_ii(mr->tri_len, end); - extract_iter_looptri_mesh(mr, ¶ms, extractors, task_id); - } - if (iter_type & MR_ITER_POLY) { - ExtractPolyMesh_Params params; - params.poly_range[0] = start; - params.poly_range[1] = min_ii(mr->poly_len, end); - extract_iter_poly_mesh(mr, ¶ms, extractors, task_id); - } - if (iter_type & MR_ITER_LEDGE) { - ExtractLEdgeMesh_Params params; - params.ledge = mr->ledges; - params.ledge_range[0] = start; - params.ledge_range[1] = min_ii(mr->edge_loose_len, end); - extract_iter_ledge_mesh(mr, ¶ms, extractors, task_id); - } - if (iter_type & MR_ITER_LVERT) { - ExtractLVertMesh_Params params; - params.lvert = mr->lverts; - params.lvert_range[0] = start; - params.lvert_range[1] = min_ii(mr->vert_loose_len, end); - extract_iter_lvert_mesh(mr, ¶ms, extractors, task_id); - } - break; - } -} - -static void extract_task_init(ExtractTaskData *data) -{ - extract_init(data->mr, data->cache, *data->extractors, data->mbc); - extract_task_init(*data->extractors, data->task_len); -} - -static void extract_task_run(void *__restrict taskdata) -{ - ExtractTaskData *data = (ExtractTaskData *)taskdata; - mesh_extract_iter( - data->mr, data->iter_type, data->start, data->end, *data->extractors, data->task_id); - - /* If this is the last task, we do the finish function. */ - int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); - if (remainin_tasks == 0) { - extract_task_finish(*data->extractors, data->task_len); - extract_finish(data->mr, data->cache, *data->extractors); - } -} -static void extract_task_init_and_run(void *__restrict taskdata) +static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph, + const MeshRenderData *mr, + MeshBatchCache *cache, + ExtractorRunDatas *extractors, + MeshBufferCache *mbc, + const bool use_threading) { - ExtractTaskData *data = (ExtractTaskData *)taskdata; - extract_run_single_threaded( - data->mr, data->cache, *data->extractors, data->iter_type, data->mbc); + ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, use_threading); + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + extract_task_range_run, + taskdata, + (TaskGraphNodeFreeFunction)extract_task_data_free); + return task_node; } /** \} */ @@ -614,11 +496,15 @@ static void extract_task_init_and_run(void *__restrict taskdata) * \{ */ struct MeshRenderDataUpdateTaskData { MeshRenderData *mr = nullptr; + MeshBufferExtractionCache *cache = nullptr; eMRIterType iter_type; eMRDataType data_flag; - MeshRenderDataUpdateTaskData(MeshRenderData *mr, eMRIterType iter_type, eMRDataType data_flag) - : mr(mr), iter_type(iter_type), data_flag(data_flag) + MeshRenderDataUpdateTaskData(MeshRenderData *mr, + MeshBufferExtractionCache *cache, + eMRIterType iter_type, + eMRDataType data_flag) + : mr(mr), cache(cache), iter_type(iter_type), data_flag(data_flag) { } @@ -649,15 +535,17 @@ static void mesh_extract_render_data_node_exec(void *__restrict task_data) mesh_render_data_update_normals(mr, data_flag); mesh_render_data_update_looptris(mr, iter_type, data_flag); + mesh_render_data_update_mat_offsets(mr, update_task_data->cache, data_flag); } static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, MeshRenderData *mr, + MeshBufferExtractionCache *cache, const eMRIterType iter_type, const eMRDataType data_flag) { MeshRenderDataUpdateTaskData *task_data = new MeshRenderDataUpdateTaskData( - mr, iter_type, data_flag); + mr, cache, iter_type, data_flag); struct TaskNode *task_node = BLI_task_graph_node_create( task_graph, @@ -670,153 +558,9 @@ static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *t /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Task Node - Extract Single Threaded - * \{ */ - -static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, - ExtractTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - extract_task_init_and_run, - task_data, - (TaskGraphNodeFreeFunction)extract_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - UserData Initializer - * \{ */ -struct UserDataInitTaskData { - ExtractTaskData *td = nullptr; - int32_t task_counter = 0; - - ~UserDataInitTaskData() - { - extract_task_data_free(td); - } - -#ifdef WITH_CXX_GUARDEDALLOC - MEM_CXX_CLASS_ALLOC_FUNCS("DRW:UserDataInitTaskData") -#endif -}; - -static void user_data_init_task_data_free(void *data) -{ - UserDataInitTaskData *taskdata = static_cast<UserDataInitTaskData *>(data); - delete taskdata; -} - -static void user_data_init_task_data_exec(void *__restrict task_data) -{ - UserDataInitTaskData *extract_task_data = static_cast<UserDataInitTaskData *>(task_data); - ExtractTaskData *taskdata_base = extract_task_data->td; - extract_task_init(taskdata_base); -} - -static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, - UserDataInitTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - user_data_init_task_data_exec, - task_data, - (TaskGraphNodeFreeFunction)user_data_init_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ /** \name Extract Loop * \{ */ -static void extract_range_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_user_data_init, - ExtractTaskData *taskdata, - const eMRIterType type, - int start, - int length) -{ - taskdata = new ExtractTaskData(*taskdata); - taskdata->task_id = atomic_fetch_and_add_int32(taskdata->task_counter, 1); - BLI_assert(taskdata->task_id < taskdata->task_len); - taskdata->iter_type = type; - taskdata->start = start; - taskdata->end = start + length; - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, extract_task_run, taskdata, extract_task_data_free_ex); - BLI_task_graph_edge_create(task_node_user_data_init, task_node); -} - -static int extract_range_task_num_elements_get(const MeshRenderData *mr, - const eMRIterType iter_type) -{ - /* Divide task into sensible chunks. */ - int iter_len = 0; - if (iter_type & MR_ITER_LOOPTRI) { - iter_len += mr->tri_len; - } - if (iter_type & MR_ITER_POLY) { - iter_len += mr->poly_len; - } - if (iter_type & MR_ITER_LEDGE) { - iter_len += mr->edge_loose_len; - } - if (iter_type & MR_ITER_LVERT) { - iter_len += mr->vert_loose_len; - } - return iter_len; -} - -static int extract_range_task_chunk_size_get(const MeshRenderData *mr, - const eMRIterType iter_type, - const int num_threads) -{ - /* Divide task into sensible chunks. */ - const int num_elements = extract_range_task_num_elements_get(mr, iter_type); - int range_len = (num_elements + num_threads) / num_threads; - CLAMP_MIN(range_len, CHUNK_SIZE); - return range_len; -} - -static void extract_task_in_ranges_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_user_data_init, - ExtractTaskData *taskdata_base, - const int num_threads) -{ - const MeshRenderData *mr = taskdata_base->mr; - const int range_len = extract_range_task_chunk_size_get( - mr, taskdata_base->iter_type, num_threads); - - if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { - for (int i = 0; i < mr->tri_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_POLY) { - for (int i = 0; i < mr->poly_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_LEDGE) { - for (int i = 0; i < mr->edge_loose_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_LVERT) { - for (int i = 0; i < mr->vert_loose_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); - } - } -} - static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache *mbc, @@ -962,26 +706,22 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, #endif struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( - task_graph, mr, iter_type, data_flag); + task_graph, mr, extraction_cache, iter_type, data_flag); /* Simple heuristic. */ - const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; + const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIN_RANGE_LEN; if (use_thread) { - uint single_threaded_extractors_len = 0; - /* First run the requested extractors that do not support asynchronous ranges. */ for (const ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; if (!extractor->use_threading) { ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas(); single_threaded_extractors->append(extractor); - ExtractTaskData *taskdata = new ExtractTaskData( - mr, cache, single_threaded_extractors, mbc, nullptr, 1); - struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, - taskdata); + struct TaskNode *task_node = extract_task_node_create( + task_graph, mr, cache, single_threaded_extractors, mbc, false); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); - single_threaded_extractors_len++; } } @@ -989,31 +729,10 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, ExtractorRunDatas *multi_threaded_extractors = new ExtractorRunDatas(); extractors.filter_threaded_extractors_into(*multi_threaded_extractors); if (!multi_threaded_extractors->is_empty()) { - /* - * Determine the number of thread to use for multithreading. - * Thread can be used for single threaded tasks. These typically take longer to execute so - * fill the rest of the threads for range operations. - */ - int num_threads = BLI_task_scheduler_num_threads(); - num_threads -= single_threaded_extractors_len % num_threads; - const int max_multithreaded_task_len = multi_threaded_extractors->iter_types_len() + - num_threads; - - UserDataInitTaskData *user_data_init_task_data = new UserDataInitTaskData(); - struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( - task_graph, user_data_init_task_data); - - user_data_init_task_data->td = new ExtractTaskData(mr, - cache, - multi_threaded_extractors, - mbc, - &user_data_init_task_data->task_counter, - max_multithreaded_task_len); - - extract_task_in_ranges_create( - task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); - - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); + struct TaskNode *task_node = extract_task_node_create( + task_graph, mr, cache, multi_threaded_extractors, mbc, true); + + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); } else { /* No tasks created freeing extractors list. */ @@ -1023,9 +742,9 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, else { /* Run all requests on the same thread. */ ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors); - ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors_copy, mbc, nullptr, 1); + struct TaskNode *task_node = extract_task_node_create( + task_graph, mr, cache, extractors_copy, mbc, false); - struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); } diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index 3cac391c42d..42cd571b089 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -130,12 +130,13 @@ typedef struct PosNorLoop { typedef struct MeshExtract_PosNor_Data { PosNorLoop *vbo_data; - GPUNormal normals[]; + GPUNormal *normals; } MeshExtract_PosNor_Data; -static void *extract_pos_nor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_pos_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -149,9 +150,9 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); /* Pack normals per vert, reduce amount of computation. */ - size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; - MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); + MeshExtract_PosNor_Data *data = tls_data; data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo); + data->normals = MEM_mallocN(sizeof(GPUNormal) * mr->vert_len, __func__); /* Quicker than doing it for each loop. */ if (mr->extract_type == MR_EXTRACT_BMESH) { @@ -168,11 +169,10 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, data->normals[v].low = GPU_normal_convert_i10_s3(mv->no); } } - return data; } static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -220,7 +220,7 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, } static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, + const BMEdge *eed, const int ledge_index, void *_data) { @@ -249,7 +249,7 @@ static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, } static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, + const BMVert *eve, const int lvert_index, void *_data) { @@ -280,9 +280,10 @@ static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), - void *data) + void *_data) { - MEM_freeN(data); + MeshExtract_PosNor_Data *data = _data; + MEM_freeN(data->normals); } const MeshExtract extract_pos_nor = { @@ -295,6 +296,7 @@ const MeshExtract extract_pos_nor = { .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, .finish = extract_pos_nor_finish, .data_type = 0, + .data_size = sizeof(MeshExtract_PosNor_Data), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor), }; @@ -312,12 +314,13 @@ typedef struct PosNorHQLoop { typedef struct MeshExtract_PosNorHQ_Data { PosNorHQLoop *vbo_data; - GPUNormal normals[]; + GPUNormal *normals; } MeshExtract_PosNorHQ_Data; -static void *extract_pos_nor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_pos_nor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -331,9 +334,9 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); /* Pack normals per vert, reduce amount of computation. */ - size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; - MeshExtract_PosNorHQ_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); + MeshExtract_PosNorHQ_Data *data = tls_data; data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo); + data->normals = MEM_mallocN(sizeof(GPUNormal) * mr->vert_len, __func__); /* Quicker than doing it for each loop. */ if (mr->extract_type == MR_EXTRACT_BMESH) { @@ -350,11 +353,10 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, copy_v3_v3_short(data->normals[v].high, mv->no); } } - return data; } static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -404,7 +406,7 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, } static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, + const BMEdge *eed, const int ledge_index, void *_data) { @@ -436,7 +438,7 @@ static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, } static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, + const BMVert *eve, const int lvert_index, void *_data) { @@ -469,9 +471,10 @@ static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), - void *data) + void *_data) { - MEM_freeN(data); + MeshExtract_PosNorHQ_Data *data = _data; + MEM_freeN(data->normals); } const MeshExtract extract_pos_nor_hq = { @@ -484,6 +487,7 @@ const MeshExtract extract_pos_nor_hq = { .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, .finish = extract_pos_nor_hq_finish, .data_type = 0, + .data_size = sizeof(MeshExtract_PosNorHQ_Data), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; @@ -496,9 +500,10 @@ typedef struct gpuHQNor { short x, y, z, w; } gpuHQNor; -static void *extract_lnor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_lnor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -509,11 +514,11 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); - return GPU_vertbuf_get_data(vbo); + *(gpuHQNor **)tls_data = GPU_vertbuf_get_data(vbo); } static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *data) { @@ -522,14 +527,14 @@ static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, do { const int l_index = BM_elem_index_get(l_iter); if (mr->loop_normals) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); + normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, mr->loop_normals[l_index]); } else { if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); + normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); } else { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); + normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, bm_face_no_get(mr, f)); } } } while ((l_iter = l_iter->next) != l_first); @@ -544,7 +549,7 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; + gpuHQNor *lnor_data = &(*(gpuHQNor **)data)[ml_index]; if (mr->loop_normals) { normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); } @@ -576,6 +581,7 @@ const MeshExtract extract_lnor_hq = { .iter_poly_bm = extract_lnor_hq_iter_poly_bm, .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, .data_type = MR_DATA_LOOP_NOR, + .data_size = sizeof(gpuHQNor *), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor), }; @@ -585,9 +591,10 @@ const MeshExtract extract_lnor_hq = { /** \name Extract Loop Normal * \{ */ -static void *extract_lnor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_lnor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -598,11 +605,11 @@ static void *extract_lnor_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); - return GPU_vertbuf_get_data(vbo); + *(GPUPackedNormal **)tls_data = GPU_vertbuf_get_data(vbo); } static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *data) { @@ -611,18 +618,18 @@ static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, do { const int l_index = BM_elem_index_get(l_iter); if (mr->loop_normals) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); + (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); } else { if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( + (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3( bm_vert_no_get(mr, l_iter->v)); } else { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); + (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); } } - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + (*(GPUPackedNormal **)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; } while ((l_iter = l_iter->next) != l_first); } @@ -635,7 +642,7 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; + GPUPackedNormal *lnor_data = &(*(GPUPackedNormal **)data)[ml_index]; if (mr->loop_normals) { *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); } @@ -667,6 +674,7 @@ const MeshExtract extract_lnor = { .iter_poly_bm = extract_lnor_iter_poly_bm, .iter_poly_mesh = extract_lnor_iter_poly_mesh, .data_type = MR_DATA_LOOP_NOR, + .data_size = sizeof(GPUPackedNormal *), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor), }; @@ -677,7 +685,10 @@ const MeshExtract extract_lnor = { /** \name Extract UV layers * \{ */ -static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +static void extract_uv_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data)) { GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; @@ -757,13 +768,12 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca } } } - - return NULL; } const MeshExtract extract_uv = { .init = extract_uv_init, .data_type = 0, + .data_size = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv), }; @@ -946,15 +956,18 @@ static void extract_tan_ex_init(const MeshRenderData *mr, CustomData_free(&loop_data, mr->loop_len); } -static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +static void extract_tan_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data)) { extract_tan_ex_init(mr, cache, buf, false); - return NULL; } const MeshExtract extract_tan = { .init = extract_tan_init, .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .data_size = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan), }; @@ -965,16 +978,20 @@ const MeshExtract extract_tan = { /** \name Extract HQ Tangent layers * \{ */ -static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +static void extract_tan_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data)) { extract_tan_ex_init(mr, cache, buf, true); - return NULL; } const MeshExtract extract_tan_hq = { .init = extract_tan_hq_init, .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .data_size = 0, .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan), }; /** \} */ @@ -983,9 +1000,10 @@ const MeshExtract extract_tan_hq = { /** \name Extract Sculpt Data * \{ */ -static void *extract_sculpt_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_sculpt_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(tls_data)) { GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; @@ -1066,13 +1084,12 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, } } } - - return NULL; } const MeshExtract extract_sculpt_data = { .init = extract_sculpt_data_init, .data_type = 0, + .data_size = 0, /* TODO: enable threading. */ .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; @@ -1083,7 +1100,10 @@ const MeshExtract extract_sculpt_data = { /** \name Extract VCol * \{ */ -static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +static void extract_vcol_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data)) { GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; @@ -1216,12 +1236,12 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * } } } - return NULL; } const MeshExtract extract_vcol = { .init = extract_vcol_init, .data_type = 0, + .data_size = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol), }; @@ -1237,9 +1257,10 @@ typedef struct MeshExtract_Orco_Data { float (*orco)[3]; } MeshExtract_Orco_Data; -static void *extract_orco_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_orco_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -1256,16 +1277,15 @@ static void *extract_orco_init(const MeshRenderData *mr, CustomData *cd_vdata = &mr->me->vdata; - MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__); + MeshExtract_Orco_Data *data = tls_data; data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo); data->orco = CustomData_get_layer(cd_vdata, CD_ORCO); /* Make sure `orco` layer was requested only if needed! */ BLI_assert(data->orco); - return data; } static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *data) { @@ -1296,20 +1316,12 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_orco_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - const MeshExtract extract_orco = { .init = extract_orco_init, .iter_poly_bm = extract_orco_iter_poly_bm, .iter_poly_mesh = extract_orco_iter_poly_mesh, - .finish = extract_orco_finish, .data_type = 0, + .data_size = sizeof(MeshExtract_Orco_Data), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco), }; @@ -1325,7 +1337,7 @@ typedef struct MeshExtract_EdgeFac_Data { uchar *vbo_data; bool use_edge_render; /* Number of loop per edge. */ - uchar edge_loop_count[0]; + uchar *edge_loop_count; } MeshExtract_EdgeFac_Data; static float loop_edge_factor_get(const float f_no[3], @@ -1344,9 +1356,10 @@ static float loop_edge_factor_get(const float f_no[3], return d; } -static void *extract_edge_fac_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_edge_fac_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -1357,11 +1370,10 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - MeshExtract_EdgeFac_Data *data; + MeshExtract_EdgeFac_Data *data = tls_data; if (mr->extract_type == MR_EXTRACT_MESH) { - size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len; - data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__); + data->edge_loop_count = MEM_callocN(sizeof(uint32_t) * mr->edge_len, __func__); /* HACK(fclem) Detecting the need for edge render. * We could have a flag in the mesh instead or check the modifier stack. */ @@ -1374,17 +1386,15 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, } } else { - data = MEM_callocN(sizeof(*data), __func__); /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */ data->use_edge_render = true; } data->vbo_data = GPU_vertbuf_get_data(vbo); - return data; } static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -1450,7 +1460,7 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, } static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *UNUSED(eed), + const BMEdge *UNUSED(eed), const int ledge_index, void *_data) { @@ -1501,7 +1511,7 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, /* Free old byte data. */ MEM_freeN(data->vbo_data); } - MEM_freeN(data); + MEM_SAFE_FREE(data->edge_loop_count); } const MeshExtract extract_edge_fac = { @@ -1512,6 +1522,7 @@ const MeshExtract extract_edge_fac = { .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, .finish = extract_edge_fac_finish, .data_type = MR_DATA_POLY_NOR, + .data_size = sizeof(MeshExtract_EdgeFac_Data), .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; @@ -1580,9 +1591,10 @@ static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeig return input; } -static void *extract_weights_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf) +static void extract_weights_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -1592,7 +1604,7 @@ static void *extract_weights_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__); + MeshExtract_Weight_Data *data = tls_data; data->vbo_data = (float *)GPU_vertbuf_get_data(vbo); data->wstate = &cache->weight_state; @@ -1609,11 +1621,10 @@ static void *extract_weights_init(const MeshRenderData *mr, data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT); data->cd_ofs = -1; } - return data; } static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -1653,20 +1664,12 @@ static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_weights_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - const MeshExtract extract_weights = { .init = extract_weights_init, .iter_poly_bm = extract_weights_iter_poly_bm, .iter_poly_mesh = extract_weights_iter_poly_mesh, - .finish = extract_weights_finish, .data_type = 0, + .data_size = sizeof(MeshExtract_Weight_Data), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights), }; @@ -1685,7 +1688,7 @@ typedef struct EditLoopData { } EditLoopData; static void mesh_render_data_face_flag(const MeshRenderData *mr, - BMFace *efa, + const BMFace *efa, const int cd_ofs, EditLoopData *eattr) { @@ -1713,7 +1716,9 @@ static void mesh_render_data_face_flag(const MeshRenderData *mr, #endif } -static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr) +static void mesh_render_data_edge_flag(const MeshRenderData *mr, + const BMEdge *eed, + EditLoopData *eattr) { const ToolSettings *ts = mr->toolsettings; const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0; @@ -1805,7 +1810,9 @@ static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr, } } -static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr) +static void mesh_render_data_vert_flag(const MeshRenderData *mr, + const BMVert *eve, + EditLoopData *eattr) { if (eve == mr->eve_act) { eattr->e_flag |= VFLAG_VERT_ACTIVE; @@ -1815,9 +1822,10 @@ static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, Ed } } -static void *extract_edit_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_edit_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -1828,20 +1836,23 @@ static void *extract_edit_data_init(const MeshRenderData *mr, } GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - return GPU_vertbuf_get_data(vbo); + EditLoopData *vbo_data = GPU_vertbuf_get_data(vbo); + *(EditLoopData **)tls_data = vbo_data; } static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { + EditLoopData *vbo_data = *(EditLoopData **)_data; + BMLoop *l_iter, *l_first; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { const int l_index = BM_elem_index_get(l_iter); - EditLoopData *data = (EditLoopData *)_data + l_index; + EditLoopData *data = vbo_data + l_index; memset(data, 0x0, sizeof(*data)); mesh_render_data_face_flag(mr, f, -1, data); mesh_render_data_edge_flag(mr, l_iter->e, data); @@ -1854,11 +1865,13 @@ static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, const int mp_index, void *_data) { + EditLoopData *vbo_data = *(EditLoopData **)_data; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - EditLoopData *data = (EditLoopData *)_data + ml_index; + EditLoopData *data = vbo_data + ml_index; memset(data, 0x0, sizeof(*data)); BMFace *efa = bm_original_face_get(mr, mp_index); BMEdge *eed = bm_original_edge_get(mr, ml->e); @@ -1876,11 +1889,12 @@ static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, } static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, + const BMEdge *eed, const int ledge_index, void *_data) { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); + EditLoopData *vbo_data = *(EditLoopData **)_data; + EditLoopData *data = vbo_data + mr->loop_len + (ledge_index * 2); memset(data, 0x0, sizeof(*data) * 2); mesh_render_data_edge_flag(mr, eed, &data[0]); data[1] = data[0]; @@ -1893,7 +1907,8 @@ static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, const int ledge_index, void *_data) { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; + EditLoopData *vbo_data = *(EditLoopData **)_data; + EditLoopData *data = vbo_data + mr->loop_len + ledge_index * 2; memset(data, 0x0, sizeof(*data) * 2); const int e_index = mr->ledges[ledge_index]; BMEdge *eed = bm_original_edge_get(mr, e_index); @@ -1912,12 +1927,13 @@ static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, } static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, + const BMVert *eve, const int lvert_index, void *_data) { + EditLoopData *vbo_data = *(EditLoopData **)_data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + EditLoopData *data = vbo_data + offset + lvert_index; memset(data, 0x0, sizeof(*data)); mesh_render_data_vert_flag(mr, eve, data); } @@ -1927,9 +1943,10 @@ static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, const int lvert_index, void *_data) { + EditLoopData *vbo_data = *(EditLoopData **)_data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + EditLoopData *data = vbo_data + offset + lvert_index; memset(data, 0x0, sizeof(*data)); const int v_index = mr->lverts[lvert_index]; BMVert *eve = bm_original_vert_get(mr, v_index); @@ -1947,6 +1964,7 @@ const MeshExtract extract_edit_data = { .iter_lvert_bm = extract_edit_data_iter_lvert_bm, .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, .data_type = 0, + .data_size = sizeof(EditLoopData *), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; @@ -1961,9 +1979,10 @@ typedef struct MeshExtract_EditUVData_Data { int cd_ofs; } MeshExtract_EditUVData_Data; -static void *extract_edituv_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -1978,14 +1997,13 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__); + MeshExtract_EditUVData_Data *data = tls_data; data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); - return data; } static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -2044,20 +2062,12 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - const MeshExtract extract_edituv_data = { .init = extract_edituv_data_init, .iter_poly_bm = extract_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, - .finish = extract_edituv_data_finish, .data_type = 0, + .data_size = sizeof(MeshExtract_EditUVData_Data), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; @@ -2067,9 +2077,10 @@ const MeshExtract extract_edituv_data = { /** \name Extract Edit UV area stretch * \{ */ -static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_edituv_stretch_area_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(tls_data)) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -2079,8 +2090,6 @@ static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return NULL; } BLI_INLINE float area_ratio_get(float area, float uvarea) @@ -2174,6 +2183,7 @@ const MeshExtract extract_edituv_stretch_area = { .init = extract_edituv_stretch_area_init, .finish = extract_edituv_stretch_area_finish, .data_type = 0, + .data_size = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; @@ -2237,9 +2247,10 @@ static void edituv_get_edituv_stretch_angle(float auv[2][2], #endif } -static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_edituv_stretch_angle_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -2252,7 +2263,7 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); - MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__); + MeshExtract_StretchAngle_Data *data = tls_data; data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo); /* Special iterator needed to save about half of the computing cost. */ @@ -2263,11 +2274,10 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); } - return data; } static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -2363,20 +2373,12 @@ static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr } } -static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - const MeshExtract extract_edituv_stretch_angle = { .init = extract_edituv_stretch_angle_init, .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, - .finish = extract_edituv_stretch_angle_finish, .data_type = 0, + .data_size = sizeof(MeshExtract_StretchAngle_Data), .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; @@ -2386,9 +2388,10 @@ const MeshExtract extract_edituv_stretch_angle = { /** \name Extract Edit Mesh Analysis Colors * \{ */ -static void *extract_mesh_analysis_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_mesh_analysis_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(tls_data)) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -2398,8 +2401,6 @@ static void *extract_mesh_analysis_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return NULL; } static void axis_from_enum_v3(float v[3], const char axis) @@ -2984,6 +2985,7 @@ const MeshExtract extract_mesh_analysis = { /* This is not needed for all visualization types. * * Maybe split into different extract. */ .data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, + .data_size = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; @@ -2993,9 +2995,10 @@ const MeshExtract extract_mesh_analysis = { /** \name Extract Face-dots positions * \{ */ -static void *extract_fdots_pos_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_fdots_pos_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -3005,15 +3008,15 @@ static void *extract_fdots_pos_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); - return GPU_vertbuf_get_data(vbo); + *(float(**)[3])tls_data = GPU_vertbuf_get_data(vbo); } static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int f_index, void *data) { - float(*center)[3] = data; + float(*center)[3] = *(float(**)[3])data; float *co = center[f_index]; zero_v3(co); @@ -3031,7 +3034,7 @@ static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, const int mp_index, void *data) { - float(*center)[3] = (float(*)[3])data; + float(*center)[3] = *(float(**)[3])data; float *co = center[mp_index]; zero_v3(co); @@ -3064,6 +3067,7 @@ const MeshExtract extract_fdots_pos = { .iter_poly_bm = extract_fdots_pos_iter_poly_bm, .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, .data_type = 0, + .data_size = sizeof(float (*)[3]), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; @@ -3077,9 +3081,10 @@ const MeshExtract extract_fdots_pos = { #define NOR_AND_FLAG_ACTIVE -1 #define NOR_AND_FLAG_HIDDEN -2 -static void *extract_fdots_nor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_fdots_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(tls_data)) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -3089,8 +3094,6 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); - - return NULL; } static void extract_fdots_nor_finish(const MeshRenderData *mr, @@ -3146,6 +3149,7 @@ const MeshExtract extract_fdots_nor = { .init = extract_fdots_nor_init, .finish = extract_fdots_nor_finish, .data_type = MR_DATA_POLY_NOR, + .data_size = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; @@ -3154,9 +3158,10 @@ const MeshExtract extract_fdots_nor = { /* ---------------------------------------------------------------------- */ /** \name Extract Face-dots High Quality Normal and edit flag * \{ */ -static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_fdots_nor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(tls_data)) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -3166,8 +3171,6 @@ static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); - - return NULL; } static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, @@ -3223,6 +3226,7 @@ const MeshExtract extract_fdots_nor_hq = { .init = extract_fdots_nor_hq_init, .finish = extract_fdots_nor_hq_finish, .data_type = MR_DATA_POLY_NOR, + .data_size = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; @@ -3238,9 +3242,10 @@ typedef struct MeshExtract_FdotUV_Data { int cd_ofs; } MeshExtract_FdotUV_Data; -static void *extract_fdots_uv_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_fdots_uv_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -3258,7 +3263,7 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride); } - MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__); + MeshExtract_FdotUV_Data *data = tls_data; data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo); if (mr->extract_type == MR_EXTRACT_BMESH) { @@ -3267,11 +3272,10 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, else { data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); } - return data; } static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -3308,20 +3312,12 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - const MeshExtract extract_fdots_uv = { .init = extract_fdots_uv_init, .iter_poly_bm = extract_fdots_uv_iter_poly_bm, .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, - .finish = extract_fdots_uv_finish, .data_type = 0, + .data_size = sizeof(MeshExtract_FdotUV_Data), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; @@ -3336,9 +3332,10 @@ typedef struct MeshExtract_EditUVFdotData_Data { int cd_ofs; } MeshExtract_EditUVFdotData_Data; -static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_fdots_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -3349,14 +3346,13 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); - MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__); + MeshExtract_EditUVFdotData_Data *data = tls_data; data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); - return data; } static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -3380,20 +3376,12 @@ static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - const MeshExtract extract_fdots_edituv_data = { .init = extract_fdots_edituv_data_init, .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, - .finish = extract_fdots_edituv_data_finish, .data_type = 0, + .data_size = sizeof(MeshExtract_EditUVFdotData_Data), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; @@ -3408,9 +3396,10 @@ typedef struct SkinRootData { float local_pos[3]; } SkinRootData; -static void *extract_skin_roots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_skin_roots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(tls_data)) { GPUVertBuf *vbo = buf; /* Exclusively for edit mode. */ @@ -3444,13 +3433,12 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */ GPU_vertbuf_data_len_set(vbo, root_len); - - return NULL; } const MeshExtract extract_skin_roots = { .init = extract_skin_roots_init, .data_type = 0, + .data_size = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; @@ -3460,9 +3448,10 @@ const MeshExtract extract_skin_roots = { /** \name Extract Selection Index * \{ */ -static void *extract_select_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_select_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -3472,7 +3461,7 @@ static void *extract_select_idx_init(const MeshRenderData *mr, } GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - return GPU_vertbuf_get_data(vbo); + *(uint32_t **)tls_data = GPU_vertbuf_get_data(vbo); } /* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the @@ -3481,7 +3470,7 @@ static void *extract_select_idx_init(const MeshRenderData *mr, * shader to output original index. */ static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int f_index, void *data) { @@ -3489,12 +3478,12 @@ static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = f_index; + (*(uint32_t **)data)[l_index] = f_index; } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *data) { @@ -3502,12 +3491,12 @@ static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); + (*(uint32_t **)data)[l_index] = BM_elem_index_get(l_iter->e); } while ((l_iter = l_iter->next) != l_first); } static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *data) { @@ -3515,36 +3504,36 @@ static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); + (*(uint32_t **)data)[l_index] = BM_elem_index_get(l_iter->v); } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, + const BMEdge *eed, const int ledge_index, void *data) { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); + (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); + (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); } static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, + const BMEdge *eed, const int ledge_index, void *data) { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); + (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); + (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); } static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, + const BMVert *eve, const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); + (*(uint32_t **)data)[offset + lvert_index] = BM_elem_index_get(eve); } static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, @@ -3554,7 +3543,7 @@ static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, { const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; + (*(uint32_t **)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; } } @@ -3567,7 +3556,7 @@ static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; + (*(uint32_t **)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; } } @@ -3580,7 +3569,7 @@ static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; + (*(uint32_t **)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; } } @@ -3591,8 +3580,8 @@ static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, { const int e_index = mr->ledges[ledge_index]; const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; + (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; + (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; } static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, @@ -3602,8 +3591,8 @@ static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, { int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; + (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; + (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; } static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, @@ -3615,7 +3604,7 @@ static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, const int v_index = mr->lverts[lvert_index]; const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; - ((uint32_t *)data)[offset + lvert_index] = v_orig; + (*(uint32_t **)data)[offset + lvert_index] = v_orig; } const MeshExtract extract_poly_idx = { @@ -3623,6 +3612,7 @@ const MeshExtract extract_poly_idx = { .iter_poly_bm = extract_poly_idx_iter_poly_bm, .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, .data_type = 0, + .data_size = sizeof(uint32_t *), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; @@ -3633,6 +3623,7 @@ const MeshExtract extract_edge_idx = { .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, .data_type = 0, + .data_size = sizeof(uint32_t *), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; @@ -3645,12 +3636,14 @@ const MeshExtract extract_vert_idx = { .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, .data_type = 0, + .data_size = sizeof(uint32_t *), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; -static void *extract_fdot_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void extract_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *tls_data) { GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; @@ -3661,15 +3654,15 @@ static void *extract_fdot_idx_init(const MeshRenderData *mr, GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); - return GPU_vertbuf_get_data(vbo); + *(uint32_t **)tls_data = GPU_vertbuf_get_data(vbo); } static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *UNUSED(f), + const BMFace *UNUSED(f), const int f_index, void *data) { - ((uint32_t *)data)[f_index] = f_index; + (*(uint32_t **)data)[f_index] = f_index; } static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, @@ -3678,10 +3671,10 @@ static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, void *data) { if (mr->p_origindex != NULL) { - ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; + (*(uint32_t **)data)[mp_index] = mr->p_origindex[mp_index]; } else { - ((uint32_t *)data)[mp_index] = mp_index; + (*(uint32_t **)data)[mp_index] = mp_index; } } @@ -3690,5 +3683,6 @@ const MeshExtract extract_fdot_idx = { .iter_poly_bm = extract_fdot_idx_iter_poly_bm, .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, .data_type = 0, + .data_size = sizeof(uint32_t *), .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 2eea53354f2..a258967564b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -38,6 +38,72 @@ extern "C" { #endif +#define MIN_RANGE_LEN 1024 + +/* ---------------------------------------------------------------------- */ +/** \name Dependencies between buffer and batch + * \{ */ +#ifndef NDEBUG +# define _MDEF_type(name) static DRWBatchFlag MDEP_assert_##name = 0, MDEP_##name +#else +# define _MDEF_type(name) static const DRWBatchFlag MDEP_##name +#endif + +/* clang-format off */ + +#define _MDEPS_CREATE1(b) (1u << MBC_BATCH_INDEX(b)) +#define _MDEPS_CREATE2(b1, b2) _MDEPS_CREATE1(b1) | _MDEPS_CREATE1(b2) +#define _MDEPS_CREATE3(b1, b2, b3) _MDEPS_CREATE2(b1, b2) | _MDEPS_CREATE1(b3) +#define _MDEPS_CREATE4(b1, b2, b3, b4) _MDEPS_CREATE3(b1, b2, b3) | _MDEPS_CREATE1(b4) +#define _MDEPS_CREATE5(b1, b2, b3, b4, b5) _MDEPS_CREATE4(b1, b2, b3, b4) | _MDEPS_CREATE1(b5) +#define _MDEPS_CREATE6(b1, b2, b3, b4, b5, b6) _MDEPS_CREATE5(b1, b2, b3, b4, b5) | _MDEPS_CREATE1(b6) +#define _MDEPS_CREATE7(b1, b2, b3, b4, b5, b6, b7) _MDEPS_CREATE6(b1, b2, b3, b4, b5, b6) | _MDEPS_CREATE1(b7) +#define _MDEPS_CREATE8(b1, b2, b3, b4, b5, b6, b7, b8) _MDEPS_CREATE7(b1, b2, b3, b4, b5, b6, b7) | _MDEPS_CREATE1(b8) +#define _MDEPS_CREATE9(b1, b2, b3, b4, b5, b6, b7, b8, b9) _MDEPS_CREATE8(b1, b2, b3, b4, b5, b6, b7, b8) | _MDEPS_CREATE1(b9) +#define _MDEPS_CREATE10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) _MDEPS_CREATE9(b1, b2, b3, b4, b5, b6, b7, b8, b9) | _MDEPS_CREATE1(b10) +#define _MDEPS_CREATE19(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19) _MDEPS_CREATE10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) | _MDEPS_CREATE9(b11, b12, b13, b14, b15, b16, b17, b18, b19) + +#define MDEPS_CREATE(name, ...) _MDEF_type(name) = VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE, __VA_ARGS__) + +#define _MDEPS_CREATE_MAP1(a) MDEP_##a +#define _MDEPS_CREATE_MAP2(a, b) MDEP_##a | MDEP_##b +#define _MDEPS_CREATE_MAP3(a, b, c) _MDEPS_CREATE_MAP2(a, b) | MDEP_##c +#define _MDEPS_CREATE_MAP4(a, b, c, d) _MDEPS_CREATE_MAP3(a, b, c) | MDEP_##d +#define _MDEPS_CREATE_MAP5(a, b, c, d, e) _MDEPS_CREATE_MAP4(a, b, c, d) | MDEP_##e +#define _MDEPS_CREATE_MAP6(a, b, c, d, e, f) _MDEPS_CREATE_MAP5(a, b, c, d, e) | MDEP_##f +#define _MDEPS_CREATE_MAP7(a, b, c, d, e, f, g) _MDEPS_CREATE_MAP6(a, b, c, d, e, f) | MDEP_##g +#define _MDEPS_CREATE_MAP8(a, b, c, d, e, f, g, h) _MDEPS_CREATE_MAP7(a, b, c, d, e, f, g) | MDEP_##h +#define _MDEPS_CREATE_MAP9(a, b, c, d, e, f, g, h, i) _MDEPS_CREATE_MAP8(a, b, c, d, e, f, g, h) | MDEP_##i +#define _MDEPS_CREATE_MAP10(a, b, c, d, e, f, g, h, i, j) _MDEPS_CREATE_MAP9(a, b, c, d, e, f, g, h, i) | MDEP_##j + +#define MDEPS_CREATE_MAP(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE_MAP, __VA_ARGS__) + +#ifndef NDEBUG +# define _MDEPS_ASSERT2(b, name) \ + MDEP_assert_##name |= _MDEPS_CREATE1(b); \ + BLI_assert(MDEP_##name & _MDEPS_CREATE1(b)) +# define _MDEPS_ASSERT3(b, n1, n2) _MDEPS_ASSERT2(b, n1); _MDEPS_ASSERT2(b, n2) +# define _MDEPS_ASSERT4(b, n1, n2, n3) _MDEPS_ASSERT3(b, n1, n2); _MDEPS_ASSERT2(b, n3) +# define _MDEPS_ASSERT5(b, n1, n2, n3, n4) _MDEPS_ASSERT4(b, n1, n2, n3); _MDEPS_ASSERT2(b, n4) +# define _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5) _MDEPS_ASSERT5(b, n1, n2, n3, n4); _MDEPS_ASSERT2(b, n5) +# define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6) +# define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7) + +# define MDEPS_ASSERT(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) +# define MDEPS_ASSERT_MAP(name) BLI_assert(MDEP_assert_##name == MDEP_##name) +#else +# define MDEPS_ASSERT(...) +# define MDEPS_ASSERT_MAP(name) UNUSED_VARS(MDEP_##name) +#endif + +/* clang-format on */ + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Render Data + * \{ */ + typedef enum eMRExtractType { MR_EXTRACT_BMESH, MR_EXTRACT_MAPPED, @@ -94,6 +160,10 @@ typedef struct MeshRenderData { float (*loop_normals)[3]; float (*poly_normals)[3]; int *lverts, *ledges; + struct { + int *tri; + int visible_tri_len; + } mat_offsets; } MeshRenderData; BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) @@ -150,278 +220,57 @@ BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *e return efa->no; } -/* TODO(jbakker): phase out batch iteration macros as they are only used once. */ +/** \} */ + /* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loop Triangles +/** \name Mesh Elements Extract Struct * \{ */ - -typedef struct ExtractTriBMesh_Params { - BMLoop *(*looptris)[3]; - int tri_range[2]; -} ExtractTriBMesh_Params; +/* TODO(jbakker): move parameters inside a struct. */ typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, BMLoop **elt, const int elt_index, void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 3) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } - -typedef struct ExtractTriMesh_Params { - const MLoopTri *mlooptri; - int tri_range[2]; -} ExtractTriMesh_Params; typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, const MLoopTri *mlt, const int elt_index, void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 1) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Polygons, Loops - * \{ */ - -typedef struct ExtractPolyBMesh_Params { - BMLoop *(*looptris)[3]; - int poly_range[2]; -} ExtractPolyBMesh_Params; typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, - BMFace *f, + const BMFace *f, const int f_index, void *data); - -#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_poly = _ftable[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_BM_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_face = _ftable[index_poly]; \ - BMLoop *elem_loop, *l_first; \ - elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ - do { \ - const int index_loop = BM_elem_index_get(elem_loop); \ - (void)index_loop; /* Quiet warning when unused. */ - -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ - } \ - while ((elem_loop = elem_loop->next) != l_first) \ - ; \ - } \ - } - -typedef struct ExtractPolyMesh_Params { - int poly_range[2]; -} ExtractPolyMesh_Params; typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, const MPoly *mp, const int mp_index, void *data); - -#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_MESH_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ - elem_poly, index_poly, elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const MLoop *_mloop = mr->mloop; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ - for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ - const MLoop *elem_loop = &_mloop[index_loop]; \ - (void)elem_loop; - -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Edges - * \{ */ - -typedef struct ExtractLEdgeBMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeBMesh_Params; typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, - BMEdge *eed, + const BMEdge *eed, const int ledge_index, void *data); - -#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ - CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ - BMEdge **_etable = mr->bm->etable; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLEdgeMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeMesh_Params; typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, const MEdge *med, const int ledge_index, void *data); - -#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ - CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ - { \ - const MEdge *_medge = mr->medge; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Vertices - * \{ */ - -typedef struct ExtractLVertBMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertBMesh_Params; typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, - BMVert *eve, + const BMVert *eve, const int lvert_index, void *data); - -#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ - CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMVert **vtable = mr->bm->vtable; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - BMVert *elem_vert = vtable[lverts[index_lvert]]; \ - (void)elem_vert; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLVertMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertMesh_Params; typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, const MVert *mv, const int lvert_index, void *data); - -#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ - CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ - { \ - const MVert *mvert = mr->mvert; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - const MVert *elem = &mvert[lverts[index_lvert]]; \ - (void)elem; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract Struct - * \{ */ -/* TODO(jbakker): move parameters inside a struct. */ -typedef void *(ExtractInitFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer); +typedef void(ExtractInitFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *r_data); typedef void(ExtractFinishFn)(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buffer, void *data); -typedef void *(ExtractTaskInitFn)(void *userdata); -typedef void(ExtractTaskFinishFn)(void *userdata, void *task_userdata); +typedef void(ExtractTaskReduceFn)(void *userdata, void *task_userdata); typedef struct MeshExtract { /** Executed on main thread and return user data for iteration functions. */ ExtractInitFn *init; - /** Task local data. */ - ExtractTaskInitFn *task_init; /** Executed on one (or more if use_threading) worker thread(s). */ ExtractTriBMeshFn *iter_looptri_bm; ExtractTriMeshFn *iter_looptri_mesh; @@ -432,10 +281,11 @@ typedef struct MeshExtract { ExtractLVertBMeshFn *iter_lvert_bm; ExtractLVertMeshFn *iter_lvert_mesh; /** Executed on one worker thread after all elements iterations. */ - ExtractTaskFinishFn *task_finish; + ExtractTaskReduceFn *task_reduce; ExtractFinishFn *finish; /** Used to request common data. */ eMRDataType data_type; + size_t data_size; /** Used to know if the element callbacks are thread-safe and can be parallelized. */ bool use_threading; /** @@ -460,6 +310,9 @@ MeshRenderData *mesh_render_data_create(Mesh *me, const eMRIterType iter_type); void mesh_render_data_free(MeshRenderData *mr); void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag); +void mesh_render_data_update_mat_offsets(MeshRenderData *mr, + MeshBufferExtractionCache *cache, + const eMRDataType data_flag); void mesh_render_data_update_looptris(MeshRenderData *mr, const eMRIterType iter_type, const eMRDataType data_flag); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c index 4741bcb06c9..bccf894cc53 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -27,6 +27,7 @@ #include "BLI_bitmap.h" #include "BLI_math.h" +#include "BLI_task.h" #include "BKE_editmesh.h" #include "BKE_editmesh_cache.h" @@ -39,15 +40,26 @@ #include "draw_cache_extract_mesh_private.h" /* ---------------------------------------------------------------------- */ -/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). +/** \name Update Loose Geometry * \{ */ +static void mesh_render_data_lverts_bm(const MeshRenderData *mr, + MeshBufferExtractionCache *cache, + BMesh *bm); +static void mesh_render_data_ledges_bm(const MeshRenderData *mr, + MeshBufferExtractionCache *cache, + BMesh *bm); +static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, + MeshBufferExtractionCache *cache); +static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, + MeshBufferExtractionCache *cache); + static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) { - mr->ledges = cache->ledges; - mr->lverts = cache->lverts; - mr->vert_loose_len = cache->vert_loose_len; - mr->edge_loose_len = cache->edge_loose_len; + mr->ledges = cache->loose_geom.edges; + mr->lverts = cache->loose_geom.verts; + mr->vert_loose_len = cache->loose_geom.vert_len; + mr->edge_loose_len = cache->loose_geom.edge_len; mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); } @@ -55,76 +67,276 @@ static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtra static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, MeshBufferExtractionCache *cache) { - /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges - * and verts are calculated at the same time.*/ - if (cache->lverts) { + /* Early exit: Are loose geometry already available. + * Only checking for loose verts as loose edges and verts are calculated at the same time. */ + if (cache->loose_geom.verts) { return; } + mesh_render_data_loose_geom_build(mr, cache); +} - cache->vert_loose_len = 0; - cache->edge_loose_len = 0; +static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + cache->loose_geom.vert_len = 0; + cache->loose_geom.edge_len = 0; if (mr->extract_type != MR_EXTRACT_BMESH) { /* Mesh */ + mesh_render_data_loose_geom_mesh(mr, cache); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + mesh_render_data_lverts_bm(mr, cache, bm); + mesh_render_data_ledges_bm(mr, cache, bm); + } +} - BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); +static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); - cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); - const MEdge *med = mr->medge; - for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { - if (med->flag & ME_LOOSEEDGE) { - cache->ledges[cache->edge_loose_len++] = med_index; - } - /* Tag verts as not loose. */ - BLI_BITMAP_ENABLE(lvert_map, med->v1); - BLI_BITMAP_ENABLE(lvert_map, med->v2); + cache->loose_geom.edges = MEM_mallocN(mr->edge_len * sizeof(*cache->loose_geom.edges), __func__); + const MEdge *med = mr->medge; + for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { + if (med->flag & ME_LOOSEEDGE) { + cache->loose_geom.edges[cache->loose_geom.edge_len++] = med_index; } - if (cache->edge_loose_len < mr->edge_len) { - cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + /* Tag verts as not loose. */ + BLI_BITMAP_ENABLE(lvert_map, med->v1); + BLI_BITMAP_ENABLE(lvert_map, med->v2); + } + if (cache->loose_geom.edge_len < mr->edge_len) { + cache->loose_geom.edges = MEM_reallocN( + cache->loose_geom.edges, cache->loose_geom.edge_len * sizeof(*cache->loose_geom.edges)); + } + + cache->loose_geom.verts = MEM_mallocN(mr->vert_len * sizeof(*cache->loose_geom.verts), __func__); + for (int v = 0; v < mr->vert_len; v++) { + if (!BLI_BITMAP_TEST(lvert_map, v)) { + cache->loose_geom.verts[cache->loose_geom.vert_len++] = v; } + } + if (cache->loose_geom.vert_len < mr->vert_len) { + cache->loose_geom.verts = MEM_reallocN( + cache->loose_geom.verts, cache->loose_geom.vert_len * sizeof(*cache->loose_geom.verts)); + } - cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - for (int v = 0; v < mr->vert_len; v++) { - if (!BLI_BITMAP_TEST(lvert_map, v)) { - cache->lverts[cache->vert_loose_len++] = v; - } + MEM_freeN(lvert_map); +} + +static void mesh_render_data_lverts_bm(const MeshRenderData *mr, + MeshBufferExtractionCache *cache, + BMesh *bm) +{ + int elem_id; + BMIter iter; + BMVert *eve; + cache->loose_geom.verts = MEM_mallocN(mr->vert_len * sizeof(*cache->loose_geom.verts), __func__); + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { + if (eve->e == NULL) { + cache->loose_geom.verts[cache->loose_geom.vert_len++] = elem_id; } - if (cache->vert_loose_len < mr->vert_len) { - cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + if (cache->loose_geom.vert_len < mr->vert_len) { + cache->loose_geom.verts = MEM_reallocN( + cache->loose_geom.verts, cache->loose_geom.vert_len * sizeof(*cache->loose_geom.verts)); + } +} + +static void mesh_render_data_ledges_bm(const MeshRenderData *mr, + MeshBufferExtractionCache *cache, + BMesh *bm) +{ + int elem_id; + BMIter iter; + BMEdge *ede; + cache->loose_geom.edges = MEM_mallocN(mr->edge_len * sizeof(*cache->loose_geom.edges), __func__); + BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { + if (ede->l == NULL) { + cache->loose_geom.edges[cache->loose_geom.edge_len++] = elem_id; } + } + if (cache->loose_geom.edge_len < mr->edge_len) { + cache->loose_geom.edges = MEM_reallocN( + cache->loose_geom.edges, cache->loose_geom.edge_len * sizeof(*cache->loose_geom.edges)); + } +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Material Offsets + * + * Material offsets contains the offset of a material after sorting tris based on their material. + * + * \{ */ +static void mesh_render_data_mat_offset_load(MeshRenderData *mr, + const MeshBufferExtractionCache *cache); +static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr, + MeshBufferExtractionCache *cache); +static void mesh_render_data_mat_offset_build(MeshRenderData *mr, + MeshBufferExtractionCache *cache); +static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr, + MeshBufferExtractionCache *cache); +static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr, + MeshBufferExtractionCache *cache); +static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr, + MeshBufferExtractionCache *cache); + +void mesh_render_data_update_mat_offsets(MeshRenderData *mr, + MeshBufferExtractionCache *cache, + const eMRDataType data_flag) +{ + if (data_flag & MR_DATA_MAT_OFFSETS) { + mesh_render_data_mat_offset_ensure(mr, cache); + mesh_render_data_mat_offset_load(mr, cache); + } +} + +static void mesh_render_data_mat_offset_load(MeshRenderData *mr, + const MeshBufferExtractionCache *cache) +{ + mr->mat_offsets.tri = cache->mat_offsets.tri; + mr->mat_offsets.visible_tri_len = cache->mat_offsets.visible_tri_len; +} + +static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + if (cache->mat_offsets.tri) { + return; + } + mesh_render_data_mat_offset_build(mr, cache); +} + +static void mesh_render_data_mat_offset_build(MeshRenderData *mr, MeshBufferExtractionCache *cache) +{ + size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; + cache->mat_offsets.tri = MEM_callocN(mat_tri_idx_size, __func__); - MEM_freeN(lvert_map); + /* Count how many triangles for each material. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + mesh_render_data_mat_offset_build_bm(mr, cache); } else { - /* #BMesh */ - BMesh *bm = mr->bm; - int elem_id; - BMIter iter; - BMVert *eve; - BMEdge *ede; - - cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { - if (eve->e == NULL) { - cache->lverts[cache->vert_loose_len++] = elem_id; - } - } - if (cache->vert_loose_len < mr->vert_len) { - cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); - } + mesh_render_data_mat_offset_build_mesh(mr, cache); + } - cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); - BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { - if (ede->l == NULL) { - cache->ledges[cache->edge_loose_len++] = elem_id; - } - } - if (cache->edge_loose_len < mr->edge_len) { - cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); - } + mesh_render_data_mat_offset_apply_offset(mr, cache); +} + +typedef struct MatOffsetUserData { + MeshRenderData *mr; + /** This struct is extended during allocation to hold mat_tri_len for each material. */ + int mat_tri_len[0]; +} MatOffsetUserData; + +static void mesh_render_data_mat_offset_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + MatOffsetUserData *dst = chunk_join; + MatOffsetUserData *src = chunk; + int *dst_mat_len = dst->mat_tri_len; + int *src_mat_len = src->mat_tri_len; + for (int i = 0; i < dst->mr->mat_len; i++) { + dst_mat_len[i] += src_mat_len[i]; } } +static void mesh_render_data_mat_offset_build_threaded(MeshRenderData *mr, + MeshBufferExtractionCache *cache, + int face_len, + TaskParallelRangeFunc range_func) +{ + /* Extending the #MatOffsetUserData with an int per material slot. */ + size_t userdata_size = sizeof(MatOffsetUserData) + + (mr->mat_len) * sizeof(*cache->mat_offsets.tri); + MatOffsetUserData *userdata = MEM_callocN(userdata_size, __func__); + userdata->mr = mr; + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.userdata_chunk = userdata; + settings.userdata_chunk_size = userdata_size; + settings.min_iter_per_thread = MIN_RANGE_LEN; + settings.func_reduce = mesh_render_data_mat_offset_reduce; + BLI_task_parallel_range(0, face_len, NULL, range_func, &settings); + + memcpy(cache->mat_offsets.tri, + &userdata->mat_tri_len, + (mr->mat_len) * sizeof(*cache->mat_offsets.tri)); + MEM_freeN(userdata); +} + +static void mesh_render_data_mat_offset_bm_range(void *__restrict UNUSED(userdata), + const int iter, + const TaskParallelTLS *__restrict tls) +{ + MatOffsetUserData *mat_offset_userdata = tls->userdata_chunk; + MeshRenderData *mr = mat_offset_userdata->mr; + int *mat_tri_len = mat_offset_userdata->mat_tri_len; + + BMesh *bm = mr->bm; + BMFace *efa = BM_face_at_index(bm, iter); + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + int mat = min_ii(efa->mat_nr, mr->mat_len - 1); + mat_tri_len[mat] += efa->len - 2; + } +} + +static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + BMesh *bm = mr->bm; + mesh_render_data_mat_offset_build_threaded( + mr, cache, bm->totface, mesh_render_data_mat_offset_bm_range); +} + +static void mesh_render_data_mat_offset_mesh_range(void *__restrict UNUSED(userdata), + const int iter, + const TaskParallelTLS *__restrict tls) +{ + MatOffsetUserData *mat_offset_userdata = tls->userdata_chunk; + const MeshRenderData *mr = mat_offset_userdata->mr; + int *mat_tri_len = mat_offset_userdata->mat_tri_len; + + const MPoly *mp = &mr->mpoly[iter]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int mat = min_ii(mp->mat_nr, mr->mat_len - 1); + mat_tri_len[mat] += mp->totloop - 2; + } +} + +static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + mesh_render_data_mat_offset_build_threaded( + mr, cache, mr->poly_len, mesh_render_data_mat_offset_mesh_range); +} + +static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + int *mat_tri_len = cache->mat_offsets.tri; + int ofs = mat_tri_len[0]; + mat_tri_len[0] = 0; + for (int i = 1; i < mr->mat_len; i++) { + int tmp = mat_tri_len[i]; + mat_tri_len[i] = ofs; + ofs += tmp; + } + cache->mat_offsets.visible_tri_len = ofs; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). + * \{ */ + /** * Part of the creation of the #MeshRenderData that happens in a thread. */ @@ -136,9 +348,23 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, if (mr->extract_type != MR_EXTRACT_BMESH) { /* Mesh */ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + /* NOTE(campbell): It's possible to skip allocating tessellation, + * the tessellation can be calculated as part of the iterator, see: P2188. + * The overall advantage is small (around 1%), so keep this as-is. */ mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); - BKE_mesh_recalc_looptri( - me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); + if (mr->poly_normals != NULL) { + BKE_mesh_recalc_looptri_with_normals(me->mloop, + me->mpoly, + me->mvert, + me->totloop, + me->totpoly, + mr->mlooptri, + mr->poly_normals); + } + else { + BKE_mesh_recalc_looptri( + me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); + } } } else { @@ -159,16 +385,8 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_ if (mr->extract_type != MR_EXTRACT_BMESH) { /* Mesh */ if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { - mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); - BKE_mesh_calc_normals_poly((MVert *)mr->mvert, - NULL, - mr->vert_len, - mr->mloop, - mr->mpoly, - mr->loop_len, - mr->poly_len, - mr->poly_normals, - true); + BKE_mesh_ensure_normals_for_display(mr->me); + mr->poly_normals = CustomData_get_layer(&mr->me->pdata, CD_NORMAL); } if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); @@ -358,14 +576,13 @@ MeshRenderData *mesh_render_data_create(Mesh *me, void mesh_render_data_free(MeshRenderData *mr) { MEM_SAFE_FREE(mr->mlooptri); - MEM_SAFE_FREE(mr->poly_normals); MEM_SAFE_FREE(mr->loop_normals); - /* Loose geometry are owned by MeshBufferExtractionCache. */ + /* Loose geometry are owned by #MeshBufferExtractionCache. */ mr->ledges = NULL; mr->lverts = NULL; MEM_freeN(mr); } -/** \} */
\ No newline at end of file +/** \} */ diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 3cc71e47f28..3b70490509d 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -67,12 +67,109 @@ #include "ED_uvedit.h" #include "draw_cache_extract.h" +#include "draw_cache_extract_mesh_private.h" #include "draw_cache_inline.h" #include "draw_cache_impl.h" /* own include */ +MDEPS_CREATE(vbo_lnor, batch.surface, batch.wire_loops, batch.edit_lnor, surface_per_mat); +MDEPS_CREATE(vbo_pos_nor, + batch.surface, + batch.surface_weights, + batch.all_verts, + batch.all_edges, + batch.loose_edges, + batch.edge_detection, + batch.wire_loops, + batch.wire_edges, + batch.edit_vnor, + batch.edit_lnor, + batch.edit_vertices, + batch.edit_edges, + batch.edit_triangles, + batch.edit_selection_verts, + batch.edit_selection_edges, + batch.edit_selection_faces, + batch.edit_mesh_analysis, + batch.sculpt_overlays, + surface_per_mat); +MDEPS_CREATE(vbo_uv, + batch.surface, + batch.wire_loops_uvs, + batch.edituv_faces, + batch.edituv_faces_stretch_area, + batch.edituv_faces_stretch_angle, + batch.edituv_edges, + batch.edituv_verts, + surface_per_mat); +MDEPS_CREATE(vbo_vcol, batch.surface, surface_per_mat); +MDEPS_CREATE(vbo_sculpt_data, batch.sculpt_overlays); +MDEPS_CREATE(vbo_weights, batch.surface_weights); +MDEPS_CREATE(vbo_edge_fac, batch.wire_edges); +MDEPS_CREATE(vbo_mesh_analysis, batch.edit_mesh_analysis); +MDEPS_CREATE(vbo_tan, surface_per_mat); +MDEPS_CREATE(vbo_orco, surface_per_mat); +MDEPS_CREATE(vbo_edit_data, batch.edit_triangles, batch.edit_edges, batch.edit_vertices); +MDEPS_CREATE(vbo_fdots_pos, batch.edit_fdots, batch.edit_selection_fdots); +MDEPS_CREATE(vbo_fdots_nor, batch.edit_fdots); +MDEPS_CREATE(vbo_skin_roots, batch.edit_skin_roots); +MDEPS_CREATE(vbo_vert_idx, batch.edit_selection_verts); +MDEPS_CREATE(vbo_edge_idx, batch.edit_selection_edges); +MDEPS_CREATE(vbo_poly_idx, batch.edit_selection_faces); +MDEPS_CREATE(vbo_fdot_idx, batch.edit_selection_fdots); +MDEPS_CREATE(vbo_edituv_data, + batch.edituv_faces, + batch.edituv_faces_stretch_area, + batch.edituv_faces_stretch_angle, + batch.edituv_edges, + batch.edituv_verts); +MDEPS_CREATE(vbo_edituv_stretch_area, batch.edituv_faces_stretch_area); +MDEPS_CREATE(vbo_edituv_stretch_angle, batch.edituv_faces_stretch_angle); +MDEPS_CREATE(vbo_fdots_uv, batch.edituv_fdots); +MDEPS_CREATE(vbo_fdots_edituv_data, batch.edituv_fdots); + +MDEPS_CREATE(ibo_tris, + batch.surface, + batch.sculpt_overlays, + batch.surface_weights, + batch.edit_mesh_analysis, + batch.edit_triangles, + batch.edit_lnor, + batch.edit_selection_faces); +MDEPS_CREATE( + ibo_lines, batch.all_edges, batch.wire_edges, batch.edit_edges, batch.edit_selection_edges); +MDEPS_CREATE(ibo_lines_loose, batch.loose_edges); +MDEPS_CREATE(ibo_lines_adjacency, batch.edge_detection); +MDEPS_CREATE(ibo_lines_paint_mask, batch.wire_loops); +MDEPS_CREATE(ibo_tris_per_mat, surface_per_mat); +MDEPS_CREATE(ibo_points, batch.edit_vnor, batch.edit_selection_verts, batch.edit_vertices); +MDEPS_CREATE(ibo_fdots, batch.edit_fdots, batch.edit_selection_fdots); +MDEPS_CREATE(ibo_edituv_tris, + batch.edituv_faces, + batch.edituv_faces_stretch_area, + batch.edituv_faces_stretch_angle); +MDEPS_CREATE(ibo_edituv_lines, batch.edituv_edges, batch.wire_loops_uvs); +MDEPS_CREATE(ibo_edituv_points, batch.edituv_verts); +MDEPS_CREATE(ibo_edituv_fdots, batch.edituv_fdots); + +static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache); static void mesh_batch_cache_clear(Mesh *me); +static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatchFlag batch_map) +{ + for (int i = 0; i < MBC_BATCH_LEN; i++) { + DRWBatchFlag batch_requested = (1u << i); + if (batch_map & batch_requested) { + GPU_BATCH_DISCARD_SAFE(((GPUBatch **)&cache->batch)[i]); + cache->batch_ready &= ~batch_requested; + } + } + + if (batch_map & (1u << MBC_BATCH_INDEX(surface_per_mat))) { + mesh_batch_cache_discard_surface_batches(cache); + } +} + /* Return true is all layers in _b_ are inside _a_. */ BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b) { @@ -562,14 +659,8 @@ static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache) GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol); GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.orco); } - /* Discard batches using vbo.uv. */ - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_area); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_angle); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts); - - mesh_batch_cache_discard_surface_batches(cache); + DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo_uv, vbo_tan, vbo_vcol, vbo_orco); + mesh_batch_cache_discard_batch(cache, batch_map); mesh_cd_layers_type_clear(&cache->cd_used); } @@ -587,13 +678,17 @@ static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache) GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_points); GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_fdots); } - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_area); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_angle); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_fdots); - GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops_uvs); + DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo_edituv_stretch_angle, + vbo_edituv_stretch_area, + vbo_uv, + vbo_edituv_data, + vbo_fdots_uv, + vbo_fdots_edituv_data, + ibo_edituv_tris, + ibo_edituv_lines, + ibo_edituv_points, + ibo_edituv_fdots); + mesh_batch_cache_discard_batch(cache, batch_map); cache->tot_area = 0.0f; cache->tot_uv_area = 0.0f; @@ -603,9 +698,6 @@ static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache) /* We discarded the vbo.uv so we need to reset the cd_used flag. */ cache->cd_used.uv = 0; cache->cd_used.edit_uv = 0; - - /* Discard other batches that uses vbo.uv */ - mesh_batch_cache_discard_surface_batches(cache); } static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache *cache) @@ -618,14 +710,13 @@ static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache *cache) GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_points); GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_fdots); } - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_area); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_angle); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_fdots); - GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops_uvs); - cache->batch_ready &= ~MBC_EDITUV; + DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo_edituv_data, + vbo_fdots_edituv_data, + ibo_edituv_tris, + ibo_edituv_lines, + ibo_edituv_points, + ibo_edituv_fdots); + mesh_batch_cache_discard_batch(cache, batch_map); } void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) @@ -634,25 +725,16 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) if (cache == NULL) { return; } + DRWBatchFlag batch_map; switch (mode) { case BKE_MESH_BATCH_DIRTY_SELECT: FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edit_data); GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_nor); } - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_triangles); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_vertices); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_fdots); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_verts); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_faces); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_fdots); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_mesh_analysis); - cache->batch_ready &= ~(MBC_EDIT_TRIANGLES | MBC_EDIT_VERTICES | MBC_EDIT_EDGES | - MBC_EDIT_FACEDOTS | MBC_EDIT_SELECTION_FACEDOTS | - MBC_EDIT_SELECTION_FACES | MBC_EDIT_SELECTION_EDGES | - MBC_EDIT_SELECTION_VERTS | MBC_EDIT_MESH_ANALYSIS); + batch_map = MDEPS_CREATE_MAP(vbo_edit_data, vbo_fdots_nor); + mesh_batch_cache_discard_batch(cache, batch_map); + /* Because visible UVs depends on edit mode selection, discard topology. */ mesh_batch_cache_discard_uvedit_select(cache); break; @@ -664,20 +746,8 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor); GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.lnor); } - GPU_BATCH_DISCARD_SAFE(cache->batch.surface); - /* Discard batches using vbo.pos_nor. */ - GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops); - GPU_BATCH_DISCARD_SAFE(cache->batch.wire_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.all_verts); - GPU_BATCH_DISCARD_SAFE(cache->batch.all_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.loose_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.edge_detection); - GPU_BATCH_DISCARD_SAFE(cache->batch.surface_weights); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_mesh_analysis); - /* Discard batches using vbo.lnor. */ - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_lnor); - mesh_batch_cache_discard_surface_batches(cache); - cache->batch_ready &= ~(MBC_SURFACE | MBC_WIRE_EDGES | MBC_WIRE_LOOPS); + batch_map = MDEPS_CREATE_MAP(ibo_lines_paint_mask, vbo_pos_nor, vbo_lnor); + mesh_batch_cache_discard_batch(cache, batch_map); break; case BKE_MESH_BATCH_DIRTY_ALL: cache->is_dirty = true; @@ -694,13 +764,8 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data); GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data); } - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_area); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_angle); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_fdots); - cache->batch_ready &= ~MBC_EDITUV; + batch_map = MDEPS_CREATE_MAP(vbo_edituv_data, vbo_fdots_edituv_data); + mesh_batch_cache_discard_batch(cache, batch_map); break; default: BLI_assert(0); @@ -721,10 +786,12 @@ static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache) static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache) { - MEM_SAFE_FREE(extraction_cache->lverts); - MEM_SAFE_FREE(extraction_cache->ledges); - extraction_cache->edge_loose_len = 0; - extraction_cache->vert_loose_len = 0; + MEM_SAFE_FREE(extraction_cache->loose_geom.verts); + MEM_SAFE_FREE(extraction_cache->loose_geom.edges); + extraction_cache->loose_geom.edge_len = 0; + extraction_cache->loose_geom.vert_len = 0; + + MEM_SAFE_FREE(extraction_cache->mat_offsets.tri); } static void mesh_batch_cache_clear(Mesh *me) @@ -1232,7 +1299,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, /* Don't check `DRW_object_is_in_edit_mode(ob)` here because it means the same mesh * may draw with edit-mesh data and regular mesh data. - * In this case the custom-data layers used wont always match in `me->runtime.batch_cache`. + * In this case the custom-data layers used won't always match in `me->runtime.batch_cache`. * If we want to display regular mesh data, we should have a separate cache for the edit-mesh. * See T77359. */ const bool is_editmode = (me->edit_mesh != NULL) && @@ -1370,6 +1437,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MeshBufferCache *mbufcache = &cache->final; /* Initialize batches and request VBO's & IBO's. */ + MDEPS_ASSERT(batch.surface, ibo_tris, vbo_lnor, vbo_pos_nor, vbo_uv, vbo_vcol); if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.surface, &mbufcache->ibo.tris); /* Order matters. First ones override latest VBO's attributes. */ @@ -1382,43 +1450,52 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.vcol); } } + MDEPS_ASSERT(batch.all_verts, vbo_pos_nor); if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) { DRW_vbo_request(cache->batch.all_verts, &mbufcache->vbo.pos_nor); } + MDEPS_ASSERT(batch.sculpt_overlays, ibo_tris, vbo_pos_nor, vbo_sculpt_data); if (DRW_batch_requested(cache->batch.sculpt_overlays, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.sculpt_overlays, &mbufcache->ibo.tris); DRW_vbo_request(cache->batch.sculpt_overlays, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.sculpt_overlays, &mbufcache->vbo.sculpt_data); } + MDEPS_ASSERT(batch.all_edges, ibo_lines, vbo_pos_nor); if (DRW_batch_requested(cache->batch.all_edges, GPU_PRIM_LINES)) { DRW_ibo_request(cache->batch.all_edges, &mbufcache->ibo.lines); DRW_vbo_request(cache->batch.all_edges, &mbufcache->vbo.pos_nor); } + MDEPS_ASSERT(batch.loose_edges, ibo_lines_loose, vbo_pos_nor); if (DRW_batch_requested(cache->batch.loose_edges, GPU_PRIM_LINES)) { DRW_ibo_request(NULL, &mbufcache->ibo.lines); DRW_ibo_request(cache->batch.loose_edges, &mbufcache->ibo.lines_loose); DRW_vbo_request(cache->batch.loose_edges, &mbufcache->vbo.pos_nor); } + MDEPS_ASSERT(batch.edge_detection, ibo_lines_adjacency, vbo_pos_nor); if (DRW_batch_requested(cache->batch.edge_detection, GPU_PRIM_LINES_ADJ)) { DRW_ibo_request(cache->batch.edge_detection, &mbufcache->ibo.lines_adjacency); DRW_vbo_request(cache->batch.edge_detection, &mbufcache->vbo.pos_nor); } + MDEPS_ASSERT(batch.surface_weights, ibo_tris, vbo_pos_nor, vbo_weights); if (DRW_batch_requested(cache->batch.surface_weights, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.surface_weights, &mbufcache->ibo.tris); DRW_vbo_request(cache->batch.surface_weights, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.surface_weights, &mbufcache->vbo.weights); } + MDEPS_ASSERT(batch.wire_loops, ibo_lines_paint_mask, vbo_lnor, vbo_pos_nor); if (DRW_batch_requested(cache->batch.wire_loops, GPU_PRIM_LINES)) { DRW_ibo_request(cache->batch.wire_loops, &mbufcache->ibo.lines_paint_mask); /* Order matters. First ones override latest VBO's attributes. */ DRW_vbo_request(cache->batch.wire_loops, &mbufcache->vbo.lnor); DRW_vbo_request(cache->batch.wire_loops, &mbufcache->vbo.pos_nor); } + MDEPS_ASSERT(batch.wire_edges, ibo_lines, vbo_pos_nor, vbo_edge_fac); if (DRW_batch_requested(cache->batch.wire_edges, GPU_PRIM_LINES)) { DRW_ibo_request(cache->batch.wire_edges, &mbufcache->ibo.lines); DRW_vbo_request(cache->batch.wire_edges, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.wire_edges, &mbufcache->vbo.edge_fac); } + MDEPS_ASSERT(batch.wire_loops_uvs, ibo_edituv_lines, vbo_uv); if (DRW_batch_requested(cache->batch.wire_loops_uvs, GPU_PRIM_LINES)) { DRW_ibo_request(cache->batch.wire_loops_uvs, &mbufcache->ibo.edituv_lines); /* For paint overlay. Active layer should have been queried. */ @@ -1426,6 +1503,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, DRW_vbo_request(cache->batch.wire_loops_uvs, &mbufcache->vbo.uv); } } + MDEPS_ASSERT(batch.edit_mesh_analysis, ibo_tris, vbo_pos_nor, vbo_mesh_analysis); if (DRW_batch_requested(cache->batch.edit_mesh_analysis, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.edit_mesh_analysis, &mbufcache->ibo.tris); DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbufcache->vbo.pos_nor); @@ -1433,6 +1511,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } /* Per Material */ + MDEPS_ASSERT(surface_per_mat, + ibo_tris_per_mat, + vbo_lnor, + vbo_pos_nor, + vbo_uv, + vbo_tan, + vbo_vcol, + vbo_orco); for (int i = 0; i < cache->mat_len; i++) { if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { DRW_ibo_request(cache->surface_per_mat[i], &mbufcache->tris_per_mat[i]); @@ -1457,55 +1543,66 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mbufcache = (do_cage) ? &cache->cage : &cache->final; /* Edit Mesh */ + MDEPS_ASSERT(batch.edit_triangles, ibo_tris, vbo_pos_nor, vbo_edit_data); if (DRW_batch_requested(cache->batch.edit_triangles, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.edit_triangles, &mbufcache->ibo.tris); DRW_vbo_request(cache->batch.edit_triangles, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.edit_triangles, &mbufcache->vbo.edit_data); } + MDEPS_ASSERT(batch.edit_vertices, ibo_points, vbo_pos_nor, vbo_edit_data); if (DRW_batch_requested(cache->batch.edit_vertices, GPU_PRIM_POINTS)) { DRW_ibo_request(cache->batch.edit_vertices, &mbufcache->ibo.points); DRW_vbo_request(cache->batch.edit_vertices, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.edit_vertices, &mbufcache->vbo.edit_data); } + MDEPS_ASSERT(batch.edit_edges, ibo_lines, vbo_pos_nor, vbo_edit_data); if (DRW_batch_requested(cache->batch.edit_edges, GPU_PRIM_LINES)) { DRW_ibo_request(cache->batch.edit_edges, &mbufcache->ibo.lines); DRW_vbo_request(cache->batch.edit_edges, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.edit_edges, &mbufcache->vbo.edit_data); } + MDEPS_ASSERT(batch.edit_vnor, ibo_points, vbo_pos_nor); if (DRW_batch_requested(cache->batch.edit_vnor, GPU_PRIM_POINTS)) { DRW_ibo_request(cache->batch.edit_vnor, &mbufcache->ibo.points); DRW_vbo_request(cache->batch.edit_vnor, &mbufcache->vbo.pos_nor); } + MDEPS_ASSERT(batch.edit_lnor, ibo_tris, vbo_pos_nor, vbo_lnor); if (DRW_batch_requested(cache->batch.edit_lnor, GPU_PRIM_POINTS)) { DRW_ibo_request(cache->batch.edit_lnor, &mbufcache->ibo.tris); DRW_vbo_request(cache->batch.edit_lnor, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.edit_lnor, &mbufcache->vbo.lnor); } + MDEPS_ASSERT(batch.edit_fdots, ibo_fdots, vbo_fdots_pos, vbo_fdots_nor); if (DRW_batch_requested(cache->batch.edit_fdots, GPU_PRIM_POINTS)) { DRW_ibo_request(cache->batch.edit_fdots, &mbufcache->ibo.fdots); DRW_vbo_request(cache->batch.edit_fdots, &mbufcache->vbo.fdots_pos); DRW_vbo_request(cache->batch.edit_fdots, &mbufcache->vbo.fdots_nor); } + MDEPS_ASSERT(batch.edit_skin_roots, vbo_skin_roots); if (DRW_batch_requested(cache->batch.edit_skin_roots, GPU_PRIM_POINTS)) { DRW_vbo_request(cache->batch.edit_skin_roots, &mbufcache->vbo.skin_roots); } /* Selection */ + MDEPS_ASSERT(batch.edit_selection_verts, ibo_points, vbo_pos_nor, vbo_vert_idx); if (DRW_batch_requested(cache->batch.edit_selection_verts, GPU_PRIM_POINTS)) { DRW_ibo_request(cache->batch.edit_selection_verts, &mbufcache->ibo.points); DRW_vbo_request(cache->batch.edit_selection_verts, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.edit_selection_verts, &mbufcache->vbo.vert_idx); } + MDEPS_ASSERT(batch.edit_selection_edges, ibo_lines, vbo_pos_nor, vbo_edge_idx); if (DRW_batch_requested(cache->batch.edit_selection_edges, GPU_PRIM_LINES)) { DRW_ibo_request(cache->batch.edit_selection_edges, &mbufcache->ibo.lines); DRW_vbo_request(cache->batch.edit_selection_edges, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.edit_selection_edges, &mbufcache->vbo.edge_idx); } + MDEPS_ASSERT(batch.edit_selection_faces, ibo_tris, vbo_pos_nor, vbo_poly_idx); if (DRW_batch_requested(cache->batch.edit_selection_faces, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.edit_selection_faces, &mbufcache->ibo.tris); DRW_vbo_request(cache->batch.edit_selection_faces, &mbufcache->vbo.pos_nor); DRW_vbo_request(cache->batch.edit_selection_faces, &mbufcache->vbo.poly_idx); } + MDEPS_ASSERT(batch.edit_selection_fdots, ibo_fdots, vbo_fdots_pos, vbo_fdot_idx); if (DRW_batch_requested(cache->batch.edit_selection_fdots, GPU_PRIM_POINTS)) { DRW_ibo_request(cache->batch.edit_selection_fdots, &mbufcache->ibo.fdots); DRW_vbo_request(cache->batch.edit_selection_fdots, &mbufcache->vbo.fdots_pos); @@ -1520,39 +1617,90 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mbufcache = (do_uvcage) ? &cache->uv_cage : &cache->final; /* Edit UV */ + MDEPS_ASSERT(batch.edituv_faces, ibo_edituv_tris, vbo_uv, vbo_edituv_data); if (DRW_batch_requested(cache->batch.edituv_faces, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.edituv_faces, &mbufcache->ibo.edituv_tris); DRW_vbo_request(cache->batch.edituv_faces, &mbufcache->vbo.uv); DRW_vbo_request(cache->batch.edituv_faces, &mbufcache->vbo.edituv_data); } + MDEPS_ASSERT(batch.edituv_faces_stretch_area, + ibo_edituv_tris, + vbo_uv, + vbo_edituv_data, + vbo_edituv_stretch_area); if (DRW_batch_requested(cache->batch.edituv_faces_stretch_area, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->ibo.edituv_tris); DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.uv); DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.edituv_data); DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.edituv_stretch_area); } + MDEPS_ASSERT(batch.edituv_faces_stretch_angle, + ibo_edituv_tris, + vbo_uv, + vbo_edituv_data, + vbo_edituv_stretch_angle); if (DRW_batch_requested(cache->batch.edituv_faces_stretch_angle, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->ibo.edituv_tris); DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.uv); DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.edituv_data); DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.edituv_stretch_angle); } + MDEPS_ASSERT(batch.edituv_edges, ibo_edituv_lines, vbo_uv, vbo_edituv_data); if (DRW_batch_requested(cache->batch.edituv_edges, GPU_PRIM_LINES)) { DRW_ibo_request(cache->batch.edituv_edges, &mbufcache->ibo.edituv_lines); DRW_vbo_request(cache->batch.edituv_edges, &mbufcache->vbo.uv); DRW_vbo_request(cache->batch.edituv_edges, &mbufcache->vbo.edituv_data); } + MDEPS_ASSERT(batch.edituv_verts, ibo_edituv_points, vbo_uv, vbo_edituv_data); if (DRW_batch_requested(cache->batch.edituv_verts, GPU_PRIM_POINTS)) { DRW_ibo_request(cache->batch.edituv_verts, &mbufcache->ibo.edituv_points); DRW_vbo_request(cache->batch.edituv_verts, &mbufcache->vbo.uv); DRW_vbo_request(cache->batch.edituv_verts, &mbufcache->vbo.edituv_data); } + MDEPS_ASSERT(batch.edituv_fdots, ibo_edituv_fdots, vbo_fdots_uv, vbo_fdots_edituv_data); if (DRW_batch_requested(cache->batch.edituv_fdots, GPU_PRIM_POINTS)) { DRW_ibo_request(cache->batch.edituv_fdots, &mbufcache->ibo.edituv_fdots); DRW_vbo_request(cache->batch.edituv_fdots, &mbufcache->vbo.fdots_uv); DRW_vbo_request(cache->batch.edituv_fdots, &mbufcache->vbo.fdots_edituv_data); } + MDEPS_ASSERT_MAP(vbo_lnor); + MDEPS_ASSERT_MAP(vbo_pos_nor); + MDEPS_ASSERT_MAP(vbo_uv); + MDEPS_ASSERT_MAP(vbo_vcol); + MDEPS_ASSERT_MAP(vbo_sculpt_data); + MDEPS_ASSERT_MAP(vbo_weights); + MDEPS_ASSERT_MAP(vbo_edge_fac); + MDEPS_ASSERT_MAP(vbo_mesh_analysis); + MDEPS_ASSERT_MAP(vbo_tan); + MDEPS_ASSERT_MAP(vbo_orco); + MDEPS_ASSERT_MAP(vbo_edit_data); + MDEPS_ASSERT_MAP(vbo_fdots_pos); + MDEPS_ASSERT_MAP(vbo_fdots_nor); + MDEPS_ASSERT_MAP(vbo_skin_roots); + MDEPS_ASSERT_MAP(vbo_vert_idx); + MDEPS_ASSERT_MAP(vbo_edge_idx); + MDEPS_ASSERT_MAP(vbo_poly_idx); + MDEPS_ASSERT_MAP(vbo_fdot_idx); + MDEPS_ASSERT_MAP(vbo_edituv_data); + MDEPS_ASSERT_MAP(vbo_edituv_stretch_area); + MDEPS_ASSERT_MAP(vbo_edituv_stretch_angle); + MDEPS_ASSERT_MAP(vbo_fdots_uv); + MDEPS_ASSERT_MAP(vbo_fdots_edituv_data); + + MDEPS_ASSERT_MAP(ibo_tris); + MDEPS_ASSERT_MAP(ibo_lines); + MDEPS_ASSERT_MAP(ibo_lines_loose); + MDEPS_ASSERT_MAP(ibo_lines_adjacency); + MDEPS_ASSERT_MAP(ibo_lines_paint_mask); + MDEPS_ASSERT_MAP(ibo_tris_per_mat); + MDEPS_ASSERT_MAP(ibo_points); + MDEPS_ASSERT_MAP(ibo_fdots); + MDEPS_ASSERT_MAP(ibo_edituv_tris); + MDEPS_ASSERT_MAP(ibo_edituv_lines); + MDEPS_ASSERT_MAP(ibo_edituv_points); + MDEPS_ASSERT_MAP(ibo_edituv_fdots); + /* Meh loose Scene const correctness here. */ const bool use_subsurf_fdots = scene ? BKE_modifiers_uses_subsurf_facedots(scene, ob) : false; diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index 585e171adc5..d101df737ff 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -43,26 +43,26 @@ #include "GPU_vertex_buffer.h" #include "draw_hair_private.h" +#include "draw_shader.h" #ifndef __APPLE__ # define USE_TRANSFORM_FEEDBACK # define USE_COMPUTE_SHADERS #endif -BLI_INLINE bool drw_hair_use_compute_shaders(void) +BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void) { #ifdef USE_COMPUTE_SHADERS - return GPU_compute_shader_support(); -#else - return false; + if (GPU_compute_shader_support()) { + return PART_REFINE_SHADER_COMPUTE; + } #endif +#ifdef USE_TRANSFORM_FEEDBACK + return PART_REFINE_SHADER_TRANSFORM_FEEDBACK; +#endif + return PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND; } -typedef enum ParticleRefineShader { - PART_REFINE_CATMULL_ROM = 0, - PART_REFINE_MAX_SHADER, -} ParticleRefineShader; - #ifndef USE_TRANSFORM_FEEDBACK typedef struct ParticleRefineCall { struct ParticleRefineCall *next; @@ -79,89 +79,11 @@ static int g_tf_target_height; static GPUVertBuf *g_dummy_vbo = NULL; static GPUTexture *g_dummy_texture = NULL; -static GPUShader *g_refine_shaders[PART_REFINE_MAX_SHADER] = {NULL}; static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */ -extern char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_hair_refine_vert_glsl[]; -extern char datatoc_common_hair_refine_comp_glsl[]; -extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; - -/* TODO(jbakker): move shader creation to `draw_shaders` and add test cases. */ -/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OS's. - * Currently the `__APPLE__` code-path does not compile on other platforms and vice versa. */ -#ifdef USE_COMPUTE_SHADERS -static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement)) -{ - GPUShader *sh = NULL; - sh = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl, - datatoc_common_hair_lib_glsl, - "#define HAIR_PHASE_SUBDIV\n", - __func__); - return sh; -} -#endif - -#ifdef USE_TRANSFORM_FEEDBACK -static GPUShader *hair_refine_shader_transform_feedback_create( - ParticleRefineShader UNUSED(refinement)) -{ - GPUShader *sh = NULL; - - char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); - const char *var_names[1] = {"finalColor"}; - sh = DRW_shader_create_with_transform_feedback( - shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); - MEM_freeN(shader_src); - - return sh; -} -#endif - -static GPUShader *hair_refine_shader_transform_feedback_workaround_create( - ParticleRefineShader UNUSED(refinement)) -{ - GPUShader *sh = NULL; - - char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); - sh = DRW_shader_create(shader_src, - NULL, - datatoc_gpu_shader_3D_smooth_color_frag_glsl, - "#define blender_srgb_to_framebuffer_space(a) a\n" - "#define HAIR_PHASE_SUBDIV\n" - "#define TF_WORKAROUND\n"); - MEM_freeN(shader_src); - - return sh; -} - static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) { - if (g_refine_shaders[refinement]) { - return g_refine_shaders[refinement]; - } - -#ifdef USE_COMPUTE_SHADERS - if (drw_hair_use_compute_shaders()) { - g_refine_shaders[refinement] = hair_refine_shader_compute_create(refinement); - if (g_refine_shaders[refinement]) { - return g_refine_shaders[refinement]; - } - } -#endif - -#ifdef USE_TRANSFORM_FEEDBACK - g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_create(refinement); - if (g_refine_shaders[refinement]) { - return g_refine_shaders[refinement]; - } -#endif - - g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_workaround_create( - refinement); - return g_refine_shaders[refinement]; + return DRW_shader_hair_refine_get(refinement, drw_hair_shader_type_get()); } void DRW_hair_init(void) @@ -265,7 +187,7 @@ static ParticleHairCache *drw_hair_particle_cache_get( } if (update) { - if (drw_hair_use_compute_shaders()) { + if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { drw_hair_particle_cache_update_compute(cache, subdiv); } else { @@ -473,7 +395,7 @@ void DRW_hair_update(void) #else /* Just render the pass when using compute shaders or transform feedback. */ DRW_draw_pass(g_tf_pass); - if (drw_hair_use_compute_shaders()) { + if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); } #endif @@ -481,10 +403,6 @@ void DRW_hair_update(void) void DRW_hair_free(void) { - for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { - DRW_SHADER_FREE_SAFE(g_refine_shaders[i]); - } - GPU_VERTBUF_DISCARD_SAFE(g_dummy_vbo); DRW_TEXTURE_FREE_SAFE(g_dummy_texture); } diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h index 28bd5d4dfb5..1f58d8d0ead 100644 --- a/source/blender/draw/intern/draw_hair_private.h +++ b/source/blender/draw/intern/draw_hair_private.h @@ -23,11 +23,20 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + #define MAX_LAYER_NAME_CT 4 /* u0123456789, u, au, a0123456789 */ #define MAX_LAYER_NAME_LEN (GPU_MAX_SAFE_ATTR_NAME + 2) #define MAX_THICKRES 2 /* see eHairType */ #define MAX_HAIR_SUBDIV 4 /* see hair_subdiv rna */ +typedef enum ParticleRefineShader { + PART_REFINE_CATMULL_ROM = 0, + PART_REFINE_MAX_SHADER, +} ParticleRefineShader; + struct ModifierData; struct Object; struct ParticleHairCache; @@ -91,3 +100,7 @@ bool hair_ensure_procedural_data(struct Object *object, struct ParticleHairCache **r_hair_cache, int subdiv, int thickness_res); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 37f6bbf52b5..8a805409b27 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -87,6 +87,7 @@ #include "draw_manager_profiling.h" #include "draw_manager_testing.h" #include "draw_manager_text.h" +#include "draw_shader.h" /* only for callbacks */ #include "draw_cache_impl.h" @@ -2987,6 +2988,7 @@ void DRW_engines_free(void) DRW_TEXTURE_FREE_SAFE(g_select_buffer.texture_depth); GPU_FRAMEBUFFER_FREE_SAFE(g_select_buffer.framebuffer_depth_only); + DRW_shaders_free(); DRW_hair_free(); DRW_shape_cache_free(); DRW_stats_free(); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 3b852e7f8c8..56909969dd7 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1572,8 +1572,8 @@ static void draw_frustum_culling_planes_calc(const float (*persmat)[4], float (* planes_from_projmat(persmat, frustum_planes[0], frustum_planes[5], - frustum_planes[3], frustum_planes[1], + frustum_planes[3], frustum_planes[4], frustum_planes[2]); diff --git a/source/blender/draw/intern/draw_manager_profiling.c b/source/blender/draw/intern/draw_manager_profiling.c index 9bfc8d98fe4..783ec1b1d7d 100644 --- a/source/blender/draw/intern/draw_manager_profiling.c +++ b/source/blender/draw/intern/draw_manager_profiling.c @@ -41,7 +41,7 @@ #define MAX_TIMER_NAME 32 #define MAX_NESTED_TIMER 8 -#define CHUNK_SIZE 8 +#define MIM_RANGE_LEN 8 #define GPU_TIMER_FALLOFF 0.1 typedef struct DRWTimer { @@ -82,7 +82,7 @@ void DRW_stats_begin(void) if (DTP.is_recording && DTP.timers == NULL) { DTP.chunk_count = 1; - DTP.timer_count = DTP.chunk_count * CHUNK_SIZE; + DTP.timer_count = DTP.chunk_count * MIM_RANGE_LEN; DTP.timers = MEM_callocN(sizeof(DRWTimer) * DTP.timer_count, "DRWTimer stack"); } else if (!DTP.is_recording && DTP.timers != NULL) { @@ -99,7 +99,7 @@ static DRWTimer *drw_stats_timer_get(void) if (UNLIKELY(DTP.timer_increment >= DTP.timer_count)) { /* Resize the stack. */ DTP.chunk_count++; - DTP.timer_count = DTP.chunk_count * CHUNK_SIZE; + DTP.timer_count = DTP.chunk_count * MIM_RANGE_LEN; DTP.timers = MEM_recallocN(DTP.timers, sizeof(DRWTimer) * DTP.timer_count); } diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.c new file mode 100644 index 00000000000..9c756065353 --- /dev/null +++ b/source/blender/draw/intern/draw_shader.c @@ -0,0 +1,122 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "BLI_dynstr.h" +#include "BLI_string_utils.h" + +#include "GPU_batch.h" +#include "GPU_index_buffer.h" +#include "GPU_vertex_buffer.h" + +#include "draw_shader.h" + +extern char datatoc_common_hair_lib_glsl[]; + +extern char datatoc_common_hair_refine_vert_glsl[]; +extern char datatoc_common_hair_refine_comp_glsl[]; +extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; + +static struct { + struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; +} e_data = {{NULL}}; + +/* -------------------------------------------------------------------- */ +/** \name Hair refinement + * \{ */ + +static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement)) +{ + GPUShader *sh = NULL; + sh = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl, + datatoc_common_hair_lib_glsl, + "#define HAIR_PHASE_SUBDIV\n", + __func__); + return sh; +} + +static GPUShader *hair_refine_shader_transform_feedback_create( + ParticleRefineShader UNUSED(refinement)) +{ + GPUShader *sh = NULL; + + char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, + datatoc_common_hair_refine_vert_glsl); + const char *var_names[1] = {"finalColor"}; + sh = DRW_shader_create_with_transform_feedback( + shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); + MEM_freeN(shader_src); + + return sh; +} + +static GPUShader *hair_refine_shader_transform_feedback_workaround_create( + ParticleRefineShader UNUSED(refinement)) +{ + GPUShader *sh = NULL; + + char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, + datatoc_common_hair_refine_vert_glsl); + sh = DRW_shader_create(shader_src, + NULL, + datatoc_gpu_shader_3D_smooth_color_frag_glsl, + "#define blender_srgb_to_framebuffer_space(a) a\n" + "#define HAIR_PHASE_SUBDIV\n" + "#define TF_WORKAROUND\n"); + MEM_freeN(shader_src); + + return sh; +} + +GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, + eParticleRefineShaderType sh_type) +{ + if (e_data.hair_refine_sh[refinement] == NULL) { + GPUShader *sh = NULL; + switch (sh_type) { + case PART_REFINE_SHADER_COMPUTE: + sh = hair_refine_shader_compute_create(refinement); + break; + case PART_REFINE_SHADER_TRANSFORM_FEEDBACK: + sh = hair_refine_shader_transform_feedback_create(refinement); + break; + case PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND: + sh = hair_refine_shader_transform_feedback_workaround_create(refinement); + break; + default: + BLI_assert(!"Incorrect shader type"); + } + e_data.hair_refine_sh[refinement] = sh; + } + + return e_data.hair_refine_sh[refinement]; +} + +/** \} */ + +void DRW_shaders_free(void) +{ + for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { + DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); + } +} diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h new file mode 100644 index 00000000000..f9fa452671b --- /dev/null +++ b/source/blender/draw/intern/draw_shader.h @@ -0,0 +1,47 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#pragma once + +#include "draw_hair_private.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct GPUShader; + +typedef enum eParticleRefineShaderType { + PART_REFINE_SHADER_TRANSFORM_FEEDBACK, + PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND, + PART_REFINE_SHADER_COMPUTE, +} eParticleRefineShaderType; + +/* draw_shader.c */ +struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, + eParticleRefineShaderType sh_type); +void DRW_shaders_free(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index 20b0ec738ee..2dff101c71f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -37,15 +37,14 @@ struct MeshExtract_EditUvElem_Data { bool sync_selection; }; -static void *extract_edituv_tris_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) +static void extract_edituv_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo), + void *tls_data) { - MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>( - MEM_callocN(sizeof(*data), __func__)); + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data); GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; } BLI_INLINE void edituv_tri_add( @@ -93,17 +92,17 @@ static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); } constexpr MeshExtract create_extractor_edituv_tris() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_edituv_tris_init; extractor.iter_looptri_bm = extract_edituv_tris_iter_looptri_bm; extractor.iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh; extractor.finish = extract_edituv_tris_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(MeshExtract_EditUvElem_Data); extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris); return extractor; @@ -115,15 +114,14 @@ constexpr MeshExtract create_extractor_edituv_tris() /** \name Extract Edit UV Line Indices around faces * \{ */ -static void *extract_edituv_lines_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) +static void extract_edituv_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo), + void *tls_data) { - MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>( - MEM_callocN(sizeof(*data), __func__)); + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data); GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; } BLI_INLINE void edituv_edge_add( @@ -135,7 +133,7 @@ BLI_INLINE void edituv_edge_add( } static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -184,17 +182,17 @@ static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); } constexpr MeshExtract create_extractor_edituv_lines() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_edituv_lines_init; extractor.iter_poly_bm = extract_edituv_lines_iter_poly_bm; extractor.iter_poly_mesh = extract_edituv_lines_iter_poly_mesh; extractor.finish = extract_edituv_lines_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(MeshExtract_EditUvElem_Data); extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines); return extractor; @@ -206,15 +204,14 @@ constexpr MeshExtract create_extractor_edituv_lines() /** \name Extract Edit UV Points Indices * \{ */ -static void *extract_edituv_points_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) +static void extract_edituv_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo), + void *tls_data) { - MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>( - MEM_callocN(sizeof(*data), __func__)); + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data); GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; } BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, @@ -228,7 +225,7 @@ BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, } static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_data) { @@ -269,17 +266,17 @@ static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); } constexpr MeshExtract create_extractor_edituv_points() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_edituv_points_init; extractor.iter_poly_bm = extract_edituv_points_iter_poly_bm; extractor.iter_poly_mesh = extract_edituv_points_iter_poly_mesh; extractor.finish = extract_edituv_points_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(MeshExtract_EditUvElem_Data); extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points); return extractor; @@ -291,15 +288,14 @@ constexpr MeshExtract create_extractor_edituv_points() /** \name Extract Edit UV Face-dots Indices * \{ */ -static void *extract_edituv_fdots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) +static void extract_edituv_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo), + void *tls_data) { - MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>( - MEM_callocN(sizeof(*data), __func__)); + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data); GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; } BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, @@ -316,7 +312,7 @@ BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, } static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int f_index, void *_data) { @@ -366,17 +362,17 @@ static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); } constexpr MeshExtract create_extractor_edituv_fdots() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_edituv_fdots_init; extractor.iter_poly_bm = extract_edituv_fdots_iter_poly_bm; extractor.iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh; extractor.finish = extract_edituv_fdots_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(MeshExtract_EditUvElem_Data); extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots); return extractor; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc index 9bd918dc9a5..c21725ffa32 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc @@ -32,17 +32,17 @@ namespace blender::draw { /** \name Extract Face-dots Indices * \{ */ -static void *extract_fdots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) +static void extract_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *tls_data) { - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(MEM_mallocN(sizeof(*elb), __func__)); + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data); GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); - return elb; } static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int f_index, void *_userdata) { @@ -93,17 +93,17 @@ static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); } constexpr MeshExtract create_extractor_fdots() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_fdots_init; extractor.iter_poly_bm = extract_fdots_iter_poly_bm; extractor.iter_poly_mesh = extract_fdots_iter_poly_mesh; extractor.finish = extract_fdots_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots); return extractor; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index 74a3d3825c5..2c2603af1b2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -31,28 +31,19 @@ namespace blender::draw { /** \name Extract Edges Indices * \{ */ -static void *extract_lines_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) +static void extract_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *tls_data) { - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(MEM_mallocN(sizeof(*elb), __func__)); + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data); /* Put loose edges at the end. */ GPU_indexbuf_init( elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len); - return elb; -} - -static void *extract_lines_task_init(void *_userdata) -{ - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); - GPUIndexBufBuilder *sub_builder = static_cast<GPUIndexBufBuilder *>( - MEM_mallocN(sizeof(*sub_builder), __func__)); - GPU_indexbuf_subbuilder_init(elb, sub_builder); - return sub_builder; } static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *data) { @@ -109,7 +100,7 @@ static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, } static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, + const BMEdge *eed, const int ledge_index, void *data) { @@ -147,12 +138,11 @@ static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, GPU_indexbuf_set_line_restart(elb, e_index); } -static void extract_lines_task_finish(void *_userdata, void *_task_userdata) +static void extract_lines_task_reduce(void *_userdata_to, void *_userdata_from) { - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); - GPUIndexBufBuilder *sub_builder = static_cast<GPUIndexBufBuilder *>(_task_userdata); - GPU_indexbuf_subbuilder_finish(elb, sub_builder); - MEM_freeN(sub_builder); + GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to); + GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from); + GPU_indexbuf_join(elb_to, elb_from); } static void extract_lines_finish(const MeshRenderData *UNUSED(mr), @@ -163,21 +153,20 @@ static void extract_lines_finish(const MeshRenderData *UNUSED(mr), GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); } constexpr MeshExtract create_extractor_lines() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_lines_init; - extractor.task_init = extract_lines_task_init; extractor.iter_poly_bm = extract_lines_iter_poly_bm; extractor.iter_poly_mesh = extract_lines_iter_poly_mesh; extractor.iter_ledge_bm = extract_lines_iter_ledge_bm; extractor.iter_ledge_mesh = extract_lines_iter_ledge_mesh; - extractor.task_finish = extract_lines_task_finish; + extractor.task_reduce = extract_lines_task_reduce; extractor.finish = extract_lines_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = true; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines); return extractor; @@ -209,21 +198,20 @@ static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(elb, ibo); extract_lines_loose_subbuffer(mr, cache); - MEM_freeN(elb); } constexpr MeshExtract create_extractor_lines_with_lines_loose() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_lines_init; - extractor.task_init = extract_lines_task_init; extractor.iter_poly_bm = extract_lines_iter_poly_bm; extractor.iter_poly_mesh = extract_lines_iter_poly_mesh; extractor.iter_ledge_bm = extract_lines_iter_ledge_bm; extractor.iter_ledge_mesh = extract_lines_iter_ledge_mesh; - extractor.task_finish = extract_lines_task_finish; + extractor.task_reduce = extract_lines_task_reduce; extractor.finish = extract_lines_with_lines_loose_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = true; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines); return extractor; @@ -235,21 +223,22 @@ constexpr MeshExtract create_extractor_lines_with_lines_loose() /** \name Extract Loose Edges Sub Buffer * \{ */ -static void *extract_lines_loose_only_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf) +static void extract_lines_loose_only_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data)) { BLI_assert(buf == cache->final.ibo.lines_loose); UNUSED_VARS_NDEBUG(buf); extract_lines_loose_subbuffer(mr, cache); - return NULL; } constexpr MeshExtract create_extractor_lines_loose_only() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_lines_loose_only_init; extractor.data_type = MR_DATA_NONE; + extractor.data_size = 0; extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose); return extractor; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc index 6140ae86c96..35b60002c1f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc @@ -41,26 +41,25 @@ struct MeshExtract_LineAdjacency_Data { EdgeHash *eh; bool is_manifold; /* Array to convert vert index to any loop index of this vert. */ - uint vert_to_loop[0]; + uint *vert_to_loop; }; -static void *extract_lines_adjacency_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) +static void extract_lines_adjacency_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *tls_data) { /* Similar to poly_to_tri_count(). * There is always (loop + triangle - 1) edges inside a polygon. * Accumulate for all polys and you get : */ uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len; - size_t vert_to_loop_size = sizeof(uint) * mr->vert_len; + MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(tls_data); + data->vert_to_loop = static_cast<uint *>(MEM_callocN(sizeof(uint) * mr->vert_len, __func__)); - MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>( - MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__)); GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len); data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len); data->is_manifold = true; - return data; } BLI_INLINE void lines_adjacency_triangle( @@ -169,7 +168,7 @@ static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), cache->is_manifold = data->is_manifold; GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); + MEM_freeN(data->vert_to_loop); } #undef NO_EDGE @@ -178,12 +177,13 @@ static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), constexpr MeshExtract create_extractor_lines_adjacency() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_lines_adjacency_init; extractor.iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm; extractor.iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh; extractor.finish = extract_lines_adjacency_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(MeshExtract_LineAdjacency_Data); extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency); return extractor; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc index 6bbd0188f65..277f9d69c2f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc @@ -37,18 +37,17 @@ namespace blender::draw { struct MeshExtract_LinePaintMask_Data { GPUIndexBufBuilder elb; /** One bit per edge set if face is selected. */ - BLI_bitmap select_map[0]; + BLI_bitmap *select_map; }; -static void *extract_lines_paint_mask_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) +static void extract_lines_paint_mask_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo), + void *tls_data) { - size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); - MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>( - MEM_callocN(sizeof(*data) + bitmap_size, __func__)); + MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(tls_data); + data->select_map = BLI_BITMAP_NEW(mr->edge_len, __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len); - return data; } static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, @@ -101,18 +100,19 @@ static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(_data); GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); + MEM_freeN(data->select_map); } /** \} */ constexpr MeshExtract create_extractor_lines_paint_mask() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_lines_paint_mask_init; extractor.iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh; extractor.finish = extract_lines_paint_mask_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(MeshExtract_LinePaintMask_Data); extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask); return extractor; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc index 9220198d799..cee0c224aab 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc @@ -32,25 +32,16 @@ namespace blender::draw { /* ---------------------------------------------------------------------- */ /** \name Extract Point Indices * \{ */ -static void *extract_points_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) +static void extract_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *tls_data) { - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(MEM_mallocN(sizeof(*elb), __func__)); + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data); GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len); - return elb; } -static void *extract_points_task_init(void *_userdata) -{ - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); - GPUIndexBufBuilder *sub_builder = static_cast<GPUIndexBufBuilder *>( - MEM_mallocN(sizeof(*sub_builder), __func__)); - GPU_indexbuf_subbuilder_init(elb, sub_builder); - return sub_builder; -} - -BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index) +BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, const BMVert *eve, int l_index) { const int v_index = BM_elem_index_get(eve); if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { @@ -78,7 +69,7 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, } static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, + const BMFace *f, const int UNUSED(f_index), void *_userdata) { @@ -107,7 +98,7 @@ static void extract_points_iter_poly_mesh(const MeshRenderData *mr, } static void extract_points_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, + const BMEdge *eed, const int ledge_index, void *_userdata) { @@ -127,7 +118,7 @@ static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, } static void extract_points_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, + const BMVert *eve, const int lvert_index, void *_userdata) { @@ -146,12 +137,11 @@ static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); } -static void extract_points_task_finish(void *_userdata, void *_task_userdata) +static void extract_points_task_reduce(void *_userdata_to, void *_userdata_from) { - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); - GPUIndexBufBuilder *sub_builder = static_cast<GPUIndexBufBuilder *>(_task_userdata); - GPU_indexbuf_subbuilder_finish(elb, sub_builder); - MEM_freeN(sub_builder); + GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to); + GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from); + GPU_indexbuf_join(elb_to, elb_from); } static void extract_points_finish(const MeshRenderData *UNUSED(mr), @@ -162,24 +152,23 @@ static void extract_points_finish(const MeshRenderData *UNUSED(mr), GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); } constexpr MeshExtract create_extractor_points() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_points_init; - extractor.task_init = extract_points_task_init; extractor.iter_poly_bm = extract_points_iter_poly_bm; extractor.iter_poly_mesh = extract_points_iter_poly_mesh; extractor.iter_ledge_bm = extract_points_iter_ledge_bm; extractor.iter_ledge_mesh = extract_points_iter_ledge_mesh; extractor.iter_lvert_bm = extract_points_iter_lvert_bm; extractor.iter_lvert_mesh = extract_points_iter_lvert_mesh; - extractor.task_finish = extract_points_task_finish; + extractor.task_reduce = extract_points_task_reduce; extractor.finish = extract_points_finish; extractor.use_threading = true; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc index acfffdacb88..93f71f920eb 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc @@ -33,57 +33,19 @@ namespace blender::draw { struct MeshExtract_Tri_Data { GPUIndexBufBuilder elb; - int *tri_mat_start; + const int *tri_mat_start; int *tri_mat_end; }; -static void *extract_tris_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) +static void extract_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo), + void *tls_data) { - MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>( - MEM_callocN(sizeof(*data), __func__)); - - size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; - data->tri_mat_start = static_cast<int *>(MEM_callocN(mat_tri_idx_size, __func__)); - data->tri_mat_end = static_cast<int *>(MEM_callocN(mat_tri_idx_size, __func__)); - - int *mat_tri_len = data->tri_mat_start; - /* Count how many triangle for each material. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMFace *efa; - BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - int mat = min_ii(efa->mat_nr, mr->mat_len - 1); - mat_tri_len[mat] += efa->len - 2; - } - } - } - else { - const MPoly *mp = mr->mpoly; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int mat = min_ii(mp->mat_nr, mr->mat_len - 1); - mat_tri_len[mat] += mp->totloop - 2; - } - } - } - /* Accumulate triangle lengths per material to have correct offsets. */ - int ofs = mat_tri_len[0]; - mat_tri_len[0] = 0; - for (int i = 1; i < mr->mat_len; i++) { - int tmp = mat_tri_len[i]; - mat_tri_len[i] = ofs; - ofs += tmp; - } - - memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size); - - int visible_tri_tot = ofs; - GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len); - - return data; + MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(tls_data); + data->tri_mat_start = mr->mat_offsets.tri; + data->tri_mat_end = static_cast<int *>(MEM_dupallocN(data->tri_mat_start)); + GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->mat_offsets.visible_tri_len, mr->loop_len); } static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, @@ -148,19 +110,18 @@ static void extract_tris_finish(const MeshRenderData *mr, GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); } } - MEM_freeN(data->tri_mat_start); MEM_freeN(data->tri_mat_end); - MEM_freeN(data); } constexpr MeshExtract create_extractor_tris() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_tris_init; extractor.iter_looptri_bm = extract_tris_iter_looptri_bm; extractor.iter_looptri_mesh = extract_tris_iter_looptri_mesh; extractor.finish = extract_tris_finish; - extractor.data_type = MR_DATA_NONE; + extractor.data_type = MR_DATA_MAT_OFFSETS; + extractor.data_size = sizeof(MeshExtract_Tri_Data); extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris); return extractor; @@ -171,22 +132,13 @@ constexpr MeshExtract create_extractor_tris() /** \name Extract Triangles Indices (single material) * \{ */ -static void *extract_tris_single_mat_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) +static void extract_tris_single_mat_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo), + void *tls_data) { - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(MEM_mallocN(sizeof(*elb), __func__)); + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data); GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); - return elb; -} - -static void *extract_tris_single_mat_task_init(void *_userdata) -{ - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); - GPUIndexBufBuilder *sub_builder = static_cast<GPUIndexBufBuilder *>( - MEM_mallocN(sizeof(*sub_builder), __func__)); - GPU_indexbuf_subbuilder_init(elb, sub_builder); - return sub_builder; } static void extract_tris_single_mat_iter_looptri_bm(const MeshRenderData *UNUSED(mr), @@ -222,12 +174,11 @@ static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr, } } -static void extract_tris_single_mat_task_finish(void *_userdata, void *_task_userdata) +static void extract_tris_single_mat_task_reduce(void *_userdata_to, void *_userdata_from) { - GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); - GPUIndexBufBuilder *sub_builder = static_cast<GPUIndexBufBuilder *>(_task_userdata); - GPU_indexbuf_subbuilder_finish(elb, sub_builder); - MEM_freeN(sub_builder); + GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to); + GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from); + GPU_indexbuf_join(elb_to, elb_from); } static void extract_tris_single_mat_finish(const MeshRenderData *mr, @@ -246,7 +197,7 @@ static void extract_tris_single_mat_finish(const MeshRenderData *mr, for (int i = 0; i < mr->mat_len; i++) { /* These IBOs have not been queried yet but we create them just in case they are needed * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc->tris_per_mat[i] == NULL) { + if (mbc->tris_per_mat[i] == nullptr) { mbc->tris_per_mat[i] = GPU_indexbuf_calloc(); } /* Multiply by 3 because these are triangle indices. */ @@ -254,19 +205,18 @@ static void extract_tris_single_mat_finish(const MeshRenderData *mr, GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, 0, len); } } - MEM_freeN(elb); } constexpr MeshExtract create_extractor_tris_single_mat() { - MeshExtract extractor = {0}; + MeshExtract extractor = {nullptr}; extractor.init = extract_tris_single_mat_init; - extractor.task_init = extract_tris_single_mat_task_init; extractor.iter_looptri_bm = extract_tris_single_mat_iter_looptri_bm; extractor.iter_looptri_mesh = extract_tris_single_mat_iter_looptri_mesh; - extractor.task_finish = extract_tris_single_mat_task_finish; + extractor.task_reduce = extract_tris_single_mat_task_reduce; extractor.finish = extract_tris_single_mat_finish; extractor.data_type = MR_DATA_NONE; + extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = true; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris); return extractor; diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index c96f22859ca..cce69714f5e 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -3,17 +3,22 @@ #include "testing/testing.h" #include "draw_testing.hh" -#include "intern/draw_manager_testing.h" #include "GPU_context.h" +#include "GPU_index_buffer.h" #include "GPU_init_exit.h" #include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_vertex_buffer.h" + +#include "intern/draw_manager_testing.h" #include "engines/eevee/eevee_private.h" #include "engines/gpencil/gpencil_engine.h" #include "engines/image/image_private.h" #include "engines/overlay/overlay_private.h" #include "engines/workbench/workbench_private.h" +#include "intern/draw_shader.h" namespace blender::draw { @@ -366,4 +371,20 @@ TEST_F(DrawTest, eevee_glsl_shaders_static) EEVEE_shaders_free(); } +static void test_draw_shaders(eParticleRefineShaderType sh_type) +{ + DRW_shaders_free(); + EXPECT_NE(DRW_shader_hair_refine_get(PART_REFINE_CATMULL_ROM, sh_type), nullptr); + DRW_shaders_free(); +} + +TEST_F(DrawTest, draw_glsl_shaders) +{ +#ifndef __APPLE__ + test_draw_shaders(PART_REFINE_SHADER_TRANSFORM_FEEDBACK); + test_draw_shaders(PART_REFINE_SHADER_COMPUTE); +#endif + test_draw_shaders(PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND); +} + } // namespace blender::draw diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 64082b08da9..061fe0b07c5 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -1349,10 +1349,12 @@ static void join_groups_action_temp(bAction *act) /* BLI_movelisttolist() doesn't touch first->prev and last->next pointers in its "dst" list. * Ensure that after the reshuffling the list is properly terminated. */ - FCurve *act_fcurves_first = act->curves.first; - act_fcurves_first->prev = NULL; - FCurve *act_fcurves_last = act->curves.last; - act_fcurves_last->next = NULL; + if (!BLI_listbase_is_empty(&act->curves)) { + FCurve *act_fcurves_first = act->curves.first; + act_fcurves_first->prev = NULL; + FCurve *act_fcurves_last = act->curves.last; + act_fcurves_last->next = NULL; + } } /* Change the order of anim-channels within action @@ -3441,12 +3443,14 @@ static void ANIM_OT_channels_click(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - /* NOTE: don't save settings, otherwise, can end up with some weird behavior (sticky extend) */ - prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", ""); /* SHIFTKEY */ + /* NOTE: don't save settings, otherwise, can end up with some weird behavior (sticky extend) + * + * Key-map: Enable with `Shift`. */ + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean( - ot->srna, "children_only", false, "Select Children Only", ""); /* CTRLKEY|SHIFTKEY */ + /* Key-map: Enable with `Ctrl-Shift`. */ + prop = RNA_def_boolean(ot->srna, "children_only", false, "Select Children Only", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index 17251587d3b..31284e41b18 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -109,8 +109,8 @@ void ANIM_list_elem_update(Main *bmain, Scene *scene, bAnimListElem *ale) /* in other case we do standard depsgraph update, ideally * we'd be calling property update functions here too ... */ DEG_id_tag_update(id, - ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | - ID_RECALC_ANIMATION); /* XXX or do we want something more restrictive? */ + /* XXX: or do we want something more restrictive? */ + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); } } @@ -119,11 +119,10 @@ void ANIM_list_elem_update(Main *bmain, Scene *scene, bAnimListElem *ale) void ANIM_id_update(Main *bmain, ID *id) { if (id) { - DEG_id_tag_update_ex( - bmain, - id, - ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | - ID_RECALC_ANIMATION); /* XXX or do we want something more restrictive? */ + DEG_id_tag_update_ex(bmain, + id, + /* XXX: or do we want something more restrictive? */ + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); } } diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 87291974e08..b2d74376102 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -913,7 +913,7 @@ static tAnimCopybufItem *pastebuf_match_path_property(Main *bmain, if (aci->rna_path && fcu->rna_path) { /* find the property of the fcurve and compare against the end of the tAnimCopybufItem * more involved since it needs to do path lookups. - * This is not 100% reliable since the user could be editing the curves on a path that wont + * This is not 100% reliable since the user could be editing the curves on a path that won't * resolve, or a bone could be renamed after copying for eg. but in normal copy & paste * this should work out ok. */ diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 3902f6613a1..1d4936bdf5e 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -89,15 +89,14 @@ EditBone *ED_armature_ebone_add(bArmature *arm, const char *name) bone->roll1 = 0.0f; bone->roll2 = 0.0f; bone->curve_in_x = 0.0f; - bone->curve_in_y = 0.0f; + bone->curve_in_z = 0.0f; bone->curve_out_x = 0.0f; - bone->curve_out_y = 0.0f; + bone->curve_out_z = 0.0f; bone->ease1 = 1.0f; bone->ease2 = 1.0f; - bone->scale_in_x = 1.0f; - bone->scale_in_y = 1.0f; - bone->scale_out_x = 1.0f; - bone->scale_out_y = 1.0f; + + copy_v3_fl(bone->scale_in, 1.0f); + copy_v3_fl(bone->scale_out, 1.0f); return bone; } @@ -1265,6 +1264,10 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) ebone->bbone_prev_type = ebone_iter->bbone_prev_type; ebone->bbone_next_type = ebone_iter->bbone_next_type; + ebone->bbone_flag = ebone_iter->bbone_flag; + ebone->bbone_prev_flag = ebone_iter->bbone_prev_flag; + ebone->bbone_next_flag = ebone_iter->bbone_next_flag; + /* Lets try to fix any constraint subtargets that might * have been duplicated */ @@ -1464,15 +1467,14 @@ static int armature_extrude_exec(bContext *C, wmOperator *op) newbone->roll1 = ebone->roll1; newbone->roll2 = ebone->roll2; newbone->curve_in_x = ebone->curve_in_x; - newbone->curve_in_y = ebone->curve_in_y; + newbone->curve_in_z = ebone->curve_in_z; newbone->curve_out_x = ebone->curve_out_x; - newbone->curve_out_y = ebone->curve_out_y; + newbone->curve_out_z = ebone->curve_out_z; newbone->ease1 = ebone->ease1; newbone->ease2 = ebone->ease2; - newbone->scale_in_x = ebone->scale_in_x; - newbone->scale_in_y = ebone->scale_in_y; - newbone->scale_out_x = ebone->scale_out_x; - newbone->scale_out_y = ebone->scale_out_y; + + copy_v3_v3(newbone->scale_in, ebone->scale_in); + copy_v3_v3(newbone->scale_out, ebone->scale_out); BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name)); diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 4fff2ae03b0..d429e51061b 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -161,11 +161,11 @@ typedef struct tPChanFCurveLink { /** old bbone values (to be restored along with the transform properties) */ float roll1, roll2; /** (NOTE: we haven't renamed these this time, as their names are already long enough) */ - float curve_in_x, curve_in_y; - float curve_out_x, curve_out_y; + float curve_in_x, curve_in_z; + float curve_out_x, curve_out_z; float ease1, ease2; - float scale_in_x, scale_in_y; - float scale_out_x, scale_out_y; + float scale_in[3]; + float scale_out[3]; /** copy of custom properties at start of operator (to be restored before each modal step) */ struct IDProperty *oldprops; diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index f86ec545712..fc9191967f8 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -486,8 +486,8 @@ void ED_object_vgroup_calc_from_armature(ReportList *reports, defbase_add = bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb); if (defbase_add) { - /* its possible there are DWeight's outside the range of the current - * objects deform groups, in this case the new groups wont be empty T33889. */ + /* It's possible there are DWeights outside the range of the current + * object's deform groups. In this case the new groups won't be empty T33889. */ ED_vgroup_data_clamp_range(ob->data, defbase_tot); } } diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 3d1d8d0d1f1..ffcdb99c5a3 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -414,9 +414,8 @@ void ED_armature_ebone_transform_mirror_update(bArmature *arm, EditBone *ebo, bo eboflip->tail[2] = ebo->tail[2]; eboflip->rad_tail = ebo->rad_tail; eboflip->curve_out_x = -ebo->curve_out_x; - eboflip->curve_out_y = ebo->curve_out_y; - eboflip->scale_out_x = ebo->scale_out_x; - eboflip->scale_out_y = ebo->scale_out_y; + eboflip->curve_out_z = ebo->curve_out_z; + copy_v3_v3(eboflip->scale_out, ebo->scale_out); eboflip->ease2 = ebo->ease2; eboflip->roll2 = -ebo->roll2; @@ -438,9 +437,8 @@ void ED_armature_ebone_transform_mirror_update(bArmature *arm, EditBone *ebo, bo eboflip->rad_head = ebo->rad_head; eboflip->curve_in_x = -ebo->curve_in_x; - eboflip->curve_in_y = ebo->curve_in_y; - eboflip->scale_in_x = ebo->scale_in_x; - eboflip->scale_in_y = ebo->scale_in_y; + eboflip->curve_in_z = ebo->curve_in_z; + copy_v3_v3(eboflip->scale_in, ebo->scale_in); eboflip->ease1 = ebo->ease1; eboflip->roll1 = -ebo->roll1; @@ -542,19 +540,22 @@ static EditBone *make_boneList_recursive(ListBase *edbo, eBone->roll1 = curBone->roll1; eBone->roll2 = curBone->roll2; eBone->curve_in_x = curBone->curve_in_x; - eBone->curve_in_y = curBone->curve_in_y; + eBone->curve_in_z = curBone->curve_in_z; eBone->curve_out_x = curBone->curve_out_x; - eBone->curve_out_y = curBone->curve_out_y; + eBone->curve_out_z = curBone->curve_out_z; eBone->ease1 = curBone->ease1; eBone->ease2 = curBone->ease2; - eBone->scale_in_x = curBone->scale_in_x; - eBone->scale_in_y = curBone->scale_in_y; - eBone->scale_out_x = curBone->scale_out_x; - eBone->scale_out_y = curBone->scale_out_y; + + copy_v3_v3(eBone->scale_in, curBone->scale_in); + copy_v3_v3(eBone->scale_out, curBone->scale_out); eBone->bbone_prev_type = curBone->bbone_prev_type; eBone->bbone_next_type = curBone->bbone_next_type; + eBone->bbone_flag = curBone->bbone_flag; + eBone->bbone_prev_flag = curBone->bbone_prev_flag; + eBone->bbone_next_flag = curBone->bbone_next_flag; + if (curBone->prop) { eBone->prop = IDP_CopyProperty(curBone->prop); } @@ -757,19 +758,21 @@ void ED_armature_from_edit(Main *bmain, bArmature *arm) newBone->roll1 = eBone->roll1; newBone->roll2 = eBone->roll2; newBone->curve_in_x = eBone->curve_in_x; - newBone->curve_in_y = eBone->curve_in_y; + newBone->curve_in_z = eBone->curve_in_z; newBone->curve_out_x = eBone->curve_out_x; - newBone->curve_out_y = eBone->curve_out_y; + newBone->curve_out_z = eBone->curve_out_z; newBone->ease1 = eBone->ease1; newBone->ease2 = eBone->ease2; - newBone->scale_in_x = eBone->scale_in_x; - newBone->scale_in_y = eBone->scale_in_y; - newBone->scale_out_x = eBone->scale_out_x; - newBone->scale_out_y = eBone->scale_out_y; + copy_v3_v3(newBone->scale_in, eBone->scale_in); + copy_v3_v3(newBone->scale_out, eBone->scale_out); newBone->bbone_prev_type = eBone->bbone_prev_type; newBone->bbone_next_type = eBone->bbone_next_type; + newBone->bbone_flag = eBone->bbone_flag; + newBone->bbone_prev_flag = eBone->bbone_prev_flag; + newBone->bbone_next_flag = eBone->bbone_next_flag; + if (eBone->prop) { newBone->prop = IDP_CopyProperty(eBone->prop); } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 9300fcf193d..d32faf9a9ea 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -19,6 +19,28 @@ /** \file * \ingroup edarmature + * + * Pose 'Sliding' Tools + * ==================== + * + * - Push & Relax, Breakdowner + + * These tools provide the animator with various capabilities + * for interactively controlling the spacing of poses, but also + * for 'pushing' and/or 'relaxing' extremes as they see fit. + * + * - Propagate + + * This tool copies elements of the selected pose to successive + * keyframes, allowing the animator to go back and modify the poses + * for some "static" pose controls, without having to repeatedly + * doing a "next paste" dance. + * + * - Pose Sculpting (TODO) + + * This is yet to be implemented, but the idea here is to use + * sculpting techniques to make it easier to pose rigs by allowing + * rigs to be manipulated using a familiar paint-based interface. */ #include "MEM_guardedalloc.h" @@ -75,35 +97,48 @@ #define OVERSHOOT_RANGE_DELTA 0.2f /* **************************************************** */ -/* == POSE 'SLIDING' TOOLS == - * - * A) Push & Relax, Breakdowner - * These tools provide the animator with various capabilities - * for interactively controlling the spacing of poses, but also - * for 'pushing' and/or 'relaxing' extremes as they see fit. - * - * B) Propagate - * This tool copies elements of the selected pose to successive - * keyframes, allowing the animator to go back and modify the poses - * for some "static" pose controls, without having to repeatedly - * doing a "next paste" dance. - * - * C) Pose Sculpting - * This is yet to be implemented, but the idea here is to use - * sculpting techniques to make it easier to pose rigs by allowing - * rigs to be manipulated using a familiar paint-based interface. - */ -/* **************************************************** */ /* A) Push & Relax, Breakdowner */ -/* Temporary data shared between these operators */ +/** Axis Locks. */ +typedef enum ePoseSlide_AxisLock { + PS_LOCK_X = (1 << 0), + PS_LOCK_Y = (1 << 1), + PS_LOCK_Z = (1 << 2), +} ePoseSlide_AxisLock; + +/** Pose Sliding Modes. */ +typedef enum ePoseSlide_Modes { + /** Exaggerate the pose. */ + POSESLIDE_PUSH = 0, + /** soften the pose. */ + POSESLIDE_RELAX, + /** Slide between the endpoint poses, finding a 'soft' spot. */ + POSESLIDE_BREAKDOWN, + POSESLIDE_PUSH_REST, + POSESLIDE_RELAX_REST, +} ePoseSlide_Modes; + +/** Transforms/Channels to Affect. */ +typedef enum ePoseSlide_Channels { + PS_TFM_ALL = 0, /* All transforms and properties */ + + PS_TFM_LOC, /* Loc/Rot/Scale */ + PS_TFM_ROT, + PS_TFM_SIZE, + + PS_TFM_BBONE_SHAPE, /* Bendy Bones */ + + PS_TFM_PROPS, /* Custom Properties */ +} ePoseSlide_Channels; + +/** Temporary data shared between these operators. */ typedef struct tPoseSlideOp { /** current scene */ Scene *scene; /** area that we're operating in (needed for modal()) */ ScrArea *area; - /** region that we're operating in (needed for modal()) */ - ARegion *region; + /** Header of the region used for drawing the slider. */ + ARegion *region_header; /** len of the PoseSlideObject array. */ uint objects_len; @@ -120,76 +155,57 @@ typedef struct tPoseSlideOp { /** frame after current frame (blend-to) - global time */ int nextFrame; - /** sliding mode (ePoseSlide_Modes) */ - short mode; + /** Sliding Mode. */ + ePoseSlide_Modes mode; /** unused for now, but can later get used for storing runtime settings.... */ short flag; /* Store overlay settings when invoking the operator. Bones will be temporarily hidden. */ int overlay_flag; - /** which transforms/channels are affected (ePoseSlide_Channels) */ - short channels; - /** axis-limits for transforms (ePoseSlide_AxisLock) */ - short axislock; + /** Which transforms/channels are affected. */ + ePoseSlide_Channels channels; + /** Axis-limits for transforms. */ + ePoseSlide_AxisLock axislock; - /* Allow overshoot or clamp between 0% and 100%. */ + /** Allow overshoot or clamp between 0% and 100%. */ bool overshoot; - /* Reduces percentage delta from mouse movement. */ + /** Reduces factor delta from mouse movement. */ bool precision; - /* Move percentage in 10% steps. */ + /** Move factor in 10% steps. */ bool increments; - /* Draw callback handler. */ + /** Draw callback handler. */ void *draw_handle; - /* Accumulative, unclamped and unrounded percentage. */ - float raw_percentage; + /** Accumulative, unclamped and unrounded factor. */ + float raw_factor; - /* 0-1 value for determining the influence of whatever is relevant. */ - float percentage; + /** 0-1 value for determining the influence of whatever is relevant. */ + float factor; - /* Last cursor position in screen space used for mouse movement delta calculation. */ + /** Last cursor position in screen space used for mouse movement delta calculation. */ int last_cursor_x; - /* Numeric input. */ + /** Numeric input. */ NumInput num; struct tPoseSlideObject *ob_data_array; } tPoseSlideOp; typedef struct tPoseSlideObject { - Object *ob; /* active object that Pose Info comes from */ - float prevFrameF; /* prevFrame, but in local action time (for F-Curve lookups to work) */ - float nextFrameF; /* nextFrame, but in local action time (for F-Curve lookups to work) */ + /** Active object that Pose Info comes from. */ + Object *ob; + /** `prevFrame`, but in local action time (for F-Curve look-ups to work). */ + float prevFrameF; + /** `nextFrame`, but in local action time (for F-Curve look-ups to work). */ + float nextFrameF; bool valid; } tPoseSlideObject; -/* Pose Sliding Modes */ -typedef enum ePoseSlide_Modes { - POSESLIDE_PUSH = 0, /* exaggerate the pose... */ - POSESLIDE_RELAX, /* soften the pose... */ - POSESLIDE_BREAKDOWN, /* slide between the endpoint poses, finding a 'soft' spot */ - POSESLIDE_PUSH_REST, - POSESLIDE_RELAX_REST, -} ePoseSlide_Modes; - -/* Transforms/Channels to Affect */ -typedef enum ePoseSlide_Channels { - PS_TFM_ALL = 0, /* All transforms and properties */ - - PS_TFM_LOC, /* Loc/Rot/Scale */ - PS_TFM_ROT, - PS_TFM_SIZE, - - PS_TFM_BBONE_SHAPE, /* Bendy Bones */ - - PS_TFM_PROPS, /* Custom Properties */ -} ePoseSlide_Channels; - -/* Property enum for ePoseSlide_Channels */ +/** Property enum for #ePoseSlide_Channels. */ static const EnumPropertyItem prop_channels_types[] = { {PS_TFM_ALL, "ALL", @@ -204,13 +220,6 @@ static const EnumPropertyItem prop_channels_types[] = { {0, NULL, 0, NULL, NULL}, }; -/* Axis Locks */ -typedef enum ePoseSlide_AxisLock { - PS_LOCK_X = (1 << 0), - PS_LOCK_Y = (1 << 1), - PS_LOCK_Z = (1 << 2), -} ePoseSlide_AxisLock; - /* Property enum for ePoseSlide_AxisLock */ static const EnumPropertyItem prop_axis_lock_types[] = { {0, "FREE", 0, "Free", "All axes are affected"}, @@ -248,22 +257,22 @@ static void draw_overshoot_triangle(const uint8_t color[4], immUnbindProgram(); } -static void draw_ticks(const float start_percentage, - const float end_percentage, - const struct vec2f line_start, +static void draw_ticks(const float start_factor, + const float end_factor, + const float line_start[2], const float base_tick_height, const float line_width, const uint8_t color_overshoot[4], const uint8_t color_line[4]) { - /* Use percentage represented as 0-100 int to avoid floating point precision problems. */ + /* Use factor represented as 0-100 int to avoid floating point precision problems. */ const int tick_increment = 10; - /* Round initial_tick_percentage up to the next tick_increment. */ - int tick_percentage = ceil((start_percentage * 100) / tick_increment) * tick_increment; - float tick_height = base_tick_height; + /* Round initial_tick_factor up to the next tick_increment. */ + int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment; - while (tick_percentage <= (int)(end_percentage * 100)) { + while (tick_percentage <= (int)(end_factor * 100)) { + float tick_height; /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit * smaller and the rest is the minimum size. */ if (tick_percentage % 100 == 0) { @@ -276,12 +285,14 @@ static void draw_ticks(const float start_percentage, tick_height = base_tick_height * 0.5; } - const float x = line_start.x + - (((float)tick_percentage / 100) - start_percentage) * SLIDE_PIXEL_DISTANCE; - const struct rctf tick_rect = {.xmin = x - (line_width / 2), - .xmax = x + (line_width / 2), - .ymin = line_start.y - (tick_height / 2), - .ymax = line_start.y + (tick_height / 2)}; + const float x = line_start[0] + + (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE; + const rctf tick_rect = { + .xmin = x - (line_width / 2), + .xmax = x + (line_width / 2), + .ymin = line_start[1] - (tick_height / 2), + .ymax = line_start[1] + (tick_height / 2), + }; if (tick_percentage < 0 || tick_percentage > 100) { UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255); @@ -293,73 +304,83 @@ static void draw_ticks(const float start_percentage, } } -static void draw_main_line(const struct rctf main_line_rect, - const float percentage, +static void draw_main_line(const rctf *main_line_rect, + const float factor, const bool overshoot, const uint8_t color_overshoot[4], const uint8_t color_line[4]) { if (overshoot) { /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */ - const float line_zero_percent = main_line_rect.xmin - - ((percentage - 0.5f - OVERSHOOT_RANGE_DELTA) * + const float line_zero_percent = main_line_rect->xmin - + ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) * SLIDE_PIXEL_DISTANCE); const float clamped_line_zero_percent = clamp_f( - line_zero_percent, main_line_rect.xmin, main_line_rect.xmax); + line_zero_percent, main_line_rect->xmin, main_line_rect->xmax); const float clamped_line_hundred_percent = clamp_f( - line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect.xmin, main_line_rect.xmax); - - const struct rctf left_overshoot_line_rect = {.xmin = main_line_rect.xmin, - .xmax = clamped_line_zero_percent, - .ymin = main_line_rect.ymin, - .ymax = main_line_rect.ymax}; - const struct rctf right_overshoot_line_rect = {.xmin = clamped_line_hundred_percent, - .xmax = main_line_rect.xmax, - .ymin = main_line_rect.ymin, - .ymax = main_line_rect.ymax}; + line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax); + + const rctf left_overshoot_line_rect = { + .xmin = main_line_rect->xmin, + .xmax = clamped_line_zero_percent, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; + const rctf right_overshoot_line_rect = { + .xmin = clamped_line_hundred_percent, + .xmax = main_line_rect->xmax, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255); UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255); - const struct rctf non_overshoot_line_rect = {.xmin = clamped_line_zero_percent, - .xmax = clamped_line_hundred_percent, - .ymin = main_line_rect.ymin, - .ymax = main_line_rect.ymax}; + const rctf non_overshoot_line_rect = { + .xmin = clamped_line_zero_percent, + .xmax = clamped_line_hundred_percent, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255); } else { - UI_draw_roundbox_3ub_alpha(&main_line_rect, true, 0, color_line, 255); + UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255); } } static void draw_backdrop(const int fontid, - const struct rctf main_line_rect, + const rctf *main_line_rect, const float color_bg[4], const short region_y_size, const float base_tick_height) { float string_pixel_size[2]; - const char *percentage_placeholder = "000%%"; + const char *percentage_string_placeholder = "000%%"; BLF_width_and_height(fontid, - percentage_placeholder, - sizeof(percentage_placeholder), + percentage_string_placeholder, + sizeof(percentage_string_placeholder), &string_pixel_size[0], &string_pixel_size[1]); - const struct vec2f pad = {.x = (region_y_size - base_tick_height) / 2, .y = 2.0f * U.pixelsize}; - const struct rctf backdrop_rect = {.xmin = main_line_rect.xmin - string_pixel_size[0] - pad.x, - .xmax = main_line_rect.xmax + pad.x, - .ymin = pad.y, - .ymax = region_y_size - pad.y}; + const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize}; + const rctf backdrop_rect = { + .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0], + .xmax = main_line_rect->xmax + pad[0], + .ymin = pad[1], + .ymax = region_y_size - pad[1], + }; UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); } -/* Draw an on screen Slider for a Pose Slide Operator. */ +/** + * Draw an on screen Slider for a Pose Slide Operator. + */ static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg) { tPoseSlideOp *pso = arg; /* Only draw in region from which the Operator was started. */ - if (region != pso->region) { + if (region != pso->region_header) { return; } @@ -392,28 +413,30 @@ static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion const float base_tick_height = 12.0 * U.pixelsize; const float line_y = region->winy / 2; - struct rctf main_line_rect = {.xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2), - .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2), - .ymin = line_y - line_width / 2, - .ymax = line_y + line_width / 2}; - float line_start_percentage = 0; - int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->percentage; + rctf main_line_rect = { + .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2), + .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2), + .ymin = line_y - line_width / 2, + .ymax = line_y + line_width / 2, + }; + float line_start_factor = 0; + int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->factor; if (pso->overshoot) { main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; - line_start_percentage = pso->percentage - 0.5f - OVERSHOOT_RANGE_DELTA; + line_start_factor = pso->factor - 0.5f - OVERSHOOT_RANGE_DELTA; handle_pos_x = region->winx / 2; } - draw_backdrop(fontid, main_line_rect, color_bg, pso->region->winy, base_tick_height); + draw_backdrop(fontid, &main_line_rect, color_bg, pso->region_header->winy, base_tick_height); - draw_main_line(main_line_rect, pso->percentage, pso->overshoot, color_overshoot, color_line); + draw_main_line(&main_line_rect, pso->factor, pso->overshoot, color_overshoot, color_line); - const float percentage_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1; - const struct vec2f line_start_position = {.x = main_line_rect.xmin, .y = line_y}; - draw_ticks(line_start_percentage, - line_start_percentage + percentage_range, + const float factor_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1; + const float line_start_position[2] = {main_line_rect.xmin, line_y}; + draw_ticks(line_start_factor, + line_start_factor + factor_range, line_start_position, base_tick_height, line_width, @@ -423,68 +446,70 @@ static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100% * range.*/ if (pso->overshoot) { - if (pso->percentage > 1 + OVERSHOOT_RANGE_DELTA + 0.5) { + if (pso->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) { draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y); } - if (pso->percentage < 0 - OVERSHOOT_RANGE_DELTA - 0.5) { + if (pso->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) { draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y); } } char percentage_string[256]; - /* Draw handle indicating current percentage. */ - const struct rctf handle_rect = {.xmin = handle_pos_x - (line_width), - .xmax = handle_pos_x + (line_width), - .ymin = line_y - (base_tick_height / 2), - .ymax = line_y + (base_tick_height / 2)}; + /* Draw handle indicating current factor. */ + const rctf handle_rect = { + .xmin = handle_pos_x - (line_width), + .xmax = handle_pos_x + (line_width), + .ymin = line_y - (base_tick_height / 2), + .ymax = line_y + (base_tick_height / 2), + }; UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255); - BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->percentage * 100); + BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->factor * 100); /* Draw percentage string. */ - float percentage_pixel_size[2]; + float percentage_string_pixel_size[2]; BLF_width_and_height(fontid, percentage_string, sizeof(percentage_string), - &percentage_pixel_size[0], - &percentage_pixel_size[1]); + &percentage_string_pixel_size[0], + &percentage_string_pixel_size[1]); BLF_position(fontid, - main_line_rect.xmin - 24.0 * U.pixelsize - percentage_pixel_size[0] / 2, - (region->winy / 2) - percentage_pixel_size[1] / 2, + main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2, + (region->winy / 2) - percentage_string_pixel_size[1] / 2, 0.0f); BLF_draw(fontid, percentage_string, sizeof(percentage_string)); } -/* operator init */ +/** Operator custom-data initialization. */ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) { tPoseSlideOp *pso; - /* init slide-op data */ + /* Init slide-op data. */ pso = op->customdata = MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp"); - /* get info from context */ + /* Get info from context. */ pso->scene = CTX_data_scene(C); - pso->area = CTX_wm_area(C); /* only really needed when doing modal() */ - pso->region = CTX_wm_region(C); /* only really needed when doing modal() */ + pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */ + pso->region_header = CTX_wm_region(C); /* Only really needed when doing modal(). */ pso->cframe = pso->scene->r.cfra; pso->mode = mode; - /* set range info from property values - these may get overridden for the invoke() */ - pso->percentage = RNA_float_get(op->ptr, "percentage"); - pso->raw_percentage = pso->percentage; + /* Set range info from property values - these may get overridden for the invoke(). */ + pso->factor = RNA_float_get(op->ptr, "factor"); + pso->raw_factor = pso->factor; pso->prevFrame = RNA_int_get(op->ptr, "prev_frame"); pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); - /* get the set of properties/axes that can be operated on */ + /* Get the set of properties/axes that can be operated on. */ pso->channels = RNA_enum_get(op->ptr, "channels"); pso->axislock = RNA_enum_get(op->ptr, "axis_lock"); - /* for each Pose-Channel which gets affected, get the F-Curves for that channel - * and set the relevant transform flags... */ + /* For each Pose-Channel which gets affected, get the F-Curves for that channel + * and set the relevant transform flags. */ poseAnim_mapping_get(C, &pso->pfLinks); Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data( @@ -504,43 +529,44 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) ob_data->ob = ob_iter; ob_data->valid = true; - /* apply NLA mapping corrections so the frame lookups work */ + /* Apply NLA mapping corrections so the frame look-ups work. */ ob_data->prevFrameF = BKE_nla_tweakedit_remap( ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); ob_data->nextFrameF = BKE_nla_tweakedit_remap( ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); - /* set depsgraph flags */ - /* make sure the lock is set OK, unlock can be accidentally saved? */ + /* Set depsgraph flags. */ + /* Make sure the lock is set OK, unlock can be accidentally saved? */ ob_data->ob->pose->flag |= POSE_LOCKED; ob_data->ob->pose->flag &= ~POSE_DO_UNLOCK; } MEM_freeN(objects); - /* do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up - * to the caller of this (usually only invoke() will do it, to make things more efficient). - */ + /* Do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up + * to the caller of this (usually only invoke() will do it, to make things more efficient). */ BLI_dlrbTree_init(&pso->keys); /* Initialize numeric input. */ initNumInput(&pso->num); - pso->num.idx_max = 0; /* one axis */ + pso->num.idx_max = 0; /* One axis. */ pso->num.val_flag[0] |= NUM_NO_NEGATIVE; - pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */ + pso->num.unit_type[0] = B_UNIT_NONE; /* Percentages don't have any units. */ /* Register UI drawing callback. */ ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER); if (region_header != NULL) { - pso->region = region_header; + pso->region_header = region_header; pso->draw_handle = ED_region_draw_cb_activate( region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL); } - /* return status is whether we've got all the data we were requested to get */ + /* Return status is whether we've got all the data we were requested to get. */ return 1; } -/* exiting the operator - free data */ +/** + * Exiting the operator (free data). + */ static void pose_slide_exit(wmOperator *op) { tPoseSlideOp *pso = op->customdata; @@ -550,34 +576,33 @@ static void pose_slide_exit(wmOperator *op) v3d->overlay.flag = pso->overlay_flag; /* Remove UI drawing callback. */ - ED_region_draw_cb_exit(pso->region->type, pso->draw_handle); + ED_region_draw_cb_exit(pso->region_header->type, pso->draw_handle); - /* if data exists, clear its data and exit */ - if (pso) { - /* free the temp pchan links and their data */ - poseAnim_mapping_free(&pso->pfLinks); + /* Free the temp pchan links and their data. */ + poseAnim_mapping_free(&pso->pfLinks); - /* free RB-BST for keyframes (if it contained data) */ - BLI_dlrbTree_free(&pso->keys); + /* Free RB-BST for keyframes (if it contained data). */ + BLI_dlrbTree_free(&pso->keys); - if (pso->ob_data_array != NULL) { - MEM_freeN(pso->ob_data_array); - } - - /* free data itself */ - MEM_freeN(pso); + if (pso->ob_data_array != NULL) { + MEM_freeN(pso->ob_data_array); } - /* cleanup */ + /* Free data itself. */ + MEM_freeN(pso); + + /* Cleanup. */ op->customdata = NULL; } /* ------------------------------------ */ -/* helper for apply() / reset() - refresh the data */ +/** + * Helper for apply() / reset() - refresh the data. + */ static void pose_slide_refresh(bContext *C, tPoseSlideOp *pso) { - /* wrapper around the generic version, allowing us to add some custom stuff later still */ + /* Wrapper around the generic version, allowing us to add some custom stuff later still. */ for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; if (ob_data->valid) { @@ -609,7 +634,9 @@ static bool pose_frame_range_from_object_get(tPoseSlideOp *pso, return false; } -/* helper for apply() - perform sliding for some value */ +/** + * Helper for apply() - perform sliding for some value. + */ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, float *val) { float prevFrameF, nextFrameF; @@ -619,17 +646,17 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo pose_frame_range_from_object_get(pso, ob, &prevFrameF, &nextFrameF); - /* get keyframe values for endpoint poses to blend with */ - /* previous/start */ + /* Get keyframe values for endpoint poses to blend with. */ + /* Previous/start. */ sVal = evaluate_fcurve(fcu, prevFrameF); - /* next/end */ + /* Next/end. */ eVal = evaluate_fcurve(fcu, nextFrameF); - /* calculate the relative weights of the endpoints */ + /* Calculate the relative weights of the endpoints. */ if (pso->mode == POSESLIDE_BREAKDOWN) { - /* get weights from the percentage control */ - w1 = pso->percentage; /* this must come second */ - w2 = 1.0f - w1; /* this must come first */ + /* Get weights from the factor control. */ + w1 = pso->factor; /* This must come second. */ + w2 = 1.0f - w1; /* This must come first. */ } else { /* - these weights are derived from the relative distance of these @@ -652,30 +679,37 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo * the value the current frame is closer to. */ switch (pso->mode) { - case POSESLIDE_PUSH: /* make the current pose more pronounced */ + case POSESLIDE_PUSH: /* Make the current pose more pronounced. */ { /* Slide the pose away from the breakdown pose in the timeline */ - (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->percentage; + (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor; break; } - case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */ + case POSESLIDE_RELAX: /* Make the current pose more like its surrounding ones. */ { /* Slide the pose towards the breakdown pose in the timeline */ - (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->percentage; + (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor; break; } - case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */ + case POSESLIDE_BREAKDOWN: /* Make the current pose slide around between the endpoints. */ { /* Perform simple linear interpolation - - * coefficient for start must come from pso->percentage. */ + * coefficient for start must come from pso->factor. */ /* TODO: make this use some kind of spline interpolation instead? */ (*val) = ((sVal * w2) + (eVal * w1)); break; } + /* Those are handled in pose_slide_rest_pose_apply. */ + case POSESLIDE_PUSH_REST: + case POSESLIDE_RELAX_REST: { + break; + } } } -/* helper for apply() - perform sliding for some 3-element vector */ +/** + * Helper for apply() - perform sliding for some 3-element vector. + */ static void pose_slide_apply_vec3(tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], @@ -684,30 +718,32 @@ static void pose_slide_apply_vec3(tPoseSlideOp *pso, LinkData *ld = NULL; char *path = NULL; - /* get the path to use... */ + /* Get the path to use. */ path = BLI_sprintfN("%s.%s", pfl->pchan_path, propName); - /* using this path, find each matching F-Curve for the variables we're interested in */ + /* Using this path, find each matching F-Curve for the variables we're interested in. */ while ((ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path))) { FCurve *fcu = (FCurve *)ld->data; const int idx = fcu->array_index; const int lock = pso->axislock; - /* check if this F-Curve is ok given the current axis locks */ + /* Check if this F-Curve is ok given the current axis locks. */ BLI_assert(fcu->array_index < 3); if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) || ((lock & PS_LOCK_Z) && (idx == 2))) { - /* just work on these channels one by one... there's no interaction between values */ + /* Just work on these channels one by one... there's no interaction between values. */ pose_slide_apply_val(pso, fcu, pfl->ob, &vec[fcu->array_index]); } } - /* free the temp path we got */ + /* Free the temp path we got. */ MEM_freeN(path); } -/* helper for apply() - perform sliding for custom properties or bbone properties */ +/** + * Helper for apply() - perform sliding for custom properties or bbone properties. + */ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl, const char prop_prefix[]) @@ -716,7 +752,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, LinkData *ld; int len = strlen(pfl->pchan_path); - /* setup pointer RNA for resolving paths */ + /* Setup pointer RNA for resolving paths. */ RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr); /* - custom properties are just denoted using ["..."][etc.] after the end of the base path, @@ -732,22 +768,21 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, continue; } - /* do we have a match? - * - bPtr is the RNA Path with the standard part chopped off - * - pPtr is the chunk of the path which is left over + /* Do we have a match? + * - bPtr is the RNA Path with the standard part chopped off. + * - pPtr is the chunk of the path which is left over. */ bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len; pPtr = strstr(bPtr, prop_prefix); if (pPtr) { - /* use RNA to try and get a handle on this property, then, assuming that it is just - * numerical, try and grab the value as a float for temp editing before setting back - */ + /* Use RNA to try and get a handle on this property, then, assuming that it is just + * numerical, try and grab the value as a float for temp editing before setting back. */ PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr); if (prop) { switch (RNA_property_type(prop)) { - /* continuous values that can be smoothly interpolated... */ + /* Continuous values that can be smoothly interpolated. */ case PROP_FLOAT: { float tval = RNA_property_float_get(&ptr, prop); pose_slide_apply_val(pso, fcu, pfl->ob, &tval); @@ -761,7 +796,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, break; } - /* values which can only take discrete values */ + /* Values which can only take discrete values. */ case PROP_BOOLEAN: { float tval = (float)RNA_property_boolean_get(&ptr, prop); pose_slide_apply_val(pso, fcu, pfl->ob, &tval); @@ -770,14 +805,13 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, break; } case PROP_ENUM: { - /* don't handle this case - these don't usually represent interchangeable - * set of values which should be interpolated between - */ + /* Don't handle this case - these don't usually represent interchangeable + * set of values which should be interpolated between. */ break; } default: - /* cannot handle */ + /* Cannot handle. */ // printf("Cannot Pose Slide non-numerical property\n"); break; } @@ -786,7 +820,9 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, } } -/* helper for apply() - perform sliding for quaternion rotations (using quat blending) */ +/** + * Helper for apply() - perform sliding for quaternion rotations (using quat blending). + */ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) { FCurve *fcu_w = NULL, *fcu_x = NULL, *fcu_y = NULL, *fcu_z = NULL; @@ -801,17 +837,17 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) return; } - /* get the path to use - this should be quaternion rotations only (needs care) */ + /* Get the path to use - this should be quaternion rotations only (needs care). */ path = BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion"); - /* get the current frame number */ + /* Get the current frame number. */ cframe = (float)pso->cframe; - /* using this path, find each matching F-Curve for the variables we're interested in */ + /* Using this path, find each matching F-Curve for the variables we're interested in. */ while ((ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path))) { FCurve *fcu = (FCurve *)ld->data; - /* assign this F-Curve to one of the relevant pointers... */ + /* Assign this F-Curve to one of the relevant pointers. */ switch (fcu->array_index) { case 3: /* z */ fcu_z = fcu; @@ -828,14 +864,14 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) } } - /* only if all channels exist, proceed */ + /* Only if all channels exist, proceed. */ if (fcu_w && fcu_x && fcu_y && fcu_z) { float quat_final[4]; - /* perform blending */ + /* Perform blending. */ if (pso->mode == POSESLIDE_BREAKDOWN) { /* Just perform the interpolation between quat_prev and - * quat_next using pso->percentage as a guide. */ + * quat_next using pso->factor as a guide. */ float quat_prev[4]; float quat_next[4]; @@ -852,7 +888,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) normalize_qt(quat_prev); normalize_qt(quat_next); - interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->percentage); + interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->factor); } else { /* POSESLIDE_PUSH and POSESLIDE_RELAX. */ @@ -870,11 +906,11 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) normalize_qt(quat_curr); if (pso->mode == POSESLIDE_PUSH) { - interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->percentage); + interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->factor); } else { BLI_assert(pso->mode == POSESLIDE_RELAX); - interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->percentage); + interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->factor); } } @@ -882,7 +918,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) quat_to_compatible_quat(pchan->quat, quat_final, pchan->quat); } - /* free the path now */ + /* Free the path now. */ MEM_freeN(path); } @@ -895,11 +931,11 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo ((lock & PS_LOCK_Z) && (idx == 2))) { float diff_val = default_value - vec[idx]; if (pso->mode == POSESLIDE_RELAX_REST) { - vec[idx] += pso->percentage * diff_val; + vec[idx] += pso->factor * diff_val; } else { /* Push */ - vec[idx] -= pso->percentage * diff_val; + vec[idx] -= pso->factor * diff_val; } } } @@ -917,56 +953,58 @@ static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4] for (int idx = 0; idx < 4; idx++) { float diff_val = default_values[idx] - vec[idx]; if (pso->mode == POSESLIDE_RELAX_REST) { - vec[idx] += pso->percentage * diff_val; + vec[idx] += pso->factor * diff_val; } else { /* Push */ - vec[idx] -= pso->percentage * diff_val; + vec[idx] -= pso->factor * diff_val; } } } -/* apply() - perform the pose sliding between the current pose and the rest pose */ +/** + * apply() - perform the pose sliding between the current pose and the rest pose. + */ static void pose_slide_rest_pose_apply(bContext *C, tPoseSlideOp *pso) { tPChanFCurveLink *pfl; - /* for each link, handle each set of transforms */ + /* For each link, handle each set of transforms. */ for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { - /* valid transforms for each PoseChannel should have been noted already - * - sliding the pose should be a straightforward exercise for location+rotation, + /* Valid transforms for each #bPoseChannel should have been noted already. + * - Sliding the pose should be a straightforward exercise for location+rotation, * but rotations get more complicated since we may want to use quaternion blending - * for quaternions instead... + * for quaternions instead. */ bPoseChannel *pchan = pfl->pchan; if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) { - /* calculate these for the 'location' vector, and use location curves */ + /* Calculate these for the 'location' vector, and use location curves. */ pose_slide_rest_pose_apply_vec3(pso, pchan->loc, 0.0f); } if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) { - /* calculate these for the 'scale' vector, and use scale curves */ + /* Calculate these for the 'scale' vector, and use scale curves. */ pose_slide_rest_pose_apply_vec3(pso, pchan->size, 1.0f); } if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) { - /* everything depends on the rotation mode */ + /* Everything depends on the rotation mode. */ if (pchan->rotmode > 0) { - /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */ + /* Eulers - so calculate these for the 'eul' vector, and use euler_rotation curves. */ pose_slide_rest_pose_apply_vec3(pso, pchan->eul, 0.0f); } else if (pchan->rotmode == ROT_MODE_AXISANGLE) { pose_slide_rest_pose_apply_other_rot(pso, pchan->quat, false); } else { - /* quaternions - use quaternion blending */ + /* Quaternions - use quaternion blending. */ pose_slide_rest_pose_apply_other_rot(pso, pchan->quat, true); } } if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) { - /* bbone properties - they all start a "bbone_" prefix */ + /* Bbone properties - they all start a "bbone_" prefix. */ /* TODO Not implemented */ // pose_slide_apply_props(pso, pfl, "bbone_"); } @@ -979,18 +1017,20 @@ static void pose_slide_rest_pose_apply(bContext *C, tPoseSlideOp *pso) } } - /* depsgraph updates + redraws */ + /* Depsgraph updates + redraws. */ pose_slide_refresh(C, pso); } -/* apply() - perform the pose sliding based on weighting various poses */ +/** + * apply() - perform the pose sliding based on weighting various poses. + */ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) { tPChanFCurveLink *pfl; /* Sanitize the frame ranges. */ if (pso->prevFrame == pso->nextFrame) { - /* move out one step either side */ + /* Move out one step either side. */ pso->prevFrame--; pso->nextFrame++; @@ -1001,7 +1041,7 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) continue; } - /* apply NLA mapping corrections so the frame lookups work */ + /* Apply NLA mapping corrections so the frame look-ups work. */ ob_data->prevFrameF = BKE_nla_tweakedit_remap( ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); ob_data->nextFrameF = BKE_nla_tweakedit_remap( @@ -1009,9 +1049,9 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) } } - /* for each link, handle each set of transforms */ + /* For each link, handle each set of transforms. */ for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { - /* valid transforms for each PoseChannel should have been noted already + /* Valid transforms for each #bPoseChannel should have been noted already * - sliding the pose should be a straightforward exercise for location+rotation, * but rotations get more complicated since we may want to use quaternion blending * for quaternions instead... @@ -1019,32 +1059,32 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) bPoseChannel *pchan = pfl->pchan; if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) { - /* calculate these for the 'location' vector, and use location curves */ + /* Calculate these for the 'location' vector, and use location curves. */ pose_slide_apply_vec3(pso, pfl, pchan->loc, "location"); } if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) { - /* calculate these for the 'scale' vector, and use scale curves */ + /* Calculate these for the 'scale' vector, and use scale curves. */ pose_slide_apply_vec3(pso, pfl, pchan->size, "scale"); } if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) { - /* everything depends on the rotation mode */ + /* Everything depends on the rotation mode. */ if (pchan->rotmode > 0) { - /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */ + /* Eulers - so calculate these for the 'eul' vector, and use euler_rotation curves. */ pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler"); } else if (pchan->rotmode == ROT_MODE_AXISANGLE) { /* TODO: need to figure out how to do this! */ } else { - /* quaternions - use quaternion blending */ + /* Quaternions - use quaternion blending. */ pose_slide_apply_quat(pso, pfl); } } if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) { - /* bbone properties - they all start a "bbone_" prefix */ + /* Bbone properties - they all start a "bbone_" prefix. */ pose_slide_apply_props(pso, pfl, "bbone_"); } @@ -1055,28 +1095,35 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) } } - /* depsgraph updates + redraws */ + /* Depsgraph updates + redraws. */ pose_slide_refresh(C, pso); } -/* perform auto-key-framing after changes were made + confirmed */ +/** + * Perform auto-key-framing after changes were made + confirmed. + */ static void pose_slide_autoKeyframe(bContext *C, tPoseSlideOp *pso) { - /* wrapper around the generic call */ + /* Wrapper around the generic call. */ poseAnim_mapping_autoKeyframe(C, pso->scene, &pso->pfLinks, (float)pso->cframe); } -/* reset changes made to current pose */ +/** + * Reset changes made to current pose. + */ static void pose_slide_reset(tPoseSlideOp *pso) { - /* wrapper around the generic call, so that custom stuff can be added later */ + /* Wrapper around the generic call, so that custom stuff can be added later. */ poseAnim_mapping_reset(&pso->pfLinks); } /* ------------------------------------ */ -/* Draw percentage indicator in workspace footer. */ -/* TODO: Include hints about locks here... */ +/** + * Draw percentage indicator in status-bar. + * + * TODO: Include hints about locks here. + */ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) { char status_str[UI_MAX_DRAW_STR]; @@ -1100,25 +1147,25 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) break; default: - /* unknown */ + /* Unknown. */ strcpy(mode_str, TIP_("Sliding-Tool")); break; } switch (pso->axislock) { case PS_LOCK_X: - BLI_strncpy(axis_str, TIP_("[X]/Y/Z axis only (X to clear)"), sizeof(axis_str)); + STRNCPY(axis_str, TIP_("[X]/Y/Z axis only (X to clear)")); break; case PS_LOCK_Y: - BLI_strncpy(axis_str, TIP_("X/[Y]/Z axis only (Y to clear)"), sizeof(axis_str)); + STRNCPY(axis_str, TIP_("X/[Y]/Z axis only (Y to clear)")); break; case PS_LOCK_Z: - BLI_strncpy(axis_str, TIP_("X/Y/[Z] axis only (Z to clear)"), sizeof(axis_str)); + STRNCPY(axis_str, TIP_("X/Y/[Z] axis only (Z to clear)")); break; default: if (ELEM(pso->channels, PS_TFM_LOC, PS_TFM_ROT, PS_TFM_SIZE)) { - BLI_strncpy(axis_str, TIP_("X/Y/Z = Axis Constraint"), sizeof(axis_str)); + STRNCPY(axis_str, TIP_("X/Y/Z = Axis Constraint")); } else { axis_str[0] = '\0'; @@ -1146,43 +1193,38 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) axis_str); break; case PS_TFM_BBONE_SHAPE: - BLI_strncpy(limits_str, - TIP_("G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s"), - sizeof(limits_str)); + STRNCPY(limits_str, TIP_("G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s")); break; case PS_TFM_PROPS: - BLI_strncpy(limits_str, - TIP_("G/R/S/B/[C] - Custom Properties only (C to clear) | %s"), - sizeof(limits_str)); + STRNCPY(limits_str, TIP_("G/R/S/B/[C] - Custom Properties only (C to clear) | %s")); break; default: - BLI_strncpy( - limits_str, TIP_("G/R/S/B/C - Limit to Transform/Property Set"), sizeof(limits_str)); + STRNCPY(limits_str, TIP_("G/R/S/B/C - Limit to Transform/Property Set")); break; } if (pso->overshoot) { - BLI_strncpy(overshoot_str, TIP_("[E] - Disable overshoot"), sizeof(overshoot_str)); + STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot")); } else { - BLI_strncpy(overshoot_str, TIP_("E - Enable overshoot"), sizeof(overshoot_str)); + STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot")); } if (pso->precision) { - BLI_strncpy(precision_str, TIP_("[Shift] - Precision active"), sizeof(precision_str)); + STRNCPY(precision_str, TIP_("[Shift] - Precision active")); } else { - BLI_strncpy(precision_str, TIP_("Shift - Hold for precision"), sizeof(precision_str)); + STRNCPY(precision_str, TIP_("Shift - Hold for precision")); } if (pso->increments) { - BLI_strncpy(increments_str, TIP_("[Ctrl] - Increments active"), sizeof(increments_str)); + STRNCPY(increments_str, TIP_("[Ctrl] - Increments active")); } else { - BLI_strncpy(increments_str, TIP_("Ctrl - Hold for 10% increments"), sizeof(increments_str)); + STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments")); } - BLI_strncpy(bone_vis_str, TIP_("[H] - Toggle bone visibility"), sizeof(increments_str)); + STRNCPY(bone_vis_str, TIP_("[H] - Toggle bone visibility")); if (hasNumInput(&pso->num)) { Scene *scene = pso->scene; @@ -1208,57 +1250,59 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) ED_area_status_text(pso->area, ""); } -/* common code for invoke() methods */ +/** + * Common code for invoke() methods. + */ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso) { tPChanFCurveLink *pfl; wmWindow *win = CTX_wm_window(C); - /* for each link, add all its keyframes to the search tree */ + /* For each link, add all its keyframes to the search tree. */ for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { LinkData *ld; - /* do this for each F-Curve */ + /* Do this for each F-Curve. */ for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0); } } - /* cancel if no keyframes found... */ + /* Cancel if no keyframes found. */ if (pso->keys.root) { ActKeyColumn *ak; float cframe = (float)pso->cframe; - /* firstly, check if the current frame is a keyframe... */ + /* Firstly, check if the current frame is a keyframe. */ ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe); if (ak == NULL) { - /* current frame is not a keyframe, so search */ + /* Current frame is not a keyframe, so search. */ ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev( &pso->keys, compare_ak_cfraPtr, &cframe); ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next( &pso->keys, compare_ak_cfraPtr, &cframe); - /* new set the frames */ - /* prev frame */ + /* New set the frames. */ + /* Prev frame. */ pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); - /* next frame */ + /* Next frame. */ pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1); RNA_int_set(op->ptr, "next_frame", pso->nextFrame); } else { - /* current frame itself is a keyframe, so just take keyframes on either side */ - /* prev frame */ + /* Current frame itself is a keyframe, so just take keyframes on either side. */ + /* Prev frame. */ pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); - /* next frame */ + /* Next frame. */ pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1); RNA_int_set(op->ptr, "next_frame", pso->nextFrame); } - /* apply NLA mapping corrections so the frame lookups work */ + /* Apply NLA mapping corrections so the frame look-ups work. */ for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; if (ob_data->valid) { @@ -1275,8 +1319,8 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p return OPERATOR_CANCELLED; } - /* initial apply for operator... */ - /* TODO: need to calculate percentage for initial round too... */ + /* Initial apply for operator. */ + /* TODO: need to calculate factor for initial round too. */ if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) { pose_slide_apply(C, pso); } @@ -1284,16 +1328,16 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p pose_slide_rest_pose_apply(C, pso); } - /* depsgraph updates + redraws */ + /* Depsgraph updates + redraws. */ pose_slide_refresh(C, pso); - /* set cursor to indicate modal */ + /* Set cursor to indicate modal. */ WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL); - /* header print */ + /* Header print. */ pose_slide_draw_status(C, pso); - /* add a modal handler for this operator */ + /* Add a modal handler for this operator. */ WM_event_add_modal_handler(C, op); /* Hide Bone Overlay. */ @@ -1304,31 +1348,32 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p return OPERATOR_RUNNING_MODAL; } -/* Calculate percentage based on mouse movement, clamp or round to increments if - * enabled by the user. Store the new percentage value. +/** + * Calculate factor based on mouse movement, clamp or round to increments if + * enabled by the user. Store the new factor value. */ -static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso, - wmOperator *op, - const wmEvent *event) +static void pose_slide_mouse_update_factor(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event) { - const float percentage_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE)); - /* Reduced percentage delta in precision mode (shift held). */ - pso->raw_percentage += pso->precision ? (percentage_delta / 8) : percentage_delta; - pso->percentage = pso->raw_percentage; + const float factor_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE)); + /* Reduced factor delta in precision mode (shift held). */ + pso->raw_factor += pso->precision ? (factor_delta / 8) : factor_delta; + pso->factor = pso->raw_factor; pso->last_cursor_x = event->x; if (!pso->overshoot) { - pso->percentage = clamp_f(pso->percentage, 0, 1); + pso->factor = clamp_f(pso->factor, 0, 1); } if (pso->increments) { - pso->percentage = round(pso->percentage * 10) / 10; + pso->factor = round(pso->factor * 10) / 10; } - RNA_float_set(op->ptr, "percentage", pso->percentage); + RNA_float_set(op->ptr, "factor", pso->factor); } -/* handle an event to toggle channels mode */ +/** + * Handle an event to toggle channels mode. + */ static void pose_slide_toggle_channels_mode(wmOperator *op, tPoseSlideOp *pso, ePoseSlide_Channels channel) @@ -1349,7 +1394,9 @@ static void pose_slide_toggle_channels_mode(wmOperator *op, RNA_enum_set(op->ptr, "axis_lock", pso->axislock); } -/* handle an event to toggle axis locks - returns whether any change in state is needed */ +/** + * Handle an event to toggle axis locks - returns whether any change in state is needed. + */ static bool pose_slide_toggle_axis_locks(wmOperator *op, tPoseSlideOp *pso, ePoseSlide_AxisLock axis) @@ -1376,7 +1423,9 @@ static bool pose_slide_toggle_axis_locks(wmOperator *op, return true; } -/* common code for modal() */ +/** + * Operator `modal()` callback. + */ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) { tPoseSlideOp *pso = op->customdata; @@ -1386,11 +1435,11 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) const bool has_numinput = hasNumInput(&pso->num); switch (event->type) { - case LEFTMOUSE: /* confirm */ + case LEFTMOUSE: /* Confirm. */ case EVT_RETKEY: case EVT_PADENTER: { if (event->val == KM_PRESS) { - /* return to normal cursor and header status */ + /* Return to normal cursor and header status. */ ED_workspace_status_text(C, NULL); ED_area_status_text(pso->area, NULL); WM_cursor_modal_restore(win); @@ -1398,48 +1447,48 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Depsgraph updates + redraws. Redraw needed to remove UI. */ pose_slide_refresh(C, pso); - /* insert keyframes as required... */ + /* Insert keyframes as required. */ pose_slide_autoKeyframe(C, pso); pose_slide_exit(op); - /* done! */ + /* Done! */ return OPERATOR_FINISHED; } break; } - case EVT_ESCKEY: /* cancel */ + case EVT_ESCKEY: /* Cancel. */ case RIGHTMOUSE: { if (event->val == KM_PRESS) { - /* return to normal cursor and header status */ + /* Return to normal cursor and header status. */ ED_workspace_status_text(C, NULL); ED_area_status_text(pso->area, NULL); WM_cursor_modal_restore(win); - /* reset transforms back to original state */ + /* Reset transforms back to original state. */ pose_slide_reset(pso); /* Depsgraph updates + redraws.*/ pose_slide_refresh(C, pso); - /* clean up temp data */ + /* Clean up temp data. */ pose_slide_exit(op); - /* canceled! */ + /* Canceled! */ return OPERATOR_CANCELLED; } break; } - /* Percentage Change... */ - case MOUSEMOVE: /* calculate new position */ + /* Factor Change... */ + case MOUSEMOVE: /* Calculate new position. */ { - /* only handle mousemove if not doing numinput */ + /* Only handle mouse-move if not doing numinput. */ if (has_numinput == false) { - /* update percentage based on position of mouse */ - pose_slide_mouse_update_percentage(pso, op, event); + /* Update factor based on position of mouse. */ + pose_slide_mouse_update_factor(pso, op, event); - /* update pose to reflect the new values (see below) */ + /* Update pose to reflect the new values (see below). */ do_pose_update = true; } break; @@ -1451,12 +1500,12 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Grab percentage from numeric input, and store this new value for redo * NOTE: users see ints, while internally we use a 0-1 float */ - value = pso->percentage * 100.0f; + value = pso->factor * 100.0f; applyNumInput(&pso->num, &value); - pso->percentage = value / 100.0f; - CLAMP(pso->percentage, 0.0f, 1.0f); - RNA_float_set(op->ptr, "percentage", pso->percentage); + pso->factor = value / 100.0f; + CLAMP(pso->factor, 0.0f, 1.0f); + RNA_float_set(op->ptr, "factor", pso->factor); /* Update pose to reflect the new values (see below) */ do_pose_update = true; @@ -1571,8 +1620,8 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else { - /* unhandled event - maybe it was some view manipulation? */ - /* allow to pass through */ + /* Unhandled event - maybe it was some view manipulation? */ + /* Allow to pass through. */ return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; } } @@ -1581,13 +1630,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Perform pose updates - in response to some user action * (e.g. pressing a key or moving the mouse). */ if (do_pose_update) { - /* update percentage indicator in header */ + /* Update percentage indicator in header. */ pose_slide_draw_status(C, pso); - /* reset transforms (to avoid accumulation errors) */ + /* Reset transforms (to avoid accumulation errors). */ pose_slide_reset(pso); - /* apply... */ + /* Apply. */ if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) { pose_slide_apply(C, pso); } @@ -1596,21 +1645,25 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) } } - /* still running... */ + /* Still running. */ return OPERATOR_RUNNING_MODAL; } -/* common code for cancel() */ +/** + * Common code for cancel() + */ static void pose_slide_cancel(bContext *UNUSED(C), wmOperator *op) { - /* cleanup and done */ + /* Cleanup and done. */ pose_slide_exit(op); } -/* common code for exec() methods */ +/** + * Common code for exec() methods. + */ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso) { - /* settings should have been set up ok for applying, so just apply! */ + /* Settings should have been set up ok for applying, so just apply! */ if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) { pose_slide_apply(C, pso); } @@ -1618,10 +1671,10 @@ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso pose_slide_rest_pose_apply(C, pso); } - /* insert keyframes if needed */ + /* Insert keyframes if needed. */ pose_slide_autoKeyframe(C, pso); - /* cleanup and done */ + /* Cleanup and done. */ pose_slide_exit(op); return OPERATOR_FINISHED; @@ -1635,11 +1688,11 @@ static void pose_slide_opdef_properties(wmOperatorType *ot) PropertyRNA *prop; prop = RNA_def_float_factor(ot->srna, - "percentage", + "factor", 0.5f, 0.0f, 1.0f, - "Percentage", + "Factor", "Weighting factor for which keyframe is favored more", 0.0, 1.0); @@ -1685,12 +1738,14 @@ static void pose_slide_opdef_properties(wmOperatorType *ot) /* ------------------------------------ */ -/* invoke() - for 'push from breakdown' mode */ +/** + * Operator `invoke()` callback for 'push from breakdown' mode. + */ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *event) { tPoseSlideOp *pso; - /* initialize data */ + /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1700,19 +1755,21 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev pso->last_cursor_x = event->x; - /* Initialize percentage so that it won't pop on first mouse move. */ - pose_slide_mouse_update_percentage(pso, op, event); + /* Initialize factor so that it won't pop on first mouse move. */ + pose_slide_mouse_update_factor(pso, op, event); - /* do common setup work */ + /* Do common setup work. */ return pose_slide_invoke_common(C, op, pso); } -/* exec() - for push */ +/** + * Operator `exec()` callback - for push. + */ static int pose_slide_push_exec(bContext *C, wmOperator *op) { tPoseSlideOp *pso; - /* initialize data (from RNA-props) */ + /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1720,7 +1777,7 @@ static int pose_slide_push_exec(bContext *C, wmOperator *op) pso = op->customdata; - /* do common exec work */ + /* Do common exec work. */ return pose_slide_exec_common(C, op, pso); } @@ -1747,12 +1804,14 @@ void POSE_OT_push(wmOperatorType *ot) /* ........................ */ -/* invoke() - for 'relax to breakdown' mode */ +/** + * Invoke callback - for 'relax to breakdown' mode. + */ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *event) { tPoseSlideOp *pso; - /* initialize data */ + /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1762,19 +1821,21 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e pso->last_cursor_x = event->x; - /* Initialize percentage so that it won't pop on first mouse move. */ - pose_slide_mouse_update_percentage(pso, op, event); + /* Initialize factor so that it won't pop on first mouse move. */ + pose_slide_mouse_update_factor(pso, op, event); - /* do common setup work */ + /* Do common setup work. */ return pose_slide_invoke_common(C, op, pso); } -/* exec() - for relax */ +/** + * Operator exec() - for relax. + */ static int pose_slide_relax_exec(bContext *C, wmOperator *op) { tPoseSlideOp *pso; - /* initialize data (from RNA-props) */ + /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1782,7 +1843,7 @@ static int pose_slide_relax_exec(bContext *C, wmOperator *op) pso = op->customdata; - /* do common exec work */ + /* Do common exec work. */ return pose_slide_exec_common(C, op, pso); } @@ -1808,12 +1869,14 @@ void POSE_OT_relax(wmOperatorType *ot) } /* ........................ */ -/* invoke() - for 'push from rest pose' mode */ +/** + * Operator `invoke()` - for 'push from rest pose' mode. + */ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event) { tPoseSlideOp *pso; - /* initialize data */ + /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1823,19 +1886,21 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven pso->last_cursor_x = event->x; - /* Initialize percentage so that it won't pop on first mouse move. */ - pose_slide_mouse_update_percentage(pso, op, event); + /* Initialize factor so that it won't pop on first mouse move. */ + pose_slide_mouse_update_factor(pso, op, event); /* do common setup work */ return pose_slide_invoke_common(C, op, pso); } -/* exec() - for push */ +/** + * Operator `exec()` - for push. + */ static int pose_slide_push_rest_exec(bContext *C, wmOperator *op) { tPoseSlideOp *pso; - /* initialize data (from RNA-props) */ + /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1843,7 +1908,7 @@ static int pose_slide_push_rest_exec(bContext *C, wmOperator *op) pso = op->customdata; - /* do common exec work */ + /* Do common exec work. */ return pose_slide_exec_common(C, op, pso); } @@ -1870,12 +1935,14 @@ void POSE_OT_push_rest(wmOperatorType *ot) /* ........................ */ -/* invoke() - for 'relax' mode */ +/** + * Operator `invoke()` - for 'relax' mode. + */ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event) { tPoseSlideOp *pso; - /* initialize data */ + /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1885,19 +1952,21 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve pso->last_cursor_x = event->x; - /* Initialize percentage so that it won't pop on first mouse move. */ - pose_slide_mouse_update_percentage(pso, op, event); + /* Initialize factor so that it won't pop on first mouse move. */ + pose_slide_mouse_update_factor(pso, op, event); - /* do common setup work */ + /* Do common setup work. */ return pose_slide_invoke_common(C, op, pso); } -/* exec() - for relax */ +/** + * Operator `exec()` - for relax. + */ static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op) { tPoseSlideOp *pso; - /* initialize data (from RNA-props) */ + /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1905,7 +1974,7 @@ static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op) pso = op->customdata; - /* do common exec work */ + /* Do common exec work. */ return pose_slide_exec_common(C, op, pso); } @@ -1932,12 +2001,14 @@ void POSE_OT_relax_rest(wmOperatorType *ot) /* ........................ */ -/* invoke() - for 'breakdown' mode */ +/** + * Operator `invoke()` - for 'breakdown' mode. + */ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event) { tPoseSlideOp *pso; - /* initialize data */ + /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1947,19 +2018,21 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven pso->last_cursor_x = event->x; - /* Initialize percentage so that it won't pop on first mouse move. */ - pose_slide_mouse_update_percentage(pso, op, event); + /* Initialize factor so that it won't pop on first mouse move. */ + pose_slide_mouse_update_factor(pso, op, event); - /* do common setup work */ + /* Do common setup work. */ return pose_slide_invoke_common(C, op, pso); } -/* exec() - for breakdown */ +/** + * Operator exec() - for breakdown. + */ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op) { tPoseSlideOp *pso; - /* initialize data (from RNA-props) */ + /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { pose_slide_exit(op); return OPERATOR_CANCELLED; @@ -1967,7 +2040,7 @@ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op) pso = op->customdata; - /* do common exec work */ + /* Do common exec work. */ return pose_slide_exec_common(C, op, pso); } @@ -1997,36 +2070,39 @@ void POSE_OT_breakdown(wmOperatorType *ot) /* "termination conditions" - i.e. when we stop */ typedef enum ePosePropagate_Termination { - /* stop after the current hold ends */ + /** Stop after the current hold ends. */ POSE_PROPAGATE_SMART_HOLDS = 0, - /* only do on the last keyframe */ + /** Only do on the last keyframe. */ POSE_PROPAGATE_LAST_KEY, - /* stop after the next keyframe */ + /** Stop after the next keyframe. */ POSE_PROPAGATE_NEXT_KEY, - /* stop after the specified frame */ + /** Stop after the specified frame. */ POSE_PROPAGATE_BEFORE_FRAME, - /* stop when we run out of keyframes */ + /** Stop when we run out of keyframes. */ POSE_PROPAGATE_BEFORE_END, - /* only do on keyframes that are selected */ + /** Only do on keyframes that are selected. */ POSE_PROPAGATE_SELECTED_KEYS, - /* only do on the frames where markers are selected */ + /** Only do on the frames where markers are selected. */ POSE_PROPAGATE_SELECTED_MARKERS, } ePosePropagate_Termination; -/* Termination data needed for some modes - - * assumes only one of these entries will be needed at a time. */ +/** + * Termination data needed for some modes - + * assumes only one of these entries will be needed at a time. + */ typedef union tPosePropagate_ModeData { - /* smart holds + before frame: frame number to stop on */ + /** Smart holds + before frame: frame number to stop on. */ float end_frame; - /* selected markers: listbase for CfraElem's marking these frames */ + /** Selected markers: listbase for CfraElem's marking these frames. */ ListBase sel_markers; } tPosePropagate_ModeData; /* --------------------------------- */ -/* get frame on which the "hold" for the bone ends +/** + * Get frame on which the "hold" for the bone ends. * XXX: this may not really work that well if a bone moves on some channels and not others * if this happens to be a major issue, scrap this, and just make this happen * independently per F-Curve @@ -2040,7 +2116,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st LinkData *ld; float endFrame = startFrame; - /* set up optimized data-structures for searching for relevant keyframes + holds */ + /* Set up optimized data-structures for searching for relevant keyframes + holds. */ BLI_dlrbTree_init(&keys); for (ld = pfl->fcurves.first; ld; ld = ld->next) { @@ -2048,7 +2124,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st fcurve_to_keylist(adt, fcu, &keys, 0); } - /* find the long keyframe (i.e. hold), and hence obtain the endFrame value + /* Find the long keyframe (i.e. hold), and hence obtain the endFrame value * - the best case would be one that starts on the frame itself */ ActKeyColumn *ab = (ActKeyColumn *)BLI_dlrbTree_search_exact( @@ -2062,67 +2138,68 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st * otherwise forget it, as we'd be overwriting some valid data. */ if (ab == NULL) { - /* we've got case 1, so try the one after */ + /* We've got case 1, so try the one after. */ ab = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &startFrame); if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { - /* try the block before this frame then as last resort */ + /* Try the block before this frame then as last resort. */ ab = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &startFrame); } } - /* whatever happens, stop searching now... */ + /* Whatever happens, stop searching now.... */ if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { - /* restrict range to just the frame itself - * i.e. everything is in motion, so no holds to safely overwrite - */ + /* Restrict range to just the frame itself + * i.e. everything is in motion, so no holds to safely overwrite. */ ab = NULL; } - /* check if we can go any further than we've already gone */ + /* Check if we can go any further than we've already gone. */ if (ab) { - /* go to next if it is also valid and meets "extension" criteria */ + /* Go to next if it is also valid and meets "extension" criteria. */ while (ab->next) { ActKeyColumn *abn = ab->next; - /* must be valid */ + /* Must be valid. */ if ((actkeyblock_get_valid_hold(abn) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { break; } - /* should have the same number of curves */ + /* Should have the same number of curves. */ if (ab->totblock != abn->totblock) { break; } - /* we can extend the bounds to the end of this "next" block now */ + /* We can extend the bounds to the end of this "next" block now. */ ab = abn; } - /* end frame can now take the value of the end of the block */ + /* End frame can now take the value of the end of the block. */ endFrame = ab->next->cfra; } - /* free temp memory */ + /* Free temp memory. */ BLI_dlrbTree_free(&keys); - /* return the end frame we've found */ + /* Return the end frame we've found. */ return endFrame; } -/* get reference value from F-Curve using RNA */ +/** + * Get reference value from F-Curve using RNA. + */ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value) { PointerRNA id_ptr, ptr; PropertyRNA *prop; bool found = false; - /* base pointer is always the object -> id_ptr */ + /* Base pointer is always the `object -> id_ptr`. */ RNA_id_pointer_create(&ob->id, &id_ptr); - /* resolve the property... */ + /* Resolve the property. */ if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) { if (RNA_property_array_check(prop)) { - /* array */ + /* Array. */ if (fcu->array_index < RNA_property_array_length(&ptr, prop)) { found = true; switch (RNA_property_type(prop)) { @@ -2142,7 +2219,7 @@ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value) } } else { - /* not an array */ + /* Not an array. */ found = true; switch (RNA_property_type(prop)) { case PROP_BOOLEAN: @@ -2167,7 +2244,9 @@ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value) return found; } -/* propagate just works along each F-Curve in turn */ +/** + * Propagate just works along each F-Curve in turn. + */ static void pose_propagate_fcurve( wmOperator *op, Object *ob, FCurve *fcu, float startFrame, tPosePropagate_ModeData modeData) { @@ -2176,31 +2255,31 @@ static void pose_propagate_fcurve( BezTriple *bezt; float refVal = 0.0f; bool keyExists; - int i, match; + int i; bool first = true; - /* skip if no keyframes to edit */ + /* Skip if no keyframes to edit. */ if ((fcu->bezt == NULL) || (fcu->totvert < 2)) { return; } - /* find the reference value from bones directly, which means that the user + /* Find the reference value from bones directly, which means that the user * doesn't need to firstly keyframe the pose (though this doesn't mean that - * they can't either) - */ + * they can't either). */ if (!pose_propagate_get_refVal(ob, fcu, &refVal)) { return; } - /* find the first keyframe to start propagating from + /* Find the first keyframe to start propagating from: * - if there's a keyframe on the current frame, we probably want to save this value there too - * since it may be as of yet unkeyed + * since it may be as of yet un-keyed * - if starting before the starting frame, don't touch the key, as it may have had some valid * values * - if only doing selected keyframes, start from the first one */ if (mode != POSE_PROPAGATE_SELECTED_KEYS) { - match = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, startFrame, fcu->totvert, &keyExists); + const int match = BKE_fcurve_bezt_binarysearch_index( + fcu->bezt, startFrame, fcu->totvert, &keyExists); if (fcu->bezt[match].vec[1][0] < startFrame) { i = match + 1; @@ -2210,58 +2289,58 @@ static void pose_propagate_fcurve( } } else { - /* selected - start from first keyframe */ + /* Selected - start from first keyframe. */ i = 0; } for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) { - /* additional termination conditions based on the operator 'mode' property go here... */ + /* Additional termination conditions based on the operator 'mode' property go here. */ if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) { - /* stop if keyframe is outside the accepted range */ + /* Stop if keyframe is outside the accepted range. */ if (bezt->vec[1][0] > modeData.end_frame) { break; } } else if (mode == POSE_PROPAGATE_NEXT_KEY) { - /* stop after the first keyframe has been processed */ + /* Stop after the first keyframe has been processed. */ if (first == false) { break; } } else if (mode == POSE_PROPAGATE_LAST_KEY) { - /* only affect this frame if it will be the last one */ + /* Only affect this frame if it will be the last one. */ if (i != (fcu->totvert - 1)) { continue; } } else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { - /* only allow if there's a marker on this frame */ + /* Only allow if there's a marker on this frame. */ CfraElem *ce = NULL; - /* stop on matching marker if there is one */ + /* Stop on matching marker if there is one. */ for (ce = modeData.sel_markers.first; ce; ce = ce->next) { if (ce->cfra == round_fl_to_int(bezt->vec[1][0])) { break; } } - /* skip this keyframe if no marker */ + /* Skip this keyframe if no marker. */ if (ce == NULL) { continue; } } else if (mode == POSE_PROPAGATE_SELECTED_KEYS) { - /* only allow if this keyframe is already selected - skip otherwise */ + /* Only allow if this keyframe is already selected - skip otherwise. */ if (BEZT_ISSEL_ANY(bezt) == 0) { continue; } } - /* just flatten handles, since values will now be the same either side... */ + /* Just flatten handles, since values will now be the same either side. */ /* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */ bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal; - /* select keyframe to indicate that it's been changed */ + /* Select keyframe to indicate that it's been changed. */ bezt->f2 |= SELECT; first = false; } @@ -2281,7 +2360,7 @@ static int pose_propagate_exec(bContext *C, wmOperator *op) tPosePropagate_ModeData modeData; const int mode = RNA_enum_get(op->ptr, "mode"); - /* isolate F-Curves related to the selected bones */ + /* Isolate F-Curves related to the selected bones. */ poseAnim_mapping_get(C, &pflinks); if (BLI_listbase_is_empty(&pflinks)) { @@ -2292,42 +2371,41 @@ static int pose_propagate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* mode-specific data preprocessing (requiring no access to curves) */ + /* Mode-specific data preprocessing (requiring no access to curves). */ if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { - /* get a list of selected markers */ + /* Get a list of selected markers. */ ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT); } else { - /* assume everything else wants endFrame */ + /* Assume everything else wants endFrame. */ modeData.end_frame = RNA_float_get(op->ptr, "end_frame"); } - /* for each bone, perform the copying required */ + /* For each bone, perform the copying required. */ for (pfl = pflinks.first; pfl; pfl = pfl->next) { LinkData *ld; - /* mode-specific data preprocessing (requiring access to all curves) */ + /* Mode-specific data preprocessing (requiring access to all curves). */ if (mode == POSE_PROPAGATE_SMART_HOLDS) { - /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting - * from the keyframe that occurs after the current frame - */ + /* We store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting + * from the keyframe that occurs after the current frame. */ modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)CFRA); } - /* go through propagating pose to keyframes, curve by curve */ + /* Go through propagating pose to keyframes, curve by curve. */ for (ld = pfl->fcurves.first; ld; ld = ld->next) { pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)CFRA, modeData); } } - /* free temp data */ + /* Free temp data. */ poseAnim_mapping_free(&pflinks); if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { BLI_freelistN(&modeData.sel_markers); } - /* updates + notifiers */ + /* Updates + notifiers. */ FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { poseAnim_mapping_refresh(C, scene, ob); } diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 43ab20eb71c..6466773daac 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -144,26 +144,25 @@ static void applyarmature_transfer_properties(EditBone *curbone, if (pchan->bone->segments > 1) { /* Combine rest/pose values. */ curbone->curve_in_x += pchan_eval->curve_in_x; - curbone->curve_in_y += pchan_eval->curve_in_y; + curbone->curve_in_z += pchan_eval->curve_in_z; curbone->curve_out_x += pchan_eval->curve_out_x; - curbone->curve_out_y += pchan_eval->curve_out_y; + curbone->curve_out_z += pchan_eval->curve_out_z; curbone->roll1 += pchan_eval->roll1; curbone->roll2 += pchan_eval->roll2; curbone->ease1 += pchan_eval->ease1; curbone->ease2 += pchan_eval->ease2; - curbone->scale_in_x *= pchan_eval->scale_in_x; - curbone->scale_in_y *= pchan_eval->scale_in_y; - curbone->scale_out_x *= pchan_eval->scale_out_x; - curbone->scale_out_y *= pchan_eval->scale_out_y; + mul_v3_v3(curbone->scale_in, pchan_eval->scale_in); + mul_v3_v3(curbone->scale_out, pchan_eval->scale_out); /* Reset pose values. */ pchan->curve_in_x = pchan->curve_out_x = 0.0f; - pchan->curve_in_y = pchan->curve_out_y = 0.0f; + pchan->curve_in_z = pchan->curve_out_z = 0.0f; pchan->roll1 = pchan->roll2 = 0.0f; pchan->ease1 = pchan->ease2 = 0.0f; - pchan->scale_in_x = pchan->scale_in_y = 1.0f; - pchan->scale_out_x = pchan->scale_out_y = 1.0f; + + copy_v3_fl(pchan->scale_in, 1.0f); + copy_v3_fl(pchan->scale_out, 1.0f); } /* Clear transform values for pchan. */ @@ -699,18 +698,17 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, /* B-Bone posing options should also be included... */ pchan->curve_in_x = chan->curve_in_x; - pchan->curve_in_y = chan->curve_in_y; + pchan->curve_in_z = chan->curve_in_z; pchan->curve_out_x = chan->curve_out_x; - pchan->curve_out_y = chan->curve_out_y; + pchan->curve_out_z = chan->curve_out_z; pchan->roll1 = chan->roll1; pchan->roll2 = chan->roll2; pchan->ease1 = chan->ease1; pchan->ease2 = chan->ease2; - pchan->scale_in_x = chan->scale_in_x; - pchan->scale_in_y = chan->scale_in_y; - pchan->scale_out_x = chan->scale_out_x; - pchan->scale_out_y = chan->scale_out_y; + + copy_v3_v3(pchan->scale_in, chan->scale_in); + copy_v3_v3(pchan->scale_out, chan->scale_out); /* paste flipped pose? */ if (flip) { @@ -972,8 +970,9 @@ static void pchan_clear_scale(bPoseChannel *pchan) pchan->ease1 = 0.0f; pchan->ease2 = 0.0f; - pchan->scale_in_x = pchan->scale_in_y = 1.0f; - pchan->scale_out_x = pchan->scale_out_y = 1.0f; + + copy_v3_fl(pchan->scale_in, 1.0f); + copy_v3_fl(pchan->scale_out, 1.0f); } /* Clear the scale. When X-mirror is enabled, * also clear the scale of the mirrored pose channel. */ @@ -1136,9 +1135,9 @@ static void pchan_clear_rot(bPoseChannel *pchan) pchan->roll2 = 0.0f; pchan->curve_in_x = 0.0f; - pchan->curve_in_y = 0.0f; + pchan->curve_in_z = 0.0f; pchan->curve_out_x = 0.0f; - pchan->curve_out_y = 0.0f; + pchan->curve_out_z = 0.0f; } /* Clear the rotation. When X-mirror is enabled, * also clear the rotation of the mirrored pose channel. */ diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index 75348c2b196..8eae5288f7a 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -116,15 +116,14 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, pfl->roll1 = pchan->roll1; pfl->roll2 = pchan->roll2; pfl->curve_in_x = pchan->curve_in_x; - pfl->curve_in_y = pchan->curve_in_y; + pfl->curve_in_z = pchan->curve_in_z; pfl->curve_out_x = pchan->curve_out_x; - pfl->curve_out_y = pchan->curve_out_y; + pfl->curve_out_z = pchan->curve_out_z; pfl->ease1 = pchan->ease1; pfl->ease2 = pchan->ease2; - pfl->scale_in_x = pchan->scale_in_x; - pfl->scale_in_y = pchan->scale_in_y; - pfl->scale_out_x = pchan->scale_out_x; - pfl->scale_out_y = pchan->scale_out_y; + + copy_v3_v3(pfl->scale_in, pchan->scale_in); + copy_v3_v3(pfl->scale_out, pchan->scale_out); /* make copy of custom properties */ if (pchan->prop && (transFlags & ACT_TRANS_PROP)) { @@ -251,15 +250,14 @@ void poseAnim_mapping_reset(ListBase *pfLinks) pchan->roll1 = pfl->roll1; pchan->roll2 = pfl->roll2; pchan->curve_in_x = pfl->curve_in_x; - pchan->curve_in_y = pfl->curve_in_y; + pchan->curve_in_z = pfl->curve_in_z; pchan->curve_out_x = pfl->curve_out_x; - pchan->curve_out_y = pfl->curve_out_y; + pchan->curve_out_z = pfl->curve_out_z; pchan->ease1 = pfl->ease1; pchan->ease2 = pfl->ease2; - pchan->scale_in_x = pfl->scale_in_x; - pchan->scale_in_y = pfl->scale_in_y; - pchan->scale_out_x = pfl->scale_out_x; - pchan->scale_out_y = pfl->scale_out_y; + + copy_v3_v3(pchan->scale_in, pfl->scale_in); + copy_v3_v3(pchan->scale_out, pfl->scale_out); /* just overwrite values of properties from the stored copies (there should be some) */ if (pfl->oldprops) { diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 535ccaa06fd..2999ac784ba 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -6646,7 +6646,7 @@ void CURVE_OT_dissolve_verts(wmOperatorType *ot) static bool nurb_bezt_flag_any(const Nurb *nu, const char flag_test) { - BezTriple *bezt = nu->bezt; + const BezTriple *bezt; int i; for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) { diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index e4f2de1f741..febcf83116b 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -128,6 +128,7 @@ struct CurveDrawData { } prev; ViewContext vc; + ViewDepths *depths; enum { CURVE_DRAW_IDLE = 0, CURVE_DRAW_PAINTING = 1, @@ -188,7 +189,6 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd, float r_normal_world[3]) { ARegion *region = cdd->vc.region; - RegionView3D *rv3d = cdd->vc.rv3d; bool is_location_world_set = false; @@ -204,7 +204,7 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd, } } else { - const ViewDepths *depths = rv3d->depths; + const ViewDepths *depths = cdd->depths; if (depths && ((uint)mval_i[0] < depths->w) && ((uint)mval_i[1] < depths->h)) { float depth_fl = 1.0f; ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl); @@ -219,7 +219,7 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd, if (surface_offset != 0.0f) { const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius; float normal[3]; - if (ED_view3d_depth_read_cached_normal(&cdd->vc, mval_i, normal)) { + if (ED_view3d_depth_read_cached_normal(region, depths, mval_i, normal)) { madd_v3_v3fl(r_location_world, normal, offset * surface_offset); if (r_normal_world) { copy_v3_v3(r_normal_world, normal); @@ -387,7 +387,6 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), GPU_matrix_translate_3f(selem->location_local[0] - location_prev[0], selem->location_local[1] - location_prev[1], selem->location_local[2] - location_prev[2]); - location_prev = selem->location_local; const float radius = stroke_elem_radius(cdd, selem); @@ -529,7 +528,7 @@ static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event) if (ELEM(cps->surface_plane, CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW, CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE)) { - if (ED_view3d_depth_read_cached_normal(&cdd->vc, event->mval, normal)) { + if (ED_view3d_depth_read_cached_normal(cdd->vc.region, cdd->depths, event->mval, normal)) { if (cps->surface_plane == CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW) { float cross_a[3], cross_b[3]; cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal); @@ -623,6 +622,9 @@ static void curve_draw_exit(wmOperator *op) BLI_mempool_destroy(cdd->stroke_elem_pool); } + if (cdd->depths) { + ED_view3d_depths_free(cdd->depths); + } MEM_freeN(cdd); op->customdata = NULL; } @@ -1085,10 +1087,14 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* needed or else the draw matrix can be incorrect */ view3d_operator_needs_opengl(C); - ED_view3d_depth_override( - cdd->vc.depsgraph, cdd->vc.region, cdd->vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, true); + ED_view3d_depth_override(cdd->vc.depsgraph, + cdd->vc.region, + cdd->vc.v3d, + NULL, + V3D_DEPTH_NO_GPENCIL, + &cdd->depths); - if (cdd->vc.rv3d->depths != NULL) { + if (cdd->depths != NULL) { cdd->project.use_depth = true; } else { diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index fe1c5efc747..3131ec70fb0 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -760,15 +760,15 @@ static void annotation_draw_data_all(Scene *scene, int winy, int cfra, int dflag, - const char spacetype) + const eSpace_Type space_type) { bGPdata *gpd_source = NULL; if (scene) { - if (spacetype == SPACE_VIEW3D) { + if (space_type == SPACE_VIEW3D) { gpd_source = (scene->gpd ? scene->gpd : NULL); } - else if (spacetype == SPACE_CLIP && scene->clip) { + else if (space_type == SPACE_CLIP && scene->clip) { /* currently drawing only gpencil data from either clip or track, * but not both - XXX fix logic behind */ gpd_source = (scene->clip->gpd ? scene->clip->gpd : NULL); diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index c155587e95a..e3c6fd8f878 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -667,7 +667,7 @@ static short annotation_stroke_addpoint(tGPsdata *p, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? V3D_DEPTH_GPENCIL_ONLY : V3D_DEPTH_NO_GPENCIL, - false); + NULL); } /* convert screen-coordinates to appropriate coordinates (and store them) */ @@ -1226,7 +1226,7 @@ static void annotation_stroke_doeraser(tGPsdata *p) if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); + ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); } } @@ -1706,7 +1706,7 @@ static void annotation_paint_strokeend(tGPsdata *p) (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? V3D_DEPTH_GPENCIL_ONLY : V3D_DEPTH_NO_GPENCIL, - false); + NULL); } /* check if doing eraser or not */ diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c index 3271096c433..6d6f4bc4b40 100644 --- a/source/blender/editors/gpencil/gpencil_armature.c +++ b/source/blender/editors/gpencil/gpencil_armature.c @@ -462,8 +462,8 @@ static void gpencil_object_vgroup_calc_from_armature(const bContext *C, defbase_add = gpencil_bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb); if (defbase_add) { - /* its possible there are DWeight's outside the range of the current - * objects deform groups, in this case the new groups wont be empty */ + /* It's possible there are DWeights outside the range of the current + * object's deform groups. In this case the new groups won't be empty */ ED_vgroup_data_clamp_range(ob->data, defbase_tot); } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index f29f5187015..21fa3ad3967 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -863,7 +863,7 @@ static void gpencil_duplicate_points(bGPdata *gpd, start_idx = i; } } - else { + if ((start_idx != -1) || (start_idx == gps->totpoints - 1)) { size_t len = 0; /* is this the end of current island yet? @@ -4618,6 +4618,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) /* add layer if not created before */ if (gpl_dst == NULL) { gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false); + BKE_gpencil_layer_copy_settings(gpl, gpl_dst); } /* add frame if not created before */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index f74e211dd65..5c4e2de6aa8 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1373,7 +1373,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(tgpf->win, tgpf->region); ED_view3d_depth_override( - tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); + tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); /* Since strokes are so fine, when using their depth we need a margin * otherwise they might get missed. */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index e40748e5f6e..638994bbc2a 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1744,7 +1744,7 @@ static void gpencil_stroke_doeraser(tGPsdata *p) if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); + ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); } } @@ -2334,7 +2334,7 @@ static void gpencil_paint_strokeend(tGPsdata *p) (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? V3D_DEPTH_GPENCIL_ONLY : V3D_DEPTH_NO_GPENCIL, - false); + NULL); } /* check if doing eraser or not */ diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 5f02bbf0a77..a2b4e5dee64 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -792,7 +792,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? V3D_DEPTH_GPENCIL_ONLY : V3D_DEPTH_NO_GPENCIL, - false); + NULL); depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points"); tGPspoint *ptc = &points2D[0]; diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index e1776988186..55521ac4d15 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -1276,7 +1276,7 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd, pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; bGPDspoint pt_temp; - gpencil_point_to_parent_space(pt_active, diff_mat, &pt_temp); + gpencil_point_to_parent_space(pt, diff_mat, &pt_temp); gpencil_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); /* do boundbox check first */ @@ -1847,7 +1847,7 @@ static bool gpencil_generic_stroke_select(bContext *C, bGPDspoint *pt; int i; bool hit = false; - for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; /* convert point coords to screenspace */ @@ -1889,7 +1889,7 @@ static bool gpencil_generic_stroke_select(bContext *C, mval[0] = (box.xmax + box.xmin) / 2; mval[1] = (box.ymax + box.ymin) / 2; - whole = ED_gpencil_stroke_point_is_inside(gps_active, &gsc, mval, gpstroke_iter.diff_mat); + whole = ED_gpencil_stroke_point_is_inside(gps, &gsc, mval, gpstroke_iter.diff_mat); } /* if stroke mode expand selection. */ @@ -2252,7 +2252,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) int i; /* firstly, check for hit-point */ - for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int xy[2]; bGPDspoint pt2; diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c index 482f7015720..12c38fb2744 100644 --- a/source/blender/editors/gpencil/gpencil_trace_utils.c +++ b/source/blender/editors/gpencil/gpencil_trace_utils.c @@ -281,7 +281,6 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain, mat_mask_idx = ob->totcol - 1; } - potrace_path_t *path = st->plist; int n, *tag; potrace_dpoint_t(*c)[3]; @@ -289,7 +288,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain, * good results using the Potrace data. */ const float scalef = 0.008f * scale; /* Draw each curve. */ - path = st->plist; + potrace_path_t *path = st->plist; while (path != NULL) { n = path->curve.n; tag = path->curve.tag; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index c9ef340b9d3..4da21bd05ee 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -680,7 +680,7 @@ void gpencil_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) view3d_operator_needs_opengl(C); view3d_region_operator_needs_opengl(win, region); - ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); + ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); /* for camera view set the subrect */ if (rv3d->persp == RV3D_CAMOB) { diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h index 9ac6b6c1085..dde26c072d0 100644 --- a/source/blender/editors/include/ED_info.h +++ b/source/blender/editors/include/ED_info.h @@ -27,9 +27,10 @@ extern "C" { #endif struct Main; +struct wmWindowManager; /* info_stats.c */ -void ED_info_stats_clear(struct ViewLayer *view_layer); +void ED_info_stats_clear(struct wmWindowManager *wm, struct ViewLayer *view_layer); const char *ED_info_statusbar_string(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer); @@ -41,6 +42,7 @@ const char *ED_info_statistics_string(struct Main *bmain, void ED_info_draw_stats(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, + struct View3D *v3d_local, int x, int *y, int height); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index b8e9f6e8871..0e2be5eb568 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -36,6 +36,7 @@ struct BMFace; struct BMLoop; struct BMVert; struct BMesh; +struct BMeshNormalsUpdate_Params; struct Base; struct Depsgraph; struct ID; @@ -76,6 +77,8 @@ struct BMFace *EDBM_verts_mirror_get_face(struct BMEditMesh *em, struct BMFace * void EDBM_verts_mirror_cache_clear(struct BMEditMesh *em, struct BMVert *v); void EDBM_verts_mirror_cache_end(struct BMEditMesh *em); +void EDBM_mesh_normals_update_ex(struct BMEditMesh *em, + const struct BMeshNormalsUpdate_Params *params); void EDBM_mesh_normals_update(struct BMEditMesh *em); void EDBM_mesh_clear(struct BMEditMesh *em); @@ -103,7 +106,14 @@ bool EDBM_vert_color_check(struct BMEditMesh *em); bool EDBM_mesh_hide(struct BMEditMesh *em, bool swap); bool EDBM_mesh_reveal(struct BMEditMesh *em, bool select); -void EDBM_update_generic(struct Mesh *me, const bool do_tessellation, const bool is_destructive); +struct EDBMUpdate_Params { + uint calc_looptri : 1; + uint calc_normals : 1; + uint is_destructive : 1; +}; + +void EDBM_update(struct Mesh *me, const struct EDBMUpdate_Params *params); +void EDBM_update_extern(struct Mesh *me, const bool do_tessellation, const bool is_destructive); struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm, const struct Scene *scene, diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 67a50b83bd6..ba65840dc99 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -49,6 +49,11 @@ typedef enum { } NodeBorder; #define NODE_GRID_STEPS 5 +#define NODE_EDGE_PAN_INSIDE_PAD 2 +#define NODE_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for node panning, use whole screen. */ +#define NODE_EDGE_PAN_SPEED_RAMP 1 +#define NODE_EDGE_PAN_MAX_SPEED 40 /* In UI units per second, slower than default. */ +#define NODE_EDGE_PAN_DELAY 1.0f /* space_node.c */ diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 6f88ab4253a..1738c383328 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -295,12 +295,12 @@ void ED_object_add_mesh_props(struct wmOperatorType *ot); bool ED_object_add_generic_get_opts(struct bContext *C, struct wmOperator *op, const char view_align_axis, - float loc[3], - float rot[3], - float scale[3], - bool *enter_editmode, - unsigned short *local_view_bits, - bool *is_view_aligned); + float r_loc[3], + float r_rot[3], + float r_scale[3], + bool *r_enter_editmode, + unsigned short *r_local_view_bits, + bool *r_is_view_aligned); struct Object *ED_object_add_type_with_obdata(struct bContext *C, const int type, diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h index e84298bd9c2..6d0172e724a 100644 --- a/source/blender/editors/include/ED_particle.h +++ b/source/blender/editors/include/ED_particle.h @@ -34,6 +34,7 @@ struct ParticleSystem; struct Scene; struct UndoType; struct ViewLayer; +struct wmGenericUserData; struct bContext; struct rcti; @@ -68,7 +69,11 @@ void PE_update_object(struct Depsgraph *depsgraph, bool PE_mouse_particles( struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); 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); +bool PE_circle_select(struct bContext *C, + struct wmGenericUserData *wm_userdata, + const int sel_op, + const int mval[2], + float rad); int PE_lasso_select(struct bContext *C, const int mcoords[][2], const int mcoords_len, diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index bdd7ec571dc..823050b46f7 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -215,7 +215,7 @@ void ED_screen_restore_temp_type(struct bContext *C, ScrArea *area); ScrArea *ED_screen_full_newspace(struct bContext *C, ScrArea *area, int type); void ED_screen_full_prevspace(struct bContext *C, ScrArea *area); void ED_screen_full_restore(struct bContext *C, ScrArea *area); -ScrArea *ED_screen_state_maximized_create(struct bContext *C); +bScreen *ED_screen_state_maximized_create(struct bContext *C); struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *area, @@ -317,6 +317,7 @@ bool ED_operator_animview_active(struct bContext *C); bool ED_operator_outliner_active(struct bContext *C); bool ED_operator_outliner_active_no_editobject(struct bContext *C); bool ED_operator_file_active(struct bContext *C); +bool ED_operator_spreadsheet_active(struct bContext *C); bool ED_operator_action_active(struct bContext *C); bool ED_operator_buttons_active(struct bContext *C); bool ED_operator_node_active(struct bContext *C); @@ -354,6 +355,7 @@ bool ED_operator_uvedit(struct bContext *C); bool ED_operator_uvedit_space_image(struct bContext *C); bool ED_operator_uvmap(struct bContext *C); bool ED_operator_posemode_exclusive(struct bContext *C); +bool ED_operator_object_active_local_editable_posemode_exclusive(struct bContext *C); bool ED_operator_posemode_context(struct bContext *C); bool ED_operator_posemode(struct bContext *C); bool ED_operator_posemode_local(struct bContext *C); diff --git a/source/blender/editors/include/ED_sequencer.h b/source/blender/editors/include/ED_sequencer.h index 11eff2d583b..606b4c9cad0 100644 --- a/source/blender/editors/include/ED_sequencer.h +++ b/source/blender/editors/include/ED_sequencer.h @@ -42,6 +42,8 @@ bool ED_space_sequencer_maskedit_poll(struct bContext *C); bool ED_space_sequencer_check_show_imbuf(struct SpaceSeq *sseq); bool ED_space_sequencer_check_show_strip(struct SpaceSeq *sseq); +bool ED_space_sequencer_has_playback_animation(const struct SpaceSeq *sseq, + const struct Scene *scene); void ED_operatormacros_sequencer(void); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 52d69d12253..64883ed5f1d 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -90,8 +90,6 @@ typedef struct ViewDepths { short x, y; /* only for temp use for sub-rects, added to region->winx/y */ float *depths; double depth_range[2]; - - bool damaged; } ViewDepths; /* Rotate 3D cursor on placement. */ @@ -154,19 +152,20 @@ void ED_view3d_depth_override(struct Depsgraph *depsgraph, struct View3D *v3d, struct Object *obact, eV3DDepthOverrideMode mode, - bool update_cache); + struct ViewDepths **r_depths); +void ED_view3d_depths_free(ViewDepths *depths); bool ED_view3d_depth_read_cached(const ViewDepths *vd, const int mval[2], int margin, float *r_depth); -bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, +bool ED_view3d_depth_read_cached_normal(const struct ARegion *region, + const ViewDepths *depths, const int mval[2], float r_normal[3]); bool ED_view3d_depth_unproject_v3(const struct ARegion *region, const int mval[2], const double depth, float r_location_world[3]); -void ED_view3d_depth_tag_update(struct RegionView3D *rv3d); /* Projection */ #define IS_CLIPPED 12000 @@ -196,12 +195,38 @@ typedef enum { V3D_PROJ_TEST_CLIP_NEAR = (1 << 2), V3D_PROJ_TEST_CLIP_FAR = (1 << 3), V3D_PROJ_TEST_CLIP_ZERO = (1 << 4), + /** + * Clip the contents of the data being iterated over. + * Currently this is only used to edges when projecting into screen space. + * + * Clamp the edge within the viewport limits defined by + * #V3D_PROJ_TEST_CLIP_WIN, #V3D_PROJ_TEST_CLIP_NEAR & #V3D_PROJ_TEST_CLIP_FAR. + * This resolves the problem of a visible edge having one of it's vertices + * behind the viewport. See: T32214. + * + * This is not default behavior as it may be important for the screen-space location + * of an edges vertex to represent that vertices location (instead of a location along the edge). + * + * \note Perspective views should enable #V3D_PROJ_TEST_CLIP_WIN along with + * #V3D_PROJ_TEST_CLIP_NEAR as the near-plane-clipped location of a point + * may become very large (even infinite) when projected into screen-space. + * Unless the that point happens to coincide with the camera's point of view. + * + * Use #V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT instead of #V3D_PROJ_TEST_CLIP_CONTENT, + * to avoid accidentally enabling near clipping without clipping by window bounds. + */ + V3D_PROJ_TEST_CLIP_CONTENT = (1 << 5), } eV3DProjTest; #define V3D_PROJ_TEST_CLIP_DEFAULT \ (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) #define V3D_PROJ_TEST_ALL \ - (V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_FAR | V3D_PROJ_TEST_CLIP_ZERO) + (V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_FAR | V3D_PROJ_TEST_CLIP_ZERO | \ + V3D_PROJ_TEST_CLIP_CONTENT) + +#define V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT \ + (V3D_PROJ_TEST_CLIP_CONTENT | V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_FAR | \ + V3D_PROJ_TEST_CLIP_WIN) /* view3d_iterators.c */ @@ -404,7 +429,7 @@ bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, float r_ray_end[3], const bool do_clip); void ED_view3d_ob_project_mat_get(const struct RegionView3D *v3d, - struct Object *ob, + const struct Object *ob, float r_pmat[4][4]); void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d, const float obmat[4][4], @@ -578,8 +603,8 @@ bool ED_view3d_area_user_region(const struct ScrArea *area, struct ARegion **r_region); bool ED_operator_rv3d_user_region_poll(struct bContext *C); -void ED_view3d_init_mats_rv3d(struct Object *ob, struct RegionView3D *rv3d); -void ED_view3d_init_mats_rv3d_gl(struct Object *ob, struct RegionView3D *rv3d); +void ED_view3d_init_mats_rv3d(const struct Object *ob, struct RegionView3D *rv3d); +void ED_view3d_init_mats_rv3d_gl(const struct Object *ob, struct RegionView3D *rv3d); #ifdef DEBUG void ED_view3d_clear_mats_rv3d(struct RegionView3D *rv3d); void ED_view3d_check_mats_rv3d(struct RegionView3D *rv3d); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 338b12f7985..df65c602fc4 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -290,18 +290,14 @@ enum { /** Active right part of number button */ UI_BUT_ACTIVE_RIGHT = 1 << 22, - /* (also used by search buttons to enforce shortcut display for their items). */ - /** Button has shortcut text. */ - UI_BUT_HAS_SHORTCUT = 1 << 23, - /** Reverse order of consecutive off/on icons */ - UI_BUT_ICON_REVERSE = 1 << 24, + UI_BUT_ICON_REVERSE = 1 << 23, /** Value is animated, but the current value differs from the animated one. */ - UI_BUT_ANIMATED_CHANGED = 1 << 25, + UI_BUT_ANIMATED_CHANGED = 1 << 24, /* Draw the checkbox buttons inverted. */ - UI_BUT_CHECKBOX_INVERT = 1 << 26, + UI_BUT_CHECKBOX_INVERT = 1 << 25, }; /* scale fixed button widths by this to account for DPI */ @@ -723,6 +719,7 @@ void UI_but_drag_set_asset(uiBut *but, const char *name, const char *path, int id_type, + int import_type, /* eFileAssetImportType */ int icon, struct ImBuf *imb, float scale); diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 64f881052a1..999f42efe65 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -105,8 +105,12 @@ struct ScrArea; struct bContext; struct bScreen; struct rctf; +struct rcti; +struct wmEvent; struct wmGizmoGroupType; struct wmKeyConfig; +struct wmOperator; +struct wmOperatorType; typedef struct View2DScrollers View2DScrollers; @@ -287,6 +291,77 @@ void UI_view2d_smooth_view(struct bContext *C, /* Caller passes in own idname. */ void VIEW2D_GGT_navigate_impl(struct wmGizmoGroupType *gzgt, const char *idname); +/* Edge pan */ + +/** + * Custom-data for view panning operators. + */ +typedef struct View2DEdgePanData { + /** Screen where view pan was initiated. */ + struct bScreen *screen; + /** Area where view pan was initiated. */ + struct ScrArea *area; + /** Region where view pan was initiated. */ + struct ARegion *region; + /** View2d we're operating in. */ + struct View2D *v2d; + + /** Inside distance in UI units from the edge of the region within which to start panning. */ + float inside_pad; + /** Outside distance in UI units from the edge of the region at which to stop panning. */ + float outside_pad; + /** + * Width of the zone in UI units where speed increases with distance from the edge. + * At the end of this zone max speed is reached. + */ + float speed_ramp; + /** Maximum speed in UI units per second. */ + float max_speed; + /** Delay in seconds before maximum speed is reached. */ + float delay; + + /** Amount to move view relative to zoom. */ + float facx, facy; + + /* Timers. */ + double edge_pan_last_time; + double edge_pan_start_time_x, edge_pan_start_time_y; +} View2DEdgePanData; + +bool UI_view2d_edge_pan_poll(struct bContext *C); + +void UI_view2d_edge_pan_init(struct bContext *C, + struct View2DEdgePanData *vpd, + float inside_pad, + float outside_pad, + float speed_ramp, + float max_speed, + float delay); + +void UI_view2d_edge_pan_reset(struct View2DEdgePanData *vpd); + +/* Apply transform to view (i.e. adjust 'cur' rect). */ +void UI_view2d_edge_pan_apply(struct bContext *C, struct View2DEdgePanData *vpd, int x, int y); + +/* Apply transform to view using mouse events. */ +void UI_view2d_edge_pan_apply_event(struct bContext *C, + struct View2DEdgePanData *vpd, + const struct wmEvent *event); + +void UI_view2d_edge_pan_operator_properties(struct wmOperatorType *ot); + +void UI_view2d_edge_pan_operator_properties_ex(struct wmOperatorType *ot, + float inside_pad, + float outside_pad, + float speed_ramp, + float max_speed, + float delay); + +/* Initialize panning data with operator settings. */ +void UI_view2d_edge_pan_operator_init(struct bContext *C, + struct View2DEdgePanData *vpd, + struct wmOperator *op); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 421019bebb8..5011a50ed73 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -75,6 +75,7 @@ set(SRC resources.c view2d.c view2d_draw.c + view2d_edge_pan.c view2d_gizmo_navigate.c view2d_ops.c diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index f6b2a6a1bc6..f5528638bec 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -985,12 +985,12 @@ bool UI_but_active_only(const bContext *C, ARegion *region, uiBlock *block, uiBu /** * \warning This must run after other handlers have been added, - * otherwise the handler wont be removed, see: T71112. + * otherwise the handler won't be removed, see: T71112. */ bool UI_block_active_only_flagged_buttons(const bContext *C, ARegion *region, uiBlock *block) { /* Running this command before end-block has run, means buttons that open menus - * wont have those menus correctly positioned, see T83539. */ + * won't have those menus correctly positioned, see T83539. */ BLI_assert(block->endblock); bool done = false; @@ -1160,7 +1160,6 @@ void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_str MEM_freeN(butstr_orig); but->str = but->strdata; but->flag |= UI_BUT_HAS_SEP_CHAR; - but->drawflag |= UI_BUT_HAS_SHORTCUT; ui_but_update(but); } @@ -3224,8 +3223,8 @@ void ui_but_range_set_hard(uiBut *but) /* note: this could be split up into functions which handle arrays and not */ void ui_but_range_set_soft(uiBut *but) { - /* ideally we would not limit this but practically, its more than - * enough worst case is very long vectors wont use a smart soft-range + /* Ideally we would not limit this, but practically it's more than + * enough. Worst case is very long vectors won't use a smart soft-range, * which isn't so bad. */ if (but->rnaprop) { @@ -6144,6 +6143,7 @@ void UI_but_drag_set_asset(uiBut *but, const char *name, const char *path, int id_type, + int import_type, int icon, struct ImBuf *imb, float scale) @@ -6153,6 +6153,7 @@ void UI_but_drag_set_asset(uiBut *but, BLI_strncpy(asset_drag->name, name, sizeof(asset_drag->name)); asset_drag->path = path; asset_drag->id_type = id_type; + asset_drag->import_type = import_type; but->dragtype = WM_DRAG_ASSET; ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index b142e383df0..775e3923edc 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -1280,7 +1280,6 @@ void ui_popup_context_menu_for_panel(bContext *C, ARegion *region, Panel *panel) uiBlock *block = uiLayoutGetBlock(layout); uiBut *but = block->buttons.last; but->flag |= UI_BUT_HAS_SEP_CHAR; - but->drawflag |= UI_BUT_HAS_SHORTCUT; } } UI_popup_menu_end(C, pup); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 5f98a501bec..1c55ce0f348 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1561,7 +1561,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const }; /* check if this is a different button, - * chances are high the button wont move about :) */ + * chances are high the button won't move about :) */ if (len_manhattan_v2v2(drag_info->but_cent_start, but_cent_new) > 1.0f) { if (fabsf(drag_info->but_cent_start[0] - but_cent_new[0]) < fabsf(drag_info->but_cent_start[1] - but_cent_new[1])) { @@ -1939,7 +1939,7 @@ static bool ui_but_drag_init(bContext *C, uiDragToggleHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__); ARegion *region_prev; - /* call here because regular mouse-up event wont run, + /* call here because regular mouse-up event won't run, * typically 'button_activate_exit()' handles this */ ui_apply_but_autokey(C, but); @@ -3414,7 +3414,7 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) const int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr)); /* not a file?, strip non utf-8 chars */ if (strip) { - /* wont happen often so isn't that annoying to keep it here for a while */ + /* won't happen often so isn't that annoying to keep it here for a while */ printf("%s: invalid utf8 - stripped chars %d\n", __func__, strip); } } @@ -3906,7 +3906,6 @@ static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data) data->startvalue = ui_but_value_get(but); data->origvalue = data->startvalue; data->value = data->origvalue; - but->editval = &data->value; } static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) @@ -3935,6 +3934,7 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) } else { ui_numedit_begin_set_values(but, data); + but->editval = &data->value; float softmin = but->softmin; float softmax = but->softmax; @@ -9662,7 +9662,7 @@ static void ui_region_auto_open_clear(ARegion *region) * This allows a menu to be open, * but send key events to the parent if there's no active buttons. * - * Without this keyboard navigation from menu's wont work. + * Without this keyboard navigation from menus won't work. */ static bool ui_menu_pass_event_to_parent_if_nonactive(uiPopupBlockHandle *menu, const uiBut *but, diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 0cf3ad59903..ce5c17a0718 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -803,7 +803,7 @@ bool UI_context_copy_to_selected_list(bContext *C, } else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) { /* Special case when we do this for 'Sequence.lock'. - * (if the sequence is locked, it wont be in "selected_editable_sequences"). */ + * (if the sequence is locked, it won't be in "selected_editable_sequences"). */ const char *prop_id = RNA_property_identifier(prop); if (STREQ(prop_id, "lock")) { *r_lb = CTX_data_collection_get(C, "selected_sequences"); @@ -921,7 +921,7 @@ bool UI_context_copy_to_selected_list(bContext *C, * to handle situations like T41062... */ if ((*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) { /* Special case when we do this for 'Sequence.lock'. - * (if the sequence is locked, it wont be in "selected_editable_sequences"). */ + * (if the sequence is locked, it won't be in "selected_editable_sequences"). */ const char *prop_id = RNA_property_identifier(prop); if (STREQ(prop_id, "lock")) { *r_lb = CTX_data_collection_get(C, "selected_sequences"); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 6505a7cd76a..6694535e3af 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -674,7 +674,7 @@ static bool panel_type_context_poll(ARegion *region, const PanelType *panel_type, const char *context) { - if (UI_panel_category_is_visible(region)) { + if (!BLI_listbase_is_empty(®ion->panels_category)) { return STREQ(panel_type->category, UI_panel_category_active_get(region, false)); } diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 987cde61f97..c35dbc5d7a6 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -95,7 +95,7 @@ typedef struct uiSearchboxData { /** draw thumbnail previews, rather than list */ bool preview; /** Use the #UI_SEP_CHAR char for splitting shortcuts (good for operators, bad for data). */ - bool use_sep; + bool use_shortcut_sep; int prv_rows, prv_cols; /** * Show the active icon and text after the last instance of this string. @@ -314,7 +314,7 @@ bool ui_searchbox_apply(uiBut *but, ARegion *region) data->items.name_prefix_offsets[data->active] : 0); - const char *name_sep = data->use_sep ? strrchr(name, UI_SEP_CHAR) : NULL; + const char *name_sep = data->use_shortcut_sep ? strrchr(name, UI_SEP_CHAR) : NULL; BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); @@ -535,7 +535,7 @@ void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool re /* Never include the prefix in the button. */ (data->items.name_prefix_offsets ? data->items.name_prefix_offsets[a] : 0); - const char *name_sep = data->use_sep ? strrchr(name, UI_SEP_CHAR) : NULL; + const char *name_sep = data->use_shortcut_sep ? strrchr(name, UI_SEP_CHAR) : NULL; if (STREQLEN(but->editstr, name, name_sep ? (name_sep - name) : data->items.maxstrlen)) { data->active = a; break; @@ -627,7 +627,7 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) char *name_sep_test = NULL; uiMenuItemSeparatorType separator_type = UI_MENU_ITEM_SEPARATOR_NONE; - if (data->use_sep) { + if (data->use_shortcut_sep) { separator_type = UI_MENU_ITEM_SEPARATOR_SHORTCUT; } /* Only set for displaying additional hint (e.g. library name of a linked data-block). */ @@ -719,7 +719,10 @@ static void ui_searchbox_region_free_cb(ARegion *region) region->regiondata = NULL; } -ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearch *search_but) +static ARegion *ui_searchbox_create_generic_ex(bContext *C, + ARegion *butregion, + uiButSearch *search_but, + const bool use_shortcut_sep) { wmWindow *win = CTX_wm_window(C); const uiStyle *style = UI_style_get(); @@ -759,12 +762,8 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearc data->prv_cols = but->a2; } - /* Only show key shortcuts when needed (checking RNA prop pointer is useless here, a lot of - * buttons are about data without having that pointer defined, let's rather try with optype!). - * One can also enforce that behavior by setting - * UI_BUT_HAS_SHORTCUT drawflag of search button. */ - if (but->optype != NULL || (but->drawflag & UI_BUT_HAS_SHORTCUT) != 0) { - data->use_sep = true; + if (but->optype != NULL || use_shortcut_sep) { + data->use_shortcut_sep = true; } data->sep_string = search_but->item_sep_string; @@ -888,6 +887,11 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearc return region; } +ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearch *search_but) +{ + return ui_searchbox_create_generic_ex(C, butregion, search_but, false); +} + /** * Similar to Python's `str.title` except... * @@ -973,8 +977,8 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe data->items.names[a], 0, state, - data->use_sep ? UI_MENU_ITEM_SEPARATOR_SHORTCUT : - UI_MENU_ITEM_SEPARATOR_NONE, + data->use_shortcut_sep ? UI_MENU_ITEM_SEPARATOR_SHORTCUT : + UI_MENU_ITEM_SEPARATOR_NONE, NULL); } } @@ -996,8 +1000,7 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe ARegion *ui_searchbox_create_operator(bContext *C, ARegion *butregion, uiButSearch *search_but) { - UI_but_drawflag_enable(&search_but->but, UI_BUT_HAS_SHORTCUT); - ARegion *region = ui_searchbox_create_generic(C, butregion, search_but); + ARegion *region = ui_searchbox_create_generic_ex(C, butregion, search_but, true); region->type->draw = ui_searchbox_region_draw_cb__operator; @@ -1016,8 +1019,7 @@ static void ui_searchbox_region_draw_cb__menu(const bContext *UNUSED(C), ARegion ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiButSearch *search_but) { - UI_but_drawflag_enable(&search_but->but, UI_BUT_HAS_SHORTCUT); - ARegion *region = ui_searchbox_create_generic(C, butregion, search_but); + ARegion *region = ui_searchbox_create_generic_ex(C, butregion, search_but, true); if (false) { region->type->draw = ui_searchbox_region_draw_cb__menu; diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index fe6a8b0d1a6..ce1109ad9df 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1949,8 +1949,8 @@ static bool widget_draw_text_underline_calc_position(const char *UNUSED(str), /* Full width of this glyph including both bearings. */ const float width = glyph_bounds->xmin + BLI_rctf_size_x(glyph_bounds) + glyph_bounds->xmin; ul_data->r_offset_px[0] = glyph_step_bounds->xmin + ((width - ul_data->width_px) * 0.5f); - /* Two line-widths below the lower glyph bounds. */ - ul_data->r_offset_px[1] = glyph_bounds->ymin - U.pixelsize - U.pixelsize; + /* One line-width below the lower glyph bounds. */ + ul_data->r_offset_px[1] = glyph_bounds->ymin - U.pixelsize; /* Early exit. */ return false; } @@ -2131,14 +2131,15 @@ static void widget_draw_text(const uiFontStyle *fstyle, transopts = ui_translate_buttons(); #endif + bool use_drawstr_right_as_hint = false; + /* cut string in 2 parts - only for menu entries */ - if ((but->drawflag & UI_BUT_HAS_SHORTCUT) && (but->editstr == NULL)) { - if (but->flag & UI_BUT_HAS_SEP_CHAR) { - drawstr_right = strrchr(drawstr, UI_SEP_CHAR); - if (drawstr_right) { - drawstr_left_len = (drawstr_right - drawstr); - drawstr_right++; - } + if (but->flag & UI_BUT_HAS_SEP_CHAR && (but->editstr == NULL)) { + drawstr_right = strrchr(drawstr, UI_SEP_CHAR); + if (drawstr_right) { + use_drawstr_right_as_hint = true; + drawstr_left_len = (drawstr_right - drawstr); + drawstr_right++; } } @@ -2207,7 +2208,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, } int ul_width = round_fl_to_int(BLF_width(fstyle->uifont_id, "_", 2)); - int ul_height = max_ii(fstyle->points * U.dpi_fac * 0.1f, U.pixelsize); struct UnderlineData ul_data = { .str_offset = ul_index, @@ -2220,16 +2220,13 @@ static void widget_draw_text(const uiFontStyle *fstyle, widget_draw_text_underline_calc_position, &ul_data); - GPU_blend(GPU_BLEND_ALPHA); - const uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor4ubv(wcol->text); const int pos_x = rect->xmin + font_xofs + ul_data.r_offset_px[0]; const int pos_y = rect->ymin + font_yofs + ul_data.r_offset_px[1]; - immRecti(pos, pos_x, pos_y, pos_x + ul_width, pos_y - ul_height); - immUnbindProgram(); - GPU_blend(GPU_BLEND_NONE); + + /* Use text output because direct drawing doesn't always work. See T89246. */ + BLF_position(fstyle->uifont_id, pos_x, pos_y, 0.0f); + BLF_color4ubv(fstyle->uifont_id, wcol->text); + BLF_draw(fstyle->uifont_id, "_", 2); if (fstyle->kerning == 1) { BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); @@ -2243,7 +2240,7 @@ static void widget_draw_text(const uiFontStyle *fstyle, if (drawstr_right) { uchar col[4]; copy_v4_v4_uchar(col, wcol->text); - if (but->drawflag & UI_BUT_HAS_SHORTCUT) { + if (use_drawstr_right_as_hint) { col[3] *= 0.5f; } diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index e4dad0f1a53..224993555bf 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -991,10 +991,11 @@ void UI_view2d_totRect_set_resize(View2D *v2d, int width, int height, bool resiz if (ELEM(0, width, height)) { if (G.debug & G_DEBUG) { + /* XXX: temp debug info. */ printf("Error: View2D totRect set exiting: v2d=%p width=%d height=%d\n", (void *)v2d, width, - height); /* XXX temp debug info */ + height); } return; } diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c new file mode 100644 index 00000000000..ca32a754f1d --- /dev/null +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -0,0 +1,345 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup spnode + */ + +#include "BKE_context.h" + +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" +#include "UI_view2d.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* -------------------------------------------------------------------- */ +/** \name Edge Pan Operator Utilities + * \{ */ + +bool UI_view2d_edge_pan_poll(bContext *C) +{ + ARegion *region = CTX_wm_region(C); + + /* Check if there's a region in context to work with. */ + if (region == NULL) { + return false; + } + + View2D *v2d = ®ion->v2d; + + /* Check that 2d-view can pan. */ + if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) { + return false; + } + + /* View can pan. */ + return true; +} + +void UI_view2d_edge_pan_init(bContext *C, + View2DEdgePanData *vpd, + float inside_pad, + float outside_pad, + float speed_ramp, + float max_speed, + float delay) +{ + if (!UI_view2d_edge_pan_poll(C)) { + return; + } + + /* Set pointers to owners. */ + vpd->screen = CTX_wm_screen(C); + vpd->area = CTX_wm_area(C); + vpd->region = CTX_wm_region(C); + vpd->v2d = &vpd->region->v2d; + + BLI_assert(speed_ramp > 0.0f); + vpd->inside_pad = inside_pad; + vpd->outside_pad = outside_pad; + vpd->speed_ramp = speed_ramp; + vpd->max_speed = max_speed; + vpd->delay = delay; + + /* Calculate translation factor, based on size of view. */ + const float winx = (float)(BLI_rcti_size_x(&vpd->region->winrct) + 1); + const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1); + vpd->facx = (BLI_rctf_size_x(&vpd->v2d->cur)) / winx; + vpd->facy = (BLI_rctf_size_y(&vpd->v2d->cur)) / winy; + + UI_view2d_edge_pan_reset(vpd); +} + +void UI_view2d_edge_pan_reset(View2DEdgePanData *vpd) +{ + vpd->edge_pan_start_time_x = 0.0; + vpd->edge_pan_start_time_y = 0.0; + vpd->edge_pan_last_time = PIL_check_seconds_timer(); +} + +/** + * Reset the edge pan timers if the mouse isn't in the scroll zone and + * start the timers when the mouse enters a scroll zone. + */ +static void edge_pan_manage_delay_timers(View2DEdgePanData *vpd, + int pan_dir_x, + int pan_dir_y, + const double current_time) +{ + if (pan_dir_x == 0) { + vpd->edge_pan_start_time_x = 0.0; + } + else if (vpd->edge_pan_start_time_x == 0.0) { + vpd->edge_pan_start_time_x = current_time; + } + if (pan_dir_y == 0) { + vpd->edge_pan_start_time_y = 0.0; + } + else if (vpd->edge_pan_start_time_y == 0.0) { + vpd->edge_pan_start_time_y = current_time; + } +} + +/** + * Used to calculate a "fade in" factor for edge panning to make the interaction feel smooth + * and more purposeful. + * + * \note Assumes a domain_min of 0.0f. + */ +static float smootherstep(const float domain_max, float x) +{ + x = clamp_f(x / domain_max, 0.0, 1.0); + return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); +} + +static float edge_pan_speed(View2DEdgePanData *vpd, + int event_loc, + bool x_dir, + const double current_time) +{ + ARegion *region = vpd->region; + + /* Find the distance from the start of the drag zone. */ + const int pad = vpd->inside_pad * U.widget_unit; + const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + pad; + const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - pad; + int distance = 0.0; + if (event_loc > max) { + distance = event_loc - max; + } + else if (event_loc < min) { + distance = min - event_loc; + } + else { + BLI_assert(!"Calculating speed outside of pan zones"); + return 0.0f; + } + float distance_factor = distance / (vpd->speed_ramp * U.widget_unit); + CLAMP(distance_factor, 0.0f, 1.0f); + + /* Apply a fade in to the speed based on a start time delay. */ + const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y; + const float delay_factor = smootherstep(vpd->delay, (float)(current_time - start_time)); + + return distance_factor * delay_factor * vpd->max_speed * U.widget_unit * (float)U.dpi_fac; +} + +static void edge_pan_apply_delta(bContext *C, View2DEdgePanData *vpd, float dx, float dy) +{ + View2D *v2d = vpd->v2d; + if (!v2d) { + return; + } + + /* Calculate amount to move view by. */ + dx *= vpd->facx; + dy *= vpd->facy; + + /* Only move view on an axis if change is allowed. */ + if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) { + v2d->cur.xmin += dx; + v2d->cur.xmax += dx; + } + if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) { + v2d->cur.ymin += dy; + v2d->cur.ymax += dy; + } + + /* Inform v2d about changes after this operation. */ + UI_view2d_curRect_changed(C, v2d); + + /* Don't rebuild full tree in outliner, since we're just changing our view. */ + ED_region_tag_redraw_no_rebuild(vpd->region); + + /* Request updates to be done. */ + WM_event_add_mousemove(CTX_wm_window(C)); + + UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY); +} + +void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, int x, int y) +{ + ARegion *region = vpd->region; + + rcti inside_rect, outside_rect; + inside_rect = region->winrct; + outside_rect = region->winrct; + BLI_rcti_pad(&inside_rect, -vpd->inside_pad * U.widget_unit, -vpd->inside_pad * U.widget_unit); + BLI_rcti_pad(&outside_rect, vpd->outside_pad * U.widget_unit, vpd->outside_pad * U.widget_unit); + + int pan_dir_x = 0; + int pan_dir_y = 0; + if ((vpd->outside_pad == 0) || BLI_rcti_isect_pt(&outside_rect, x, y)) { + /* Find whether the mouse is beyond X and Y edges. */ + if (x > inside_rect.xmax) { + pan_dir_x = 1; + } + else if (x < inside_rect.xmin) { + pan_dir_x = -1; + } + if (y > inside_rect.ymax) { + pan_dir_y = 1; + } + else if (y < inside_rect.ymin) { + pan_dir_y = -1; + } + } + + const double current_time = PIL_check_seconds_timer(); + edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time); + + /* Calculate the delta since the last time the operator was called. */ + const float dtime = (float)(current_time - vpd->edge_pan_last_time); + float dx = 0.0f, dy = 0.0f; + if (pan_dir_x != 0) { + const float speed = edge_pan_speed(vpd, x, true, current_time); + dx = dtime * speed * (float)pan_dir_x; + } + if (pan_dir_y != 0) { + const float speed = edge_pan_speed(vpd, y, false, current_time); + dy = dtime * speed * (float)pan_dir_y; + } + vpd->edge_pan_last_time = current_time; + + /* Pan, clamping inside the regions total bounds. */ + edge_pan_apply_delta(C, vpd, dx, dy); +} + +void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const wmEvent *event) +{ + /* Only mouse-move events matter here, ignore others. */ + if (event->type != MOUSEMOVE) { + return; + } + + UI_view2d_edge_pan_apply(C, vpd, event->x, event->y); +} + +void UI_view2d_edge_pan_operator_properties(wmOperatorType *ot) +{ + /* Default values for edge panning operators. */ + UI_view2d_edge_pan_operator_properties_ex(ot, + /*inside_pad*/ 1.0f, + /*outside_pad*/ 0.0f, + /*speed_ramp*/ 1.0f, + /*max_speed*/ 500.0f, + /*delay*/ 1.0f); +} + +void UI_view2d_edge_pan_operator_properties_ex(struct wmOperatorType *ot, + float inside_pad, + float outside_pad, + float speed_ramp, + float max_speed, + float delay) +{ + RNA_def_float( + ot->srna, + "inside_padding", + inside_pad, + 0.0f, + 100.0f, + "Inside Padding", + "Inside distance in UI units from the edge of the region within which to start panning", + 0.0f, + 100.0f); + RNA_def_float( + ot->srna, + "outside_padding", + outside_pad, + 0.0f, + 100.0f, + "Outside Padding", + "Outside distance in UI units from the edge of the region at which to stop panning", + 0.0f, + 100.0f); + RNA_def_float(ot->srna, + "speed_ramp", + speed_ramp, + 0.0f, + 100.0f, + "Speed Ramp", + "Width of the zone in UI units where speed increases with distance from the edge", + 0.0f, + 100.0f); + RNA_def_float(ot->srna, + "max_speed", + max_speed, + 0.0f, + 10000.0f, + "Max Speed", + "Maximum speed in UI units per second", + 0.0f, + 10000.0f); + RNA_def_float(ot->srna, + "delay", + delay, + 0.0f, + 10.0f, + "Delay", + "Delay in seconds before maximum speed is reached", + 0.0f, + 10.0f); +} + +void UI_view2d_edge_pan_operator_init(bContext *C, View2DEdgePanData *vpd, wmOperator *op) +{ + UI_view2d_edge_pan_init(C, + vpd, + RNA_float_get(op->ptr, "inside_padding"), + RNA_float_get(op->ptr, "outside_padding"), + RNA_float_get(op->ptr, "speed_ramp"), + RNA_float_get(op->ptr, "max_speed"), + RNA_float_get(op->ptr, "delay")); +} + +/** \} */ diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 40c510af7e5..7ad28cd6069 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -341,162 +341,37 @@ static void VIEW2D_OT_pan(wmOperatorType *ot) * passes through. * \{ */ -/** Distance from the edge of the region within which to start panning. */ -#define EDGE_PAN_REGION_PAD (U.widget_unit) -/** Speed factor in pixels per second per pixel of distance from edge pan zone beginning. */ -#define EDGE_PAN_SPEED_PER_PIXEL (25.0f * (float)U.dpi_fac) -/** Delay before drag panning in seconds. */ -#define EDGE_PAN_DELAY 1.0f - /* set up modal operator and relevant settings */ static int view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - /* Set up customdata. */ - view_pan_init(C, op); - - v2dViewPanData *vpd = op->customdata; - - vpd->edge_pan_start_time_x = 0.0; - vpd->edge_pan_start_time_y = 0.0; - vpd->edge_pan_last_time = PIL_check_seconds_timer(); + op->customdata = MEM_callocN(sizeof(View2DEdgePanData), "View2DEdgePanData"); + View2DEdgePanData *vpd = op->customdata; + UI_view2d_edge_pan_operator_init(C, vpd, op); WM_event_add_modal_handler(C, op); return (OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH); } -/** - * Reset the edge pan timers if the mouse isn't in the scroll zone and - * start the timers when the mouse enters a scroll zone. - */ -static void edge_pan_manage_delay_timers(v2dViewPanData *vpd, - int pan_dir_x, - int pan_dir_y, - const double current_time) -{ - if (pan_dir_x == 0) { - vpd->edge_pan_start_time_x = 0.0; - } - else if (vpd->edge_pan_start_time_x == 0.0) { - vpd->edge_pan_start_time_x = current_time; - } - if (pan_dir_y == 0) { - vpd->edge_pan_start_time_y = 0.0; - } - else if (vpd->edge_pan_start_time_y == 0.0) { - vpd->edge_pan_start_time_y = current_time; - } -} - -/** - * Used to calculate a "fade in" factor for edge panning to make the interaction feel smooth - * and more purposeful. - * - * \note Assumes a domain_min of 0.0f. - */ -static float smootherstep(const float domain_max, float x) -{ - x = clamp_f(x / domain_max, 0.0, 1.0); - return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); -} - -static float edge_pan_speed(v2dViewPanData *vpd, - int event_loc, - bool x_dir, - const double current_time) -{ - ARegion *region = vpd->region; - - /* Find the distance from the start of the drag zone. */ - const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + EDGE_PAN_REGION_PAD; - const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - EDGE_PAN_REGION_PAD; - int distance = 0.0; - if (event_loc > max) { - distance = event_loc - max; - } - else if (event_loc < min) { - distance = min - event_loc; - } - else { - BLI_assert(!"Calculating speed outside of pan zones"); - return 0.0f; - } - - /* Apply a fade in to the speed based on a start time delay. */ - const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y; - const float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time)); - - return distance * EDGE_PAN_SPEED_PER_PIXEL * delay_factor; -} - static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) { - v2dViewPanData *vpd = op->customdata; - ARegion *region = vpd->region; + View2DEdgePanData *vpd = op->customdata; if (event->val == KM_RELEASE || event->type == EVT_ESCKEY) { - view_pan_exit(op); + MEM_SAFE_FREE(op->customdata); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); } - /* Only mousemove events matter here, ignore others. */ - if (event->type != MOUSEMOVE) { - return OPERATOR_PASS_THROUGH; - } + + UI_view2d_edge_pan_apply_event(C, vpd, event); /* This operator is supposed to run together with some drag action. * On successful handling, always pass events on to other handlers. */ - const int success_retval = OPERATOR_PASS_THROUGH; - - const int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X; - rcti padding_rect; - if (outside_padding != 0) { - padding_rect = region->winrct; - BLI_rcti_pad(&padding_rect, outside_padding, outside_padding); - } - - int pan_dir_x = 0; - int pan_dir_y = 0; - if ((outside_padding == 0) || BLI_rcti_isect_pt(&padding_rect, event->x, event->y)) { - /* Find whether the mouse is beyond X and Y edges. */ - if (event->x > region->winrct.xmax - EDGE_PAN_REGION_PAD) { - pan_dir_x = 1; - } - else if (event->x < region->winrct.xmin + EDGE_PAN_REGION_PAD) { - pan_dir_x = -1; - } - if (event->y > region->winrct.ymax - EDGE_PAN_REGION_PAD) { - pan_dir_y = 1; - } - else if (event->y < region->winrct.ymin + EDGE_PAN_REGION_PAD) { - pan_dir_y = -1; - } - } - - const double current_time = PIL_check_seconds_timer(); - edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time); - - /* Calculate the delta since the last time the operator was called. */ - const float dtime = (float)(current_time - vpd->edge_pan_last_time); - float dx = 0.0f, dy = 0.0f; - if (pan_dir_x != 0) { - const float speed = edge_pan_speed(vpd, event->x, true, current_time); - dx = dtime * speed * (float)pan_dir_x; - } - if (pan_dir_y != 0) { - const float speed = edge_pan_speed(vpd, event->y, false, current_time); - dy = dtime * speed * (float)pan_dir_y; - } - vpd->edge_pan_last_time = current_time; - - /* Pan, clamping inside the regions's total bounds. */ - view_pan_apply_ex(C, vpd, dx, dy); - - return success_retval; + return OPERATOR_PASS_THROUGH; } static void view_edge_pan_cancel(bContext *UNUSED(C), wmOperator *op) { - view_pan_exit(op); + MEM_SAFE_FREE(op->customdata); } static void VIEW2D_OT_edge_pan(wmOperatorType *ot) @@ -510,26 +385,13 @@ static void VIEW2D_OT_edge_pan(wmOperatorType *ot) ot->invoke = view_edge_pan_invoke; ot->modal = view_edge_pan_modal; ot->cancel = view_edge_pan_cancel; - ot->poll = view_pan_poll; + ot->poll = UI_view2d_edge_pan_poll; /* operator is modal */ ot->flag = OPTYPE_INTERNAL; - RNA_def_int(ot->srna, - "outside_padding", - 0, - 0, - 100, - "Outside Padding", - "Padding around the region in UI units within which panning is activated (0 to " - "disable boundary)", - 0, - 100); + UI_view2d_edge_pan_operator_properties(ot); } -#undef EDGE_PAN_REGION_PAD -#undef EDGE_PAN_SPEED_PER_PIXEL -#undef EDGE_PAN_DELAY - /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index 81bf66da72c..4ce4a416796 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -291,7 +291,7 @@ static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op) BLI_addtail(&shapes_tmp, mask_layer_shape_tmp); } - /* re-key, note: cant modify the keys here since it messes uop */ + /* re-key, note: can't modify the keys here since it messes uop */ for (mask_layer_shape_tmp = shapes_tmp.first; mask_layer_shape_tmp; mask_layer_shape_tmp = mask_layer_shape_tmp->next) { BKE_mask_layer_evaluate(mask_layer, mask_layer_shape_tmp->frame, true); diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index 18e231893d4..a64b90e15a3 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -95,7 +95,12 @@ static void make_prim_finish(bContext *C, EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); /* only recalc editmode tessface if we are staying in editmode */ - EDBM_update_generic(obedit->data, !exit_editmode, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = !exit_editmode, + .calc_normals = false, + .is_destructive = true, + }); /* userdef */ if (exit_editmode) { diff --git a/source/blender/editors/mesh/editmesh_add_gizmo.c b/source/blender/editors/mesh/editmesh_add_gizmo.c index e7a99ca9e08..9efcf0963b4 100644 --- a/source/blender/editors/mesh/editmesh_add_gizmo.c +++ b/source/blender/editors/mesh/editmesh_add_gizmo.c @@ -357,7 +357,12 @@ static int add_primitive_cube_gizmo_exec(bContext *C, wmOperator *op) } EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_automerge.c b/source/blender/editors/mesh/editmesh_automerge.c index 2bd5b9b26ca..35fff1f8f3a 100644 --- a/source/blender/editors/mesh/editmesh_automerge.c +++ b/source/blender/editors/mesh/editmesh_automerge.c @@ -76,7 +76,12 @@ void EDBM_automerge(Object *obedit, bool update, const char hflag, const float d BMO_op_finish(bm, &weldop); if ((totvert_prev != bm->totvert) && update) { - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } } @@ -134,7 +139,12 @@ void EDBM_automerge_and_split(Object *obedit, #endif if (LIKELY(ok) && update) { - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } } diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 43492cd57af..110f1975d8d 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -403,9 +403,12 @@ static bool edbm_bevel_calc(wmOperator *op) continue; } - EDBM_mesh_normals_update(em); - - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); changed = true; } return changed; @@ -454,7 +457,12 @@ static void edbm_bevel_cancel(bContext *C, wmOperator *op) Object *obedit = opdata->ob_store[ob_index].ob; BMEditMesh *em = BKE_editmesh_from_object(obedit); EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, em, true); - EDBM_update_generic(obedit->data, false, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = true, + .is_destructive = true, + }); } } diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index ea35d5a9e26..42cf36dda81 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -347,7 +347,7 @@ static int mesh_bisect_exec(bContext *C, wmOperator *op) BMOperator bmop_attr; /* The fill normal sign is ignored as the face-winding is defined by surrounding faces. - * The normal is passed so triangle fill wont have to calculate it. */ + * The normal is passed so triangle fill won't have to calculate it. */ normalize_v3_v3(normal_fill, plane_no_local); /* Fill */ @@ -383,7 +383,12 @@ static int mesh_bisect_exec(bContext *C, wmOperator *op) bm, bmop.slots_out, "geom_cut.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true); if (EDBM_op_finish(em, &bmop, op, true)) { - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); EDBM_selectmode_flush(em); ret = OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index d5ddb7fc2c4..e03390780f9 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -319,9 +319,12 @@ static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op) em->bm, BMO_FLAG_DEFAULTS, "translate vec=%v verts=%hv", offset_local, BM_ELEM_SELECT); } - EDBM_mesh_normals_update(em); - - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -448,11 +451,13 @@ static int edbm_extrude_region_exec(bContext *C, wmOperator *op) continue; } /* This normally happens when pushing undo but modal operators - * like this one don't push undo data until after modal mode is - * done.*/ - EDBM_mesh_normals_update(em); - - EDBM_update_generic(obedit->data, true, true); + * like this one don't push undo data until after modal mode is done. */ + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } MEM_freeN(objects); return OPERATOR_FINISHED; @@ -502,13 +507,15 @@ static int edbm_extrude_context_exec(bContext *C, wmOperator *op) } edbm_extrude_mesh(obedit, em, op); - /* This normally happens when pushing undo but modal operators - * like this one don't push undo data until after modal mode is - * done.*/ - EDBM_mesh_normals_update(em); - - EDBM_update_generic(obedit->data, true, true); + /* This normally happens when pushing undo but modal operators + * like this one don't push undo data until after modal mode is done.*/ + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } MEM_freeN(objects); return OPERATOR_FINISHED; @@ -555,7 +562,12 @@ static int edbm_extrude_verts_exec(bContext *C, wmOperator *op) edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -603,7 +615,12 @@ static int edbm_extrude_edges_exec(bContext *C, wmOperator *op) edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT, use_normal_flip); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -651,7 +668,12 @@ static int edbm_extrude_faces_exec(bContext *C, wmOperator *op) edbm_extrude_discrete_faces(em, op, BM_ELEM_SELECT); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -884,11 +906,13 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w } /* This normally happens when pushing undo but modal operators - * like this one don't push undo data until after modal mode is - * done. */ - EDBM_mesh_normals_update(vc.em); - - EDBM_update_generic(vc.obedit->data, true, true); + * like this one don't push undo data until after modal mode is done. */ + EDBM_update(vc.obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); diff --git a/source/blender/editors/mesh/editmesh_extrude_screw.c b/source/blender/editors/mesh/editmesh_extrude_screw.c index 4cffd12cb34..1ba6a0f42c6 100644 --- a/source/blender/editors/mesh/editmesh_extrude_screw.c +++ b/source/blender/editors/mesh/editmesh_extrude_screw.c @@ -155,7 +155,12 @@ static int edbm_screw_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_extrude_spin.c b/source/blender/editors/mesh/editmesh_extrude_spin.c index 187652ae00f..2146207308c 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin.c @@ -108,7 +108,12 @@ static int edbm_spin_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index 73d79805f60..1c27ab00715 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -236,7 +236,12 @@ static void edbm_inset_cancel(bContext *C, wmOperator *op) Object *obedit = opdata->ob_store[ob_index].ob; BMEditMesh *em = BKE_editmesh_from_object(obedit); EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, em, true); - EDBM_update_generic(obedit->data, false, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = true, + }); } } @@ -326,7 +331,12 @@ static bool edbm_inset_calc(wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); changed = true; } return changed; diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index d1f228e951a..f2691580a9d 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -116,8 +116,12 @@ static void edbm_intersect_select(BMEditMesh *em, struct Mesh *me, bool do_selec } } - EDBM_mesh_normals_update(em); - EDBM_update_generic(me, true, true); + EDBM_update(me, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } /* -------------------------------------------------------------------- */ @@ -963,8 +967,12 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op)) } #endif - EDBM_mesh_normals_update(em); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); #ifdef USE_NET_ISLAND_CONNECT /* we may have remaining isolated regions remaining, @@ -1068,8 +1076,12 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op)) BLI_ghash_free(face_edge_map, NULL, NULL); - EDBM_mesh_normals_update(em); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } BLI_stack_free(edges_loose); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index ae824cad50b..2ba1d30900a 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -272,8 +272,7 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) float v1[3], v2[3]; float planes[4][4]; - planes_from_projmat( - (const float(*)[4])kcd->projmat, planes[2], planes[0], planes[3], planes[1], NULL, NULL); + planes_from_projmat(kcd->projmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL); /* ray-cast all planes */ { @@ -2805,8 +2804,12 @@ static void knifetool_finish_ex(KnifeTool_OpData *kcd) knife_make_cuts(kcd); EDBM_selectmode_flush(kcd->em); - EDBM_mesh_normals_update(kcd->em); - EDBM_update_generic(kcd->ob->data, true, true); + EDBM_update(kcd->ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); /* Re-tessellating makes this invalid, don't use again by accident. */ knifetool_free_bmbvh(kcd); diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 2057738221b..71319338a53 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -209,7 +209,12 @@ static void ringsel_finish(bContext *C, wmOperator *op) /* when used in a macro the tessfaces will be recalculated anyway, * this is needed here because modifiers depend on updated tessellation, see T45920 */ - EDBM_update_generic(lcd->ob->data, true, true); + EDBM_update(lcd->ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); if (is_single) { /* de-select endpoints */ @@ -218,7 +223,7 @@ static void ringsel_finish(bContext *C, wmOperator *op) EDBM_selectmode_flush_ex(lcd->em, SCE_SELECT_VERTEX); } - /* we cant slide multiple edges in vertex select mode */ + /* we can't slide multiple edges in vertex select mode */ else if (is_macro && (cuts > 1) && (em->selectmode & SCE_SELECT_VERTEX)) { EDBM_selectmode_disable(lcd->vc.scene, em, SCE_SELECT_VERTEX, SCE_SELECT_EDGE); } diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 7d849c096e7..993905462db 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -229,7 +229,7 @@ static int geometry_extract_apply(bContext *C, /* Remove the mask from the new object so it can be sculpted directly after extracting. */ CustomData_free_layers(&new_ob_mesh->vdata, CD_PAINT_MASK, new_ob_mesh->totvert); - BKE_mesh_copy_settings(new_ob_mesh, mesh); + BKE_mesh_copy_parameters_for_eval(new_ob_mesh, mesh); if (params->apply_shrinkwrap) { BKE_shrinkwrap_mesh_nearest_surface_deform(C, new_ob, ob); @@ -567,7 +567,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) BKE_mesh_nomain_to_mesh(new_ob_mesh, new_ob->data, new_ob, &CD_MASK_MESH, true); BKE_mesh_calc_normals(new_ob->data); - BKE_mesh_copy_settings(new_ob->data, mesh); + BKE_mesh_copy_parameters_for_eval(new_ob->data, mesh); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, new_ob); BKE_mesh_batch_cache_dirty_tag(new_ob->data, BKE_MESH_BATCH_DIRTY_ALL); DEG_relations_tag_update(bmain); diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index b7f671a4157..593545ddcef 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -270,7 +270,12 @@ static void mouse_mesh_shortest_path_vert(Scene *UNUSED(scene), } } - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } /** \} */ @@ -474,7 +479,12 @@ static void mouse_mesh_shortest_path_edge(Scene *scene, } } - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); if (op_params->edge_mode == EDGE_MODE_TAG_SEAM) { ED_uvedit_live_unwrap(scene, &obedit, 1); @@ -591,7 +601,12 @@ static void mouse_mesh_shortest_path_face(Scene *UNUSED(scene), BM_mesh_active_face_set(bm, f_dst_last); } - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } /** \} */ diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index 4d37b78c9b7..303cf41df0d 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -154,8 +154,12 @@ static int edbm_polybuild_transform_at_cursor_invoke(bContext *C, BM_face_select_set(bm, (BMFace *)ele_act, true); } - EDBM_mesh_normals_update(em); - EDBM_update_generic(vc.obedit->data, true, true); + EDBM_update(vc.obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); if (basact != NULL) { if (vc.view_layer->basact != basact) { ED_object_base_activate(C, basact); @@ -237,8 +241,12 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, } if (changed) { - EDBM_mesh_normals_update(em); - EDBM_update_generic(vc.obedit->data, true, true); + EDBM_update(vc.obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); if (basact != NULL) { if (vc.view_layer->basact != basact) { ED_object_base_activate(C, basact); @@ -400,8 +408,12 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, con } if (changed) { - EDBM_mesh_normals_update(em); - EDBM_update_generic(vc.obedit->data, true, true); + EDBM_update(vc.obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); if (basact != NULL) { if (vc.view_layer->basact != basact) { @@ -488,8 +500,12 @@ static int edbm_polybuild_split_at_cursor_invoke(bContext *C, } if (changed) { - EDBM_mesh_normals_update(em); - EDBM_update_generic(vc.obedit->data, true, true); + EDBM_update(vc.obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); WM_event_add_mousemove(vc.win); @@ -559,7 +575,7 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C, else { /* too involved to do inline */ - /* Avoid using selection so failure wont leave modified state. */ + /* Avoid using selection so failure won't leave modified state. */ EDBM_flag_disable_all(em, BM_ELEM_TAG); BM_elem_flag_enable(v_act, BM_ELEM_TAG); @@ -578,8 +594,12 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C, if (changed) { edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); - EDBM_mesh_normals_update(em); - EDBM_update_generic(vc.obedit->data, true, true); + EDBM_update(vc.obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); if (vc.view_layer->basact != basact) { ED_object_base_activate(C, basact); diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index 222d44f85d8..615590c51c6 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -170,15 +170,15 @@ static float edbm_rip_edge_side_measure( * * The method used for checking the side of selection is as follows... * - First tag all rip-able edges. - * - Build a contiguous edge list by looping over tagged edges and following each ones tagged + * - Build a contiguous edge list by looping over tagged edges and following each one's tagged * siblings in both directions. - * - The loops are not stored in an array, Instead both loops on either side of each edge has - * its index values set to count down from the last edge, this way, once we have the 'last' - * edge its very easy to walk down the connected edge loops. - * The reason for using loops like this is because when the edges are split we don't which - * face user gets the newly created edge - * (its as good as random so we cant assume new edges will be on once side). - * After splitting, its very simple to walk along boundary loops since each only has one edge + * - The loops are not stored in an array. Instead both loops on either side of each edge has + * its index values set to count down from the last edge. This way once we have the 'last' + * edge it's very easy to walk down the connected edge loops. + * The reason for using loops like this is because when the edges are split we don't know + * which face user gets the newly created edge + * (it's as good as random so we can't assume new edges will be on one side). + * After splitting, it's very simple to walk along boundary loops since each only has one edge * from a single side. * - The end loop pairs are stored in an array however to support multiple edge-selection-islands, * so you can rip multiple selections at once. @@ -189,7 +189,7 @@ static float edbm_rip_edge_side_measure( * * Limitation! * This currently works very poorly with intersecting edge islands - * (verts with more than 2 tagged edges). This is nice to but for now not essential. + * (verts with more than 2 tagged edges). This is nice to do but for now not essential. * * - campbell. */ @@ -639,7 +639,7 @@ static int edbm_rip_invoke__vert(bContext *C, const wmEvent *event, Object *obed /* should we go ahead with edge rip or do we need to do special case, split off vertex?: * split off vertex if... - * - we cant find an edge - this means we are ripping a faces vert that is connected to other + * - we can't find an edge - this means we are ripping a faces vert that is connected to other * geometry only at the vertex. * - the boundary edge total is greater than 2, * in this case edge split _can_ work but we get far nicer results if we use this special case. @@ -1082,7 +1082,12 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) } error_rip_failed = false; - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c index 6775cb85ef9..f7e88284d93 100644 --- a/source/blender/editors/mesh/editmesh_rip_edge.c +++ b/source/blender/editors/mesh/editmesh_rip_edge.c @@ -223,7 +223,12 @@ static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve BM_mesh_select_mode_flush(bm); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 6cb103460f6..46abf71c4e2 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -547,8 +547,10 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenEdge( - vc, find_nearest_edge_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + mesh_foreachScreenEdge(vc, + find_nearest_edge_center__doZBuf, + &data, + V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); *r_dist_center_px_manhattan = data.dist; } @@ -601,7 +603,8 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc, *dist_px_manhattan_p; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenEdge(vc, find_nearest_edge__doClosest, &data, clip_flag); + mesh_foreachScreenEdge( + vc, find_nearest_edge__doClosest, &data, clip_flag | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit; @@ -2568,7 +2571,7 @@ bool EDBM_selectmode_disable(Scene *scene, const short selectmode_fallback) { /* note essential, but switch out of vertex mode since the - * selected regions wont be nicely isolated after flushing */ + * selected regions won't be nicely isolated after flushing */ if (em->selectmode & selectmode_disable) { if (em->selectmode == selectmode_disable) { em->selectmode = selectmode_fallback; @@ -3639,8 +3642,9 @@ static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); - const int object_index = RNA_int_get(op->ptr, "object_index"); - const int index = RNA_int_get(op->ptr, "index"); + /* Intentionally wrap negative values so the lookup fails. */ + const uint object_index = (uint)RNA_int_get(op->ptr, "object_index"); + const uint index = (uint)RNA_int_get(op->ptr, "index"); ele = EDBM_elem_from_index_any_multi(view_layer, object_index, index, &obedit); } @@ -4270,7 +4274,12 @@ static int edbm_select_nth_exec(bContext *C, wmOperator *op) if (edbm_deselect_nth(em, &op_params) == true) { found_active_elt = true; - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index f3c0da67ecc..2ffeaa06751 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -497,7 +497,12 @@ static int similar_face_select_exec(bContext *C, wmOperator *op) if (changed) { EDBM_selectmode_flush(em); - EDBM_update_generic(ob->data, false, false); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } } @@ -519,7 +524,12 @@ static int similar_face_select_exec(bContext *C, wmOperator *op) } } EDBM_selectmode_flush(em); - EDBM_update_generic(ob->data, false, false); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } } @@ -917,7 +927,12 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op) if (changed) { EDBM_selectmode_flush(em); - EDBM_update_generic(ob->data, false, false); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } } @@ -939,7 +954,12 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op) } } EDBM_selectmode_flush(em); - EDBM_update_generic(ob->data, false, false); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } } @@ -1213,7 +1233,12 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) if (changed) { EDBM_selectmode_flush(em); - EDBM_update_generic(ob->data, false, false); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index bb332a4094c..c09ce126b7f 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -132,7 +132,12 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op) false, seed); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -324,7 +329,12 @@ static int edbm_subdivide_edge_ring_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -384,7 +394,12 @@ static int edbm_unsubdivide_exec(bContext *C, wmOperator *op) } EDBM_selectmode_flush(em); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -499,7 +514,12 @@ static int edbm_delete_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(em->bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); @@ -628,7 +648,12 @@ static int edbm_delete_loose_exec(bContext *C, wmOperator *op) EDBM_flag_disable_all(em, BM_ELEM_SELECT); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } int totelem_new[3]; @@ -686,7 +711,12 @@ static int edbm_collapse_edge_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -954,7 +984,7 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) * copying face data from surrounding, may have copied hidden face flag too. * * Important that faces use flushing since 'edges.out' - * wont include hidden edges that already existed. + * won't include hidden edges that already existed. */ BMO_slot_buffer_hflag_disable( em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_HIDDEN, true); @@ -971,7 +1001,12 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); changed_multi = true; } MEM_freeN(objects); @@ -1047,7 +1082,12 @@ static int edbm_mark_seam_exec(bContext *C, wmOperator *op) for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -1117,7 +1157,12 @@ static int edbm_mark_sharp_exec(bContext *C, wmOperator *op) BM_elem_flag_set(eed, BM_ELEM_SMOOTH, clear); } - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -1239,7 +1284,12 @@ static bool edbm_connect_vert_pair(BMEditMesh *em, struct Mesh *me, wmOperator * BM_custom_loop_normals_from_vector_layer(bm, false); - EDBM_update_generic(me, true, true); + EDBM_update(me, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } } MEM_freeN(verts); @@ -1538,7 +1588,12 @@ static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } else { failed_selection_order_len++; @@ -1603,7 +1658,12 @@ static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op) em, op, "faces.out", true, "connect_verts_concave faces=%hf", BM_ELEM_SELECT)) { continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -1657,7 +1717,12 @@ static int edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -1726,7 +1791,12 @@ static int edbm_face_make_planar_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -1775,7 +1845,12 @@ static bool edbm_edge_split_selected_edges(wmOperator *op, Object *obedit, BMEdi BM_custom_loop_normals_from_vector_layer(em->bm, false); EDBM_select_flush(em); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); return true; } @@ -1845,7 +1920,12 @@ static bool edbm_edge_split_selected_verts(wmOperator *op, Object *obedit, BMEdi BM_custom_loop_normals_from_vector_layer(em->bm, false); EDBM_select_flush(em); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); return true; } @@ -1958,7 +2038,12 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op) if (!EDBM_op_finish(em, &bmop, op, true)) { continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -2114,7 +2199,12 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op) lnor_ed->clnors_data); } BM_loop_normal_editdata_array_free(lnors_ed_arr); - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } continue; } @@ -2133,7 +2223,12 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op) } if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) { - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } if (lnors_ed_arr != NULL) { @@ -2255,7 +2350,12 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -2341,7 +2441,12 @@ static int edbm_hide_exec(bContext *C, wmOperator *op) } if (EDBM_mesh_hide(em, unselected)) { - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); changed = true; } } @@ -2392,7 +2497,12 @@ static int edbm_reveal_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); if (EDBM_mesh_reveal(em, select)) { - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } } MEM_freeN(objects); @@ -2458,7 +2568,12 @@ static int edbm_normals_make_consistent_exec(bContext *C, wmOperator *op) } } - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -2570,7 +2685,12 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) EDBM_verts_mirror_cache_end(em); } - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -2694,7 +2814,12 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) EDBM_verts_mirror_cache_end(em); } - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -2787,7 +2912,12 @@ static int edbm_faces_shade_smooth_exec(bContext *C, wmOperator *UNUSED(op)) } mesh_set_smooth_faces(em, 1); - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -2830,7 +2960,12 @@ static int edbm_faces_shade_flat_exec(bContext *C, wmOperator *UNUSED(op)) } mesh_set_smooth_faces(em, 0); - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -2887,7 +3022,12 @@ static int edbm_rotate_uvs_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -2920,7 +3060,12 @@ static int edbm_reverse_uvs_exec(bContext *C, wmOperator *op) if (!EDBM_op_finish(em, &bmop, op, true)) { continue; } - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -2958,7 +3103,12 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op) } /* dependencies graph and notification stuff */ - EDBM_update_generic(ob->data, false, false); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -2994,7 +3144,12 @@ static int edbm_reverse_colors_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -3243,7 +3398,12 @@ static int edbm_merge_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(em->bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); /* once collapsed, we can't have edge/face selection */ if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { @@ -3422,7 +3582,12 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) if (count) { count_multi += count; - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } } MEM_freeN(objects); @@ -3522,7 +3687,12 @@ static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op) tot_shapekeys++; } - EDBM_update_generic(me, false, false); + EDBM_update(me, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -3644,7 +3814,12 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op) interp_v3_v3v3(eve->co, eve->co, co, blend); } } - EDBM_update_generic(me, true, false); + EDBM_update(me, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = false, + }); } } MEM_freeN(objects); @@ -3781,7 +3956,12 @@ static int edbm_solidify_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -4072,7 +4252,7 @@ static int edbm_knife_cut_exec(bContext *C, wmOperator *op) const float *sco_a = screen_vert_coords[BM_elem_index_get(be->v1)]; const float *sco_b = screen_vert_coords[BM_elem_index_get(be->v2)]; - /* check for error value (vert cant be projected) */ + /* check for error value (vert can't be projected) */ if ((sco_a[0] != FLT_MAX) && (sco_b[0] != FLT_MAX)) { isect = bm_edge_seg_isect(sco_a, sco_b, mouse_path, len, mode, &isected); @@ -4113,7 +4293,12 @@ static int edbm_knife_cut_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); return OPERATOR_FINISHED; } @@ -4516,7 +4701,12 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) } if (retval) { - EDBM_update_generic(base->object->data, true, true); + EDBM_update(base->object->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } } MEM_freeN(bases); @@ -4664,7 +4854,12 @@ static int edbm_fill_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -4958,7 +5153,12 @@ static int edbm_fill_grid_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5030,7 +5230,12 @@ static int edbm_fill_holes_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5113,7 +5318,12 @@ static int edbm_beautify_fill_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5197,9 +5407,12 @@ static int edbm_poke_face_exec(bContext *C, wmOperator *op) continue; } - EDBM_mesh_normals_update(em); - - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5301,7 +5514,12 @@ static int edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(em->bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5413,7 +5631,12 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(em->bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5597,7 +5820,12 @@ static int edbm_decimate_exec(bContext *C, wmOperator *op) } EDBM_selectmode_flush_ex(em, selectmode); } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5736,7 +5964,12 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(em->bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5797,7 +6030,12 @@ static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(em->bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -5858,7 +6096,12 @@ static int edbm_dissolve_faces_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(em->bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -6003,7 +6246,12 @@ static int edbm_dissolve_limited_exec(bContext *C, wmOperator *op) BM_custom_loop_normals_from_vector_layer(em->bm, false); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -6090,7 +6338,12 @@ static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op) /* tricky to maintain correct selection here, so just flush up from verts */ EDBM_select_flush(em); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); totelem_new[0] += bm->totvert; totelem_new[1] += bm->totedge; @@ -6181,7 +6434,12 @@ static int edbm_delete_edgeloop_exec(bContext *C, wmOperator *op) EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -6242,10 +6500,13 @@ static int edbm_split_exec(bContext *C, wmOperator *op) continue; } - /* Geometry has changed, need to recalc normals and looptris */ - EDBM_mesh_normals_update(em); - - EDBM_update_generic(obedit->data, true, true); + /* Geometry has changed, need to recalculate normals and tessellation. */ + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -7085,7 +7346,12 @@ static int edbm_bridge_edge_loops_for_single_editmesh(wmOperator *op, } if (EDBM_op_finish(em, &bmop, op, true)) { - EDBM_update_generic(me, true, true); + EDBM_update(me, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } /* Always return finished so the user can select different options. */ @@ -7220,7 +7486,12 @@ static int edbm_wireframe_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); } MEM_freeN(objects); @@ -7311,7 +7582,12 @@ static int edbm_offset_edgeloop_exec(bContext *C, wmOperator *op) em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); if (EDBM_op_finish(em, &bmop, op, true)) { - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); changed_multi = true; } } @@ -7441,7 +7717,12 @@ static int edbm_convex_hull_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); EDBM_selectmode_flush(em); } @@ -7529,7 +7810,12 @@ static int mesh_symmetrize_exec(bContext *C, wmOperator *op) if (!EDBM_op_finish(em, &bmop, op, true)) { continue; } - EDBM_update_generic(obedit->data, true, true); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); EDBM_selectmode_flush(em); } MEM_freeN(objects); @@ -7671,7 +7957,12 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) } } } - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); /* No need to end cache, just free the array. */ MEM_freeN(index); @@ -8347,7 +8638,12 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent * if (point_normals_ensure(C, op)) { point_normals_apply(C, op, target, do_reset); - EDBM_update_generic(obedit->data, true, false); /* Recheck bools. */ + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); /* Recheck bools. */ point_normals_update_header(C, op); } else { @@ -8404,7 +8700,12 @@ static int edbm_point_normals_exec(bContext *C, wmOperator *op) point_normals_apply(C, op, target, false); - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); point_normals_cancel(C, op); return OPERATOR_FINISHED; @@ -8663,7 +8964,12 @@ static int normals_split_merge(bContext *C, const bool do_merge) BM_loop_normal_editdata_array_free(lnors_ed_arr); } - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -8875,7 +9181,12 @@ static int edbm_average_normals_exec(bContext *C, wmOperator *op) } while ((l_curr = l_curr->next) != l_first); } - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } BLI_heapsimple_free(loop_weight, NULL); @@ -9126,7 +9437,12 @@ static int edbm_normals_tools_exec(bContext *C, wmOperator *op) BM_loop_normal_editdata_array_free(lnors_ed_arr); - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -9280,7 +9596,12 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) MEM_freeN(loop_set); MEM_freeN(vnors); - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -9388,7 +9709,12 @@ static int edbm_smooth_normals_exec(bContext *C, wmOperator *op) BM_loop_normal_editdata_array_free(lnors_ed_arr); MEM_freeN(smooth_normal); - EDBM_update_generic(obedit->data, true, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); @@ -9477,7 +9803,12 @@ static int edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op) } } - EDBM_update_generic(obedit->data, false, false); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = false, + .is_destructive = false, + }); } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 274f4cdbb6c..112de68b52c 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -626,7 +626,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo # ifdef USE_ARRAY_STORE_THREAD if (um_arraystore.task_pool == NULL) { - um_arraystore.task_pool = BLI_task_pool_create_background(NULL, TASK_PRIORITY_LOW, true); + 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/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 19c9909039c..9c97bdd6fde 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -438,7 +438,7 @@ void EDBM_selectmode_to_scene(bContext *C) void EDBM_selectmode_flush_ex(BMEditMesh *em, const short selectmode) { - BM_mesh_select_mode_flush_ex(em->bm, selectmode); + BM_mesh_select_mode_flush_ex(em->bm, selectmode, BM_SELECT_LEN_FLUSH_RECALC_ALL); } void EDBM_selectmode_flush(BMEditMesh *em) @@ -1222,12 +1222,12 @@ BMVert *EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v) BMEdge *EDBM_verts_mirror_get_edge(BMEditMesh *em, BMEdge *e) { - BMVert *v1_mirr = EDBM_verts_mirror_get(em, e->v1); - if (v1_mirr) { - BMVert *v2_mirr = EDBM_verts_mirror_get(em, e->v2); - if (v2_mirr) { - return BM_edge_exists(v1_mirr, v2_mirr); - } + BMVert *v1_mirr, *v2_mirr; + if ((v1_mirr = EDBM_verts_mirror_get(em, e->v1)) && + (v2_mirr = EDBM_verts_mirror_get(em, e->v2)) && + /* While highly unlikely, a zero length central edges vertices can match, see T89342. */ + LIKELY(v1_mirr != v2_mirr)) { + return BM_edge_exists(v1_mirr, v2_mirr); } return NULL; @@ -1405,9 +1405,17 @@ bool EDBM_mesh_reveal(BMEditMesh *em, bool select) /** \name Update API * \{ */ +void EDBM_mesh_normals_update_ex(BMEditMesh *em, const struct BMeshNormalsUpdate_Params *params) +{ + BM_mesh_normals_update_ex(em->bm, params); +} + void EDBM_mesh_normals_update(BMEditMesh *em) { - BM_mesh_normals_update(em->bm); + EDBM_mesh_normals_update_ex(em, + &(const struct BMeshNormalsUpdate_Params){ + .face_normals = true, + }); } void EDBM_stats_update(BMEditMesh *em) @@ -1439,20 +1447,31 @@ void EDBM_stats_update(BMEditMesh *em) } } -/* so many tools call these that we better make it a generic function. +/** + * So many tools call these that we better make it a generic function. */ -void EDBM_update_generic(Mesh *mesh, const bool do_tessellation, const bool is_destructive) +void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params) { BMEditMesh *em = mesh->edit_mesh; /* Order of calling isn't important. */ DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &mesh->id); - if (do_tessellation) { - BKE_editmesh_looptri_calc(em); + if (params->calc_normals && params->calc_looptri) { + /* Calculating both has some performance gains. */ + BKE_editmesh_looptri_and_normals_calc(em); } + else { + if (params->calc_normals) { + EDBM_mesh_normals_update(em); + } - if (is_destructive) { + if (params->calc_looptri) { + BKE_editmesh_looptri_calc(em); + } + } + + if (params->is_destructive) { /* TODO. we may be able to remove this now! - Campbell */ // BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP); } @@ -1477,6 +1496,17 @@ void EDBM_update_generic(Mesh *mesh, const bool do_tessellation, const bool is_d #endif } +/* Bad level call from Python API. */ +void EDBM_update_extern(struct Mesh *me, const bool do_tessellation, const bool is_destructive) +{ + EDBM_update(me, + &(const struct EDBMUpdate_Params){ + .calc_looptri = do_tessellation, + .calc_normals = false, + .is_destructive = is_destructive, + }); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1544,7 +1574,7 @@ int EDBM_elem_to_index_any(BMEditMesh *em, BMElem *ele) return index; } -BMElem *EDBM_elem_from_index_any(BMEditMesh *em, int index) +BMElem *EDBM_elem_from_index_any(BMEditMesh *em, uint index) { BMesh *bm = em->bm; @@ -1585,14 +1615,14 @@ int EDBM_elem_to_index_any_multi(ViewLayer *view_layer, } BMElem *EDBM_elem_from_index_any_multi(ViewLayer *view_layer, - int object_index, - int elem_index, + uint object_index, + uint elem_index, Object **r_obedit) { uint bases_len; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(view_layer, NULL, &bases_len); *r_obedit = NULL; - Object *obedit = ((uint)object_index < bases_len) ? bases[object_index]->object : NULL; + Object *obedit = (object_index < bases_len) ? bases[object_index]->object : NULL; MEM_freeN(bases); if (obedit != NULL) { BMEditMesh *em = BKE_editmesh_from_object(obedit); diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 763bdf04d83..9e2e2786347 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -77,15 +77,15 @@ struct BMElem *EDBM_elem_from_selectmode(struct BMEditMesh *em, struct BMFace *efa); int EDBM_elem_to_index_any(struct BMEditMesh *em, struct BMElem *ele); -struct BMElem *EDBM_elem_from_index_any(struct BMEditMesh *em, int index); +struct BMElem *EDBM_elem_from_index_any(struct BMEditMesh *em, uint index); int EDBM_elem_to_index_any_multi(struct ViewLayer *view_layer, struct BMEditMesh *em, struct BMElem *ele, int *r_object_index); struct BMElem *EDBM_elem_from_index_any_multi(struct ViewLayer *view_layer, - int object_index, - int elem_index, + uint object_index, + uint elem_index, struct Object **r_obedit); bool edbm_extrude_edges_indiv(struct BMEditMesh *em, diff --git a/source/blender/editors/mesh/mesh_mirror.c b/source/blender/editors/mesh/mesh_mirror.c index 0f746dfd3a0..25d3eaf11d4 100644 --- a/source/blender/editors/mesh/mesh_mirror.c +++ b/source/blender/editors/mesh/mesh_mirror.c @@ -309,7 +309,7 @@ void ED_mesh_mirrtopo_init(BMEditMesh *em, last = 0; /* Get the pairs out of the sorted hashes, note, totvert+1 means we can use the previous 2, - * but you cant ever access the last 'a' index of MirrTopoPairs */ + * but you can't ever access the last 'a' index of MirrTopoPairs */ if (em) { BMVert **vtable = em->bm->vtable; for (a = 1; a <= totvert; a++) { diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 3450d61337c..f306612f295 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -149,7 +149,7 @@ static void join_mesh_single(Depsgraph *depsgraph, mul_m4_m4m4(cmat, imat, ob_src->obmat); /* transform vertex coordinates into new space */ - for (a = 0, mvert = *mvert_pp; a < me->totvert; a++, mvert++) { + for (a = 0; a < me->totvert; a++, mvert++) { mul_m4_v3(cmat, mvert->co); } diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 12aaa9c2d9f..b677a2f96c3 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -456,49 +456,53 @@ void ED_object_add_mesh_props(wmOperatorType *ot) bool ED_object_add_generic_get_opts(bContext *C, wmOperator *op, const char view_align_axis, - float loc[3], - float rot[3], - float scale[3], - bool *enter_editmode, - ushort *local_view_bits, - bool *is_view_aligned) -{ - PropertyRNA *prop; - - /* Switch to Edit mode? optional prop */ - if ((prop = RNA_struct_find_property(op->ptr, "enter_editmode"))) { + float r_loc[3], + float r_rot[3], + float r_scale[3], + bool *r_enter_editmode, + ushort *r_local_view_bits, + bool *r_is_view_aligned) +{ + /* Edit Mode! (optional) */ + { bool _enter_editmode; - if (!enter_editmode) { - enter_editmode = &_enter_editmode; + if (!r_enter_editmode) { + r_enter_editmode = &_enter_editmode; } + /* Only to ensure the value is _always_ set. + * Typically the property will exist when the argument is non-NULL. */ + *r_enter_editmode = false; - if (RNA_property_is_set(op->ptr, prop) && enter_editmode) { - *enter_editmode = RNA_property_boolean_get(op->ptr, prop); - } - else { - *enter_editmode = (U.flag & USER_ADD_EDITMODE) != 0; - RNA_property_boolean_set(op->ptr, prop, *enter_editmode); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "enter_editmode"); + if (prop != NULL) { + if (RNA_property_is_set(op->ptr, prop) && r_enter_editmode) { + *r_enter_editmode = RNA_property_boolean_get(op->ptr, prop); + } + else { + *r_enter_editmode = (U.flag & USER_ADD_EDITMODE) != 0; + RNA_property_boolean_set(op->ptr, prop, *r_enter_editmode); + } } } - if (local_view_bits) { + if (r_local_view_bits) { View3D *v3d = CTX_wm_view3d(C); - *local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + *r_local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; } /* Location! */ { float _loc[3]; - if (!loc) { - loc = _loc; + if (!r_loc) { + r_loc = _loc; } if (RNA_struct_property_is_set(op->ptr, "location")) { - RNA_float_get_array(op->ptr, "location", loc); + RNA_float_get_array(op->ptr, "location", r_loc); } else { - ED_object_location_from_view(C, loc); - RNA_float_set_array(op->ptr, "location", loc); + ED_object_location_from_view(C, r_loc); + RNA_float_set_array(op->ptr, "location", r_loc); } } @@ -506,33 +510,33 @@ bool ED_object_add_generic_get_opts(bContext *C, { bool _is_view_aligned; float _rot[3]; - if (!is_view_aligned) { - is_view_aligned = &_is_view_aligned; + if (!r_is_view_aligned) { + r_is_view_aligned = &_is_view_aligned; } - if (!rot) { - rot = _rot; + if (!r_rot) { + r_rot = _rot; } if (RNA_struct_property_is_set(op->ptr, "rotation")) { /* If rotation is set, always use it. Alignment (and corresponding user preference) * can be ignored since this is in world space anyways. * To not confuse (e.g. on redo), don't set it to #ALIGN_WORLD in the op UI though. */ - *is_view_aligned = false; - RNA_float_get_array(op->ptr, "rotation", rot); + *r_is_view_aligned = false; + RNA_float_get_array(op->ptr, "rotation", r_rot); } else { int alignment = ALIGN_WORLD; - prop = RNA_struct_find_property(op->ptr, "align"); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "align"); if (RNA_property_is_set(op->ptr, prop)) { /* If alignment is set, always use it. */ - *is_view_aligned = alignment == ALIGN_VIEW; + *r_is_view_aligned = alignment == ALIGN_VIEW; alignment = RNA_property_enum_get(op->ptr, prop); } else { /* If alignment is not set, use User Preferences. */ - *is_view_aligned = (U.flag & USER_ADD_VIEWALIGNED) != 0; - if (*is_view_aligned) { + *r_is_view_aligned = (U.flag & USER_ADD_VIEWALIGNED) != 0; + if (*r_is_view_aligned) { RNA_property_enum_set(op->ptr, prop, ALIGN_VIEW); alignment = ALIGN_VIEW; } @@ -547,18 +551,18 @@ bool ED_object_add_generic_get_opts(bContext *C, } switch (alignment) { case ALIGN_WORLD: - RNA_float_get_array(op->ptr, "rotation", rot); + RNA_float_get_array(op->ptr, "rotation", r_rot); break; case ALIGN_VIEW: - ED_object_rotation_from_view(C, rot, view_align_axis); - RNA_float_set_array(op->ptr, "rotation", rot); + ED_object_rotation_from_view(C, r_rot, view_align_axis); + RNA_float_set_array(op->ptr, "rotation", r_rot); break; case ALIGN_CURSOR: { const Scene *scene = CTX_data_scene(C); float tmat[3][3]; BKE_scene_cursor_rot_to_mat3(&scene->cursor, tmat); - mat3_normalized_to_eul(rot, tmat); - RNA_float_set_array(op->ptr, "rotation", rot); + mat3_normalized_to_eul(r_rot, tmat); + RNA_float_set_array(op->ptr, "rotation", r_rot); break; } } @@ -568,19 +572,21 @@ bool ED_object_add_generic_get_opts(bContext *C, /* Scale! */ { float _scale[3]; - if (!scale) { - scale = _scale; + if (!r_scale) { + r_scale = _scale; } /* For now this is optional, we can make it always use. */ - copy_v3_fl(scale, 1.0f); - if ((prop = RNA_struct_find_property(op->ptr, "scale"))) { + copy_v3_fl(r_scale, 1.0f); + + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "scale"); + if (prop != NULL) { if (RNA_property_is_set(op->ptr, prop)) { - RNA_property_float_get_array(op->ptr, prop, scale); + RNA_property_float_get_array(op->ptr, prop, r_scale); } else { - copy_v3_fl(scale, 1.0f); - RNA_property_float_set_array(op->ptr, prop, scale); + copy_v3_fl(r_scale, 1.0f); + RNA_property_float_set_array(op->ptr, prop, r_scale); } } } @@ -2642,10 +2648,10 @@ static Base *duplibase_for_convert( ED_object_base_select(basen, BA_SELECT); ED_object_base_select(base, BA_DESELECT); - /* XXX An ugly hack needed because if we re-run depsgraph with some new MBall objects - * having same 'family name' as orig ones, they will affect end result of MBall computation... + /* XXX: An ugly hack needed because if we re-run depsgraph with some new meta-ball objects + * having same 'family name' as orig ones, they will affect end result of meta-ball computation. * For until we get rid of that name-based thingy in MBalls, that should do the trick - * (this is weak, but other solution (to change name of obn) is even worse imho). + * (this is weak, but other solution (to change name of `obn`) is even worse imho). * See T65996. */ const bool is_meta_ball = (obn->type == OB_MBALL); void *obdata = obn->data; @@ -3462,6 +3468,19 @@ void OBJECT_OT_duplicate(wmOperatorType *ot) * Use for drag & drop. * \{ */ +static Base *object_add_ensure_in_view_layer(Main *bmain, ViewLayer *view_layer, Object *ob) +{ + Base *base = BKE_view_layer_base_find(view_layer, ob); + + if (!base) { + LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + base = BKE_view_layer_base_find(view_layer, ob); + } + + return base; +} + static int object_add_named_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -3469,7 +3488,8 @@ static int object_add_named_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); Base *basen; Object *ob; - const bool linked = RNA_boolean_get(op->ptr, "linked"); + const bool duplicate = RNA_boolean_get(op->ptr, "duplicate"); + const bool linked = duplicate && RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; char name[MAX_ID_NAME - 2]; @@ -3483,20 +3503,30 @@ static int object_add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - basen = object_add_duplicate_internal( - bmain, - scene, - view_layer, - ob, - dupflag, - /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this - * function will only work if the object is already linked in the view layer, which is not - * the case here. So we have to do the new-ID relinking ourselves (#copy_object_set_idnew()). - */ - LIB_ID_DUPLICATE_IS_SUBPROCESS); + if (duplicate) { + basen = object_add_duplicate_internal( + bmain, + scene, + view_layer, + ob, + dupflag, + /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this + * function will only work if the object is already linked in the view layer, which is not + * the case here. So we have to do the new-ID relinking ourselves + * (#copy_object_set_idnew()). + */ + LIB_ID_DUPLICATE_IS_SUBPROCESS); + } + else { + /* basen is actually not a new base in this case. */ + basen = object_add_ensure_in_view_layer(bmain, view_layer, ob); + } if (basen == NULL) { - BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); + BKE_report(op->reports, + RPT_ERROR, + duplicate ? "Object could not be duplicated" : + "Object could not be linked to the view layer"); return OPERATOR_CANCELLED; } @@ -3543,11 +3573,24 @@ void OBJECT_OT_add_named(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + PropertyRNA *prop; + + prop = RNA_def_boolean( + ot->srna, + "duplicate", + true, + "Duplicate", + "Create a duplicate of the object. If not set, only ensures the object is linked into the " + "active view layer, positions and selects/activates it (deselecting others)"); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_boolean(ot->srna, "linked", - 0, + false, "Linked", - "Duplicate object but not object data, linking to the original data"); + "Duplicate object but not object data, linking to the original data (ignored if " + "'duplicate' is false)"); + RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); object_add_drop_xy_props(ot); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 3370476d466..7f26d44a4ed 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -158,7 +158,7 @@ static int bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) /** * for exec() when there is no render job - * note: this wont check for the escape key being pressed, but doing so isn't thread-safe. + * note: this won't check for the escape key being pressed, but doing so isn't thread-safe. */ static int bake_break(void *UNUSED(rjv)) { diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 244124a6e0a..06d6f2b94f3 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -696,12 +696,11 @@ static bool edit_constraint_poll_generic(bContext *C, Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); bConstraint *con = ptr.data; - if (!ob) { - CTX_wm_operator_poll_msg_set(C, "Context missing active object"); + if (!ED_operator_object_active_editable_ex(C, ob)) { return false; } - if (ID_IS_LINKED(ob) || (ptr.owner_id && ID_IS_LINKED(ptr.owner_id))) { + if (ptr.owner_id != NULL && ID_IS_LINKED(ptr.owner_id)) { CTX_wm_operator_poll_msg_set(C, "Cannot edit library data"); return false; } @@ -1746,8 +1745,8 @@ void POSE_OT_constraints_clear(wmOperatorType *ot) /* callbacks */ ot->exec = pose_constraints_clear_exec; - ot->poll = ED_operator_posemode_exclusive; /* XXX - do we want to ensure there are selected - * bones too? */ + /* XXX - do we want to ensure there are selected bones too? */ + ot->poll = ED_operator_object_active_local_editable_posemode_exclusive; } static int object_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op)) @@ -2480,7 +2479,7 @@ void POSE_OT_ik_clear(wmOperatorType *ot) /* api callbacks */ ot->exec = pose_ik_clear_exec; - ot->poll = ED_operator_posemode_exclusive; + ot->poll = ED_operator_object_active_local_editable_posemode_exclusive; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c index 6e3a5e715f6..4a4ace309e1 100644 --- a/source/blender/editors/object/object_data_transform.c +++ b/source/blender/editors/object/object_data_transform.c @@ -745,8 +745,12 @@ void ED_object_data_xform_tag_update(struct XFormObjectData *xod_base) case ID_ME: { Mesh *me = (Mesh *)xod_base->id; if (xod_base->is_edit_mode) { - EDBM_update_generic(me, true, false); - EDBM_mesh_normals_update(me->edit_mesh); + EDBM_update(me, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = false, + }); } DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); break; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 5be572baec5..c8923bb55c1 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -794,9 +794,7 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag BMEditMesh *em = BKE_editmesh_from_object(ob); if (LIKELY(em)) { - /* order doesn't matter */ - EDBM_mesh_normals_update(em); - BKE_editmesh_looptri_calc(em); + BKE_editmesh_looptri_and_normals_calc(em); } WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MESH, NULL); diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index d56ee17a73f..7122fd09892 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -350,8 +350,7 @@ static bool object_hook_index_array(Main *bmain, em = me->edit_mesh; - EDBM_mesh_normals_update(em); - BKE_editmesh_looptri_calc(em); + BKE_editmesh_looptri_and_normals_calc(em); /* check selected vertices first */ if (return_editmesh_indexar(em, r_tot, r_indexar, r_cent) == 0) { diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 3d1a5ac2d62..ed06cd2a217 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -484,7 +484,9 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base ob_dst_orig = DEG_get_original_object(ob_dst); ED_object_mode_set_ex(C, last_mode, true, op->reports); - object_overlay_mode_transfer_animation_start(C, ob_dst); + if (RNA_boolean_get(op->ptr, "use_flash_on_transfer")) { + object_overlay_mode_transfer_animation_start(C, ob_dst); + } WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); WM_toolsystem_update_from_context_view3d(C); @@ -578,6 +580,12 @@ void OBJECT_OT_transfer_mode(wmOperatorType *ot) false, "Use Eyedropper", "Pick the object to switch to using an eyedropper"); + + RNA_def_boolean(ot->srna, + "use_flash_on_transfer", + true, + "Flash On Transfer", + "Flash the target object when transfering the mode"); } /** \} */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index f3433833b5f..cdf12bcb5df 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -153,8 +153,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) em = me->edit_mesh; - EDBM_mesh_normals_update(em); - BKE_editmesh_looptri_calc(em); + BKE_editmesh_looptri_and_normals_calc(em); /* Make sure the evaluated mesh is updated. * diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 1ff576504ce..c04c91d21b5 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -139,6 +139,13 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (mesh->totpoly == 0) { + return OPERATOR_CANCELLED; + } + + /* Output mesh will be all smooth or all flat shading. */ + const bool smooth_normals = mesh->mpoly[0].flag & ME_SMOOTH; + float isovalue = 0.0f; if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) { isovalue = mesh->remesh_voxel_size * 0.3f; @@ -185,7 +192,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); - if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) { + if (smooth_normals) { BKE_mesh_smooth_flag_set(ob->data, true); } diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c index 585a1e22a84..2723d7ad1e3 100644 --- a/source/blender/editors/object/object_shader_fx.c +++ b/source/blender/editors/object/object_shader_fx.c @@ -64,7 +64,9 @@ #include "object_intern.h" -/******************************** API ****************************/ +/* -------------------------------------------------------------------- */ +/** \name Public API + * \{ */ ShaderFxData *ED_object_shaderfx_add( ReportList *reports, Main *bmain, Scene *UNUSED(scene), Object *ob, const char *name, int type) @@ -261,7 +263,59 @@ void ED_object_shaderfx_copy(Object *dst, ShaderFxData *fx) WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, dst); } -/************************ add effect operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generic Poll Callback Helpers + * \{ */ + +static bool edit_shaderfx_poll_generic(bContext *C, + StructRNA *rna_type, + int obtype_flag, + const bool is_liboverride_allowed) +{ + PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", rna_type); + Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); + ShaderFxData *fx = ptr.data; /* May be NULL. */ + + if (!ED_operator_object_active_editable_ex(C, ob)) { + return false; + } + + /* NOTE: Temporary 'forbid all' for overrides, until we implement support to add shaderfx to + * overrides. */ + if (ID_IS_OVERRIDE_LIBRARY(ob)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit shaderfxs in a library override"); + return false; + } + + if (obtype_flag != 0 && ((1 << ob->type) & obtype_flag) == 0) { + CTX_wm_operator_poll_msg_set(C, "Object type is not supported"); + return false; + } + if (ptr.owner_id != NULL && ID_IS_LINKED(ptr.owner_id)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit library data"); + return false; + } + if (!is_liboverride_allowed && BKE_shaderfx_is_nonlocal_in_liboverride(ob, fx)) { + CTX_wm_operator_poll_msg_set( + C, "Cannot edit shaderfxs coming from linked data in a library override"); + return false; + } + + return true; +} + +static bool edit_shaderfx_poll(bContext *C) +{ + return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0, false); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Effect Operator + * \{ */ static int shaderfx_add_exec(bContext *C, wmOperator *op) { @@ -334,7 +388,7 @@ void OBJECT_OT_shaderfx_add(wmOperatorType *ot) /* api callbacks */ ot->invoke = WM_menu_invoke; ot->exec = shaderfx_add_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = edit_shaderfx_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -352,37 +406,6 @@ void OBJECT_OT_shaderfx_add(wmOperatorType *ot) /** \name Generic Functions for Operators Using Names and Data Context * \{ */ -static bool edit_shaderfx_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag) -{ - PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", rna_type); - Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); - ShaderFxData *fx = ptr.data; /* May be NULL. */ - - if (!ob || ID_IS_LINKED(ob)) { - return false; - } - if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) { - return false; - } - if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) { - return false; - } - - if (ID_IS_OVERRIDE_LIBRARY(ob)) { - if ((fx == NULL) || (fx->flag & eShaderFxFlag_OverrideLibrary_Local) == 0) { - CTX_wm_operator_poll_msg_set(C, "Cannot edit shaderfxs coming from library override"); - return false; - } - } - - return true; -} - -static bool edit_shaderfx_poll(bContext *C) -{ - return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0); -} - static void edit_shaderfx_properties(wmOperatorType *ot) { PropertyRNA *prop = RNA_def_string( @@ -461,7 +484,9 @@ static ShaderFxData *edit_shaderfx_property_get(wmOperator *op, Object *ob, int /** \} */ -/************************ remove shaderfx operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Remove ShaderFX Operator + * \{ */ static int shaderfx_remove_exec(bContext *C, wmOperator *op) { @@ -511,7 +536,11 @@ void OBJECT_OT_shaderfx_remove(wmOperatorType *ot) edit_shaderfx_report_property(ot); } -/************************ move up shaderfx operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Move up ShaderFX Operator + * \{ */ static int shaderfx_move_up_exec(bContext *C, wmOperator *op) { @@ -552,7 +581,11 @@ void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot) edit_shaderfx_properties(ot); } -/************************ move down shaderfx operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Move Down ShaderFX Operator + * \{ */ static int shaderfx_move_down_exec(bContext *C, wmOperator *op) { @@ -593,12 +626,11 @@ void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot) edit_shaderfx_properties(ot); } -/************************ move shaderfx to index operator *********************/ +/** \} */ -static bool shaderfx_move_to_index_poll(bContext *C) -{ - return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0); -} +/* -------------------------------------------------------------------- */ +/** \name Move ShaderFX to Index Operator + * \{ */ static int shaderfx_move_to_index_exec(bContext *C, wmOperator *op) { @@ -632,7 +664,7 @@ void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot) ot->invoke = shaderfx_move_to_index_invoke; ot->exec = shaderfx_move_to_index_exec; - ot->poll = shaderfx_move_to_index_poll; + ot->poll = edit_shaderfx_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; @@ -641,7 +673,11 @@ void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot) ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the effect to", 0, INT_MAX); } -/************************ copy shader operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Copy Shader Operator + * \{ */ static int shaderfx_copy_exec(bContext *C, wmOperator *op) { @@ -675,11 +711,6 @@ static int shaderfx_copy_invoke(bContext *C, wmOperator *op, const wmEvent *even return retval; } -static bool shaderfx_copy_poll(bContext *C) -{ - return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0); -} - void OBJECT_OT_shaderfx_copy(wmOperatorType *ot) { ot->name = "Copy Effect"; @@ -688,9 +719,11 @@ void OBJECT_OT_shaderfx_copy(wmOperatorType *ot) ot->invoke = shaderfx_copy_invoke; ot->exec = shaderfx_copy_exec; - ot->poll = shaderfx_copy_poll; + ot->poll = edit_shaderfx_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_shaderfx_properties(ot); } + +/** \} */ diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index b9a3bc87e19..94b2f3fd566 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1615,6 +1615,7 @@ struct XFormAxisItem { struct XFormAxisData { ViewContext vc; + ViewDepths *depths; struct { float depth; float normal[3]; @@ -1684,8 +1685,8 @@ static void object_transform_axis_target_free_data(wmOperator *op) struct XFormAxisItem *item = xfd->object_data; #ifdef USE_RENDER_OVERRIDE - if (xfd->vc.rv3d->depths) { - xfd->vc.rv3d->depths->damaged = true; + if (xfd->depths) { + ED_view3d_depths_free(xfd->depths); } #endif @@ -1782,13 +1783,14 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons vc.v3d->flag2 |= V3D_HIDE_OVERLAYS; #endif - ED_view3d_depth_override(vc.depsgraph, vc.region, vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, true); + ViewDepths *depths = NULL; + ED_view3d_depth_override(vc.depsgraph, vc.region, vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, &depths); #ifdef USE_RENDER_OVERRIDE vc.v3d->flag2 = flag2_prev; #endif - if (vc.rv3d->depths == NULL) { + if (depths == NULL) { BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane"); return OPERATOR_CANCELLED; } @@ -1800,6 +1802,7 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons /* Don't change this at runtime. */ xfd->vc = vc; + xfd->depths = depths; xfd->vc.mval[0] = event->mval[0]; xfd->vc.mval[1] = event->mval[1]; @@ -1863,7 +1866,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const const bool is_translate_init = is_translate && (xfd->is_translate != is_translate); if (event->type == MOUSEMOVE || is_translate_init) { - const ViewDepths *depths = xfd->vc.rv3d->depths; + const ViewDepths *depths = xfd->depths; if (depths && ((uint)event->mval[0] < depths->w) && ((uint)event->mval[1] < depths->h)) { float depth_fl = 1.0f; ED_view3d_depth_read_cached(depths, event->mval, 0, &depth_fl); @@ -1895,7 +1898,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const float normal[3]; bool normal_found = false; - if (ED_view3d_depth_read_cached_normal(&xfd->vc, event->mval, normal)) { + if (ED_view3d_depth_read_cached_normal(region, depths, event->mval, normal)) { normal_found = true; /* cheap attempt to smooth normals out a bit! */ @@ -1905,7 +1908,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const if (x != 0 && y != 0) { const int mval_ofs[2] = {event->mval[0] + x, event->mval[1] + y}; float n[3]; - if (ED_view3d_depth_read_cached_normal(&xfd->vc, mval_ofs, n)) { + if (ED_view3d_depth_read_cached_normal(region, depths, mval_ofs, n)) { add_v3_v3(normal, n); } } diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 97994b65f40..2bf0f842623 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -466,6 +466,7 @@ static int pe_x_mirror(Object *ob) typedef struct PEData { ViewContext vc; + ViewDepths *depths; const bContext *context; Main *bmain; @@ -524,14 +525,12 @@ static void PE_set_view3d_data(bContext *C, PEData *data) ED_view3d_viewcontext_init(C, &data->vc, data->depsgraph); if (!XRAY_ENABLED(data->vc.v3d)) { - if (!(data->vc.v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN)) { - ED_view3d_depth_override(data->depsgraph, - data->vc.region, - data->vc.v3d, - data->vc.obact, - V3D_DEPTH_OBJECT_ONLY, - true); - } + ED_view3d_depth_override(data->depsgraph, + data->vc.region, + data->vc.v3d, + data->vc.obact, + V3D_DEPTH_OBJECT_ONLY, + &data->depths); } } @@ -570,6 +569,16 @@ static void PE_free_random_generator(PEData *data) } } +static void PE_data_free(PEData *data) +{ + PE_free_random_generator(data); + PE_free_shape_tree(data); + if (data->depths) { + ED_view3d_depths_free(data->depths); + data->depths = NULL; + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -579,7 +588,7 @@ static void PE_free_random_generator(PEData *data) static bool key_test_depth(const PEData *data, const float co[3], const int screen_co[2]) { View3D *v3d = data->vc.v3d; - ViewDepths *vd = data->vc.rv3d->depths; + ViewDepths *vd = data->depths; float depth; /* nothing to do */ @@ -1871,15 +1880,13 @@ void PARTICLE_OT_select_all(wmOperatorType *ot) bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { - PEData data; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); POINT_P; KEY_K; - PE_set_view3d_data(C, &data); - - PTCacheEdit *edit = PE_get_current(data.depsgraph, scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); if (!PE_start_edit(edit)) { return false; @@ -1894,6 +1901,8 @@ bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool desele } } + PEData data; + PE_set_view3d_data(C, &data); data.mval = mval; data.rad = ED_view3d_select_dist_px(); @@ -1913,6 +1922,8 @@ bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool desele WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob); } + PE_data_free(&data); + return true; } @@ -2204,6 +2215,7 @@ static int select_linked_pick_exec(bContext *C, wmOperator *op) for_mouse_hit_keys(&data, select_keys, PSEL_NEAREST); PE_update_selection(data.depsgraph, data.scene, data.ob, 1); WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob); + PE_data_free(&data); return OPERATOR_FINISHED; } @@ -2298,11 +2310,14 @@ bool PE_box_select(bContext *C, const rcti *rect, const int sel_op) for_mouse_hit_keys(&data, select_key_op, PSEL_ALL_KEYS); } - if (data.is_changed) { - PE_update_selection(data.depsgraph, scene, ob, 1); + bool is_changed = data.is_changed; + PE_data_free(&data); + + if (is_changed) { + PE_update_selection(depsgraph, scene, ob, 1); WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob); } - return data.is_changed; + return is_changed; } /** \} */ @@ -2311,35 +2326,53 @@ bool PE_box_select(bContext *C, const rcti *rect, const int sel_op) /** \name Circle Select Operator * \{ */ -bool PE_circle_select(bContext *C, const int sel_op, const int mval[2], float rad) +static void pe_select_cache_free_generic_userdata(void *data) +{ + PE_data_free(data); + MEM_freeN(data); +} + +static void pe_select_cache_init_with_generic_userdata(bContext *C, wmGenericUserData *wm_userdata) +{ + struct PEData *data = MEM_callocN(sizeof(*data), __func__); + wm_userdata->data = data; + wm_userdata->free_fn = pe_select_cache_free_generic_userdata; + wm_userdata->use_free = true; + PE_set_view3d_data(C, data); +} + +bool PE_circle_select( + bContext *C, wmGenericUserData *wm_userdata, const int sel_op, const int mval[2], float rad) { BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB)); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); - PEData data; if (!PE_start_edit(edit)) { return false; } - const bool select = (sel_op != SEL_OP_SUB); + if (wm_userdata->data == NULL) { + pe_select_cache_init_with_generic_userdata(C, wm_userdata); + } - PE_set_view3d_data(C, &data); - data.mval = mval; - data.rad = rad; - data.select = select; + PEData *data = wm_userdata->data; + data->mval = mval; + data->rad = rad; + data->select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - data.is_changed = PE_deselect_all_visible_ex(edit); + data->is_changed = PE_deselect_all_visible_ex(edit); } - for_mouse_hit_keys(&data, select_key, 0); - if (data.is_changed) { - PE_update_selection(data.depsgraph, scene, ob, 1); + for_mouse_hit_keys(data, select_key, 0); + + if (data->is_changed) { + PE_update_selection(depsgraph, scene, ob, 1); WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob); } - return data.is_changed; + return data->is_changed; } /** \} */ @@ -2425,8 +2458,11 @@ int PE_lasso_select(bContext *C, const int mcoords[][2], const int mcoords_len, } } - if (data.is_changed) { - PE_update_selection(data.depsgraph, scene, ob, 1); + bool is_changed = data.is_changed; + PE_data_free(&data); + + if (is_changed) { + PE_update_selection(depsgraph, scene, ob, 1); WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob); return OPERATOR_FINISHED; } @@ -4921,7 +4957,7 @@ static void brush_edit_exit(wmOperator *op) { BrushEdit *bedit = op->customdata; - PE_free_random_generator(&bedit->data); + PE_data_free(&bedit->data); MEM_freeN(bedit); } diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index a94a2b9b764..cbab4fbd3d1 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -403,8 +403,10 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, BLI_path_join( temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL); BLI_path_abs(temp_dir, relbase); - BLI_dir_create_recursive( - temp_dir); /* Create 'particles' subdir if it does not exist already */ + + /* Create 'particles' subdir if it does not exist already */ + BLI_dir_create_recursive(temp_dir); + fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_PARTICLES | FLUID_DOMAIN_OUTDATED_PARTICLES); fds->cache_flag |= FLUID_DOMAIN_BAKING_PARTICLES; job->pause_frame = &fds->cache_frame_pause_particles; diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 0bec509cd7e..d39570857ab 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -285,7 +285,7 @@ static void screen_render_single_layer_set( scn = (Scene *)BLI_findstring(&mainp->scenes, scene_name, offsetof(ID, name) + 2); if (scn) { - /* camera switch wont have updated */ + /* camera switch won't have updated */ scn->r.cfra = (*scene)->r.cfra; BKE_scene_camera_switch_update(scn); @@ -773,7 +773,7 @@ static void render_endjob(void *rjv) * was locked before running the job. */ WM_set_locked_interface(G_MAIN->wm.first, false); - DEG_on_visible_update(G_MAIN, false); + DEG_tag_on_visible_update(G_MAIN, false); } } @@ -793,7 +793,7 @@ static int render_breakjob(void *rjv) /** * For exec() when there is no render job - * note: this wont check for the escape key being pressed, but doing so isn't thread-safe. + * note: this won't check for the escape key being pressed, but doing so isn't thread-safe. */ static int render_break(void *UNUSED(rjv)) { diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index cfc07de3f6c..48f937fb4ec 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -859,7 +859,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->task_pool = BLI_task_pool_create_background_serial(oglrender, TASK_PRIORITY_LOW); } else { - oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW); } oglrender->pool_ok = true; BLI_spin_init(&oglrender->reports_lock); diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index 465438f814a..eb4a040e891 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -164,6 +164,7 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) sizex, sizey, SPACE_IMAGE, + true, false, true, WIN_ALIGN_LOCATION_CENTER) == NULL) { diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index 2b2a0d10e29..5195bc8303a 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -120,7 +120,7 @@ void ED_scene_change_update(Main *bmain, Scene *scene, ViewLayer *layer) BKE_scene_set_background(bmain, scene); DEG_graph_relations_update(depsgraph); - DEG_on_visible_update(bmain, false); + DEG_tag_on_visible_update(bmain, false); ED_render_engine_changed(bmain, false); ED_update_for_newframe(bmain, depsgraph); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 2814a4c9351..377cc60f9db 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1385,7 +1385,7 @@ static void region_rect_recursive( region->flag |= RGN_FLAG_SIZE_CLAMP_Y; } - /* We need to use a test that wont have been previously clamped. */ + /* We need to use a test that won't have been previously clamped. */ rcti winrct_test = { .xmin = region->winrct.xmin, .ymin = region->winrct.ymin, @@ -2387,7 +2387,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi * However, add-on install for example, forces the header to the top which shouldn't * be applied back to the previous space type when closing - see: T57724 * - * Newly created windows wont have any space data, use the alignment + * Newly-created windows won't have any space data, use the alignment * the space type defaults to in this case instead * (needed for preferences to have space-type on bottom). */ diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 6fb5f33d836..21800536503 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -68,7 +68,7 @@ static ScrArea *screen_addarea_ex(ScrAreaMap *area_map, ScrVert *top_left, ScrVert *top_right, ScrVert *bottom_right, - short spacetype) + const eSpace_Type space_type) { ScrArea *area = MEM_callocN(sizeof(ScrArea), "addscrarea"); @@ -76,7 +76,7 @@ static ScrArea *screen_addarea_ex(ScrAreaMap *area_map, area->v2 = top_left; area->v3 = top_right; area->v4 = bottom_right; - area->spacetype = spacetype; + area->spacetype = space_type; BLI_addtail(&area_map->areabase, area); @@ -87,10 +87,10 @@ static ScrArea *screen_addarea(bScreen *screen, ScrVert *left_top, ScrVert *right_top, ScrVert *right_bottom, - short spacetype) + const eSpace_Type space_type) { return screen_addarea_ex( - AREAMAP_FROM_SCREEN(screen), left_bottom, left_top, right_top, right_bottom, spacetype); + AREAMAP_FROM_SCREEN(screen), left_bottom, left_top, right_top, right_bottom, space_type); } static void screen_delarea(bContext *C, bScreen *screen, ScrArea *area) @@ -972,7 +972,7 @@ int ED_screen_area_active(const bContext *C) */ static ScrArea *screen_area_create_with_geometry(ScrAreaMap *area_map, const rcti *rect, - short spacetype) + eSpace_Type space_type) { ScrVert *bottom_left = screen_geom_vertex_add_ex(area_map, rect->xmin, rect->ymin); ScrVert *top_left = screen_geom_vertex_add_ex(area_map, rect->xmin, rect->ymax); @@ -984,7 +984,7 @@ static ScrArea *screen_area_create_with_geometry(ScrAreaMap *area_map, screen_geom_edge_add_ex(area_map, top_right, bottom_right); screen_geom_edge_add_ex(area_map, bottom_right, bottom_left); - return screen_addarea_ex(area_map, bottom_left, top_left, top_right, bottom_right, spacetype); + return screen_addarea_ex(area_map, bottom_left, top_left, top_right, bottom_right, space_type); } static void screen_area_set_geometry_rect(ScrArea *area, const rcti *rect) @@ -1001,7 +1001,7 @@ static void screen_area_set_geometry_rect(ScrArea *area, const rcti *rect) static void screen_global_area_refresh(wmWindow *win, bScreen *screen, - eSpace_Type space_type, + const eSpace_Type space_type, GlobalAreaAlign align, const rcti *rect, const short height_cur, @@ -1275,11 +1275,14 @@ void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene) ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type) { + bScreen *newscreen = NULL; ScrArea *newsa = NULL; SpaceLink *newsl; if (!area || area->full == NULL) { - newsa = ED_screen_state_maximized_create(C); + newscreen = ED_screen_state_maximized_create(C); + newsa = newscreen->areabase.first; + BLI_assert(newsa->spacetype == SPACE_EMPTY); } if (!newsa) { @@ -1296,6 +1299,10 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type) ED_area_newspace(C, newsa, type, (newsl && newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY)); + if (newscreen) { + ED_screen_change(C, newscreen); + } + return newsa; } @@ -1361,6 +1368,10 @@ void ED_screen_full_restore(bContext *C, ScrArea *area) * \param toggle_area: If this is set, its space data will be swapped with the one of the new empty * area, when toggling back it can be swapped back again. * \return The newly created screen with the non-normal area. + * + * \note The caller must run #ED_screen_change this is not done in this function + * as it would attempt to initialize areas that don't yet have a space-type assigned + * (converting them to 3D view without creating the space-data). */ static bScreen *screen_state_to_nonnormal(bContext *C, wmWindow *win, @@ -1429,7 +1440,6 @@ static bScreen *screen_state_to_nonnormal(bContext *C, } newa->full = oldscreen; - ED_screen_change(C, screen); ED_area_tag_refresh(newa); return screen; @@ -1442,10 +1452,9 @@ static bScreen *screen_state_to_nonnormal(bContext *C, * Use this to just create a new maximized screen/area, rather than maximizing an existing one. * Otherwise, maximize with #ED_screen_state_toggle(). */ -ScrArea *ED_screen_state_maximized_create(bContext *C) +bScreen *ED_screen_state_maximized_create(bContext *C) { - bScreen *screen = screen_state_to_nonnormal(C, CTX_wm_window(C), NULL, SCREENMAXIMIZED); - return screen->areabase.first; + return screen_state_to_nonnormal(C, CTX_wm_window(C), NULL, SCREENMAXIMIZED); } /** @@ -1548,6 +1557,8 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const } screen = screen_state_to_nonnormal(C, win, toggle_area, state); + + ED_screen_change(C, screen); } BLI_assert(CTX_wm_screen(C) == screen); @@ -1585,6 +1596,7 @@ ScrArea *ED_screen_temp_space_open(bContext *C, sizex, sizey, (int)space_type, + false, dialog, true, WIN_ALIGN_LOCATION_CENTER)) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 6b8d4e73f12..f0e12ca60e9 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -279,6 +279,11 @@ bool ED_operator_file_active(bContext *C) return ed_spacetype_test(C, SPACE_FILE); } +bool ED_operator_spreadsheet_active(bContext *C) +{ + return ed_spacetype_test(C, SPACE_SPREADSHEET); +} + bool ED_operator_action_active(bContext *C) { return ed_spacetype_test(C, SPACE_ACTION); @@ -358,9 +363,24 @@ bool ED_operator_object_active(bContext *C) return ((ob != NULL) && !ed_object_hidden(ob)); } -bool ED_operator_object_active_editable_ex(bContext *UNUSED(C), const Object *ob) +bool ED_operator_object_active_editable_ex(bContext *C, const Object *ob) { - return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob)); + if (ob == NULL) { + CTX_wm_operator_poll_msg_set(C, "Context missing active object"); + return false; + } + + if (ID_IS_LINKED(ob)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit library linked object"); + return false; + } + + if (ed_object_hidden(ob)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit hidden obect"); + return false; + } + + return true; } bool ED_operator_object_active_editable(bContext *C) @@ -444,28 +464,48 @@ bool ED_operator_editarmature(bContext *C) } /** - * \brief check for pose mode (no mixed modes) + * Check for pose mode (no mixed modes). * - * We want to enable most pose operations in weight paint mode, - * when it comes to transforming bones, but managing bones layers/groups - * can be left for pose mode only. (not weight paint mode) + * We want to enable most pose operations in weight paint mode, when it comes to transforming + * bones, but managing bones layers/groups and their constraints can be left for pose mode only + * (not weight paint mode). */ -bool ED_operator_posemode_exclusive(bContext *C) +static bool ed_operator_posemode_exclusive_ex(bContext *C, Object *obact) { - Object *obact = CTX_data_active_object(C); - - if (obact && !(obact->mode & OB_MODE_EDIT)) { - Object *obpose = BKE_object_pose_armature_get(obact); - if (obpose != NULL) { - if (obact == obpose) { - return true; - } + if (obact != NULL && !(obact->mode & OB_MODE_EDIT)) { + if (obact == BKE_object_pose_armature_get(obact)) { + return true; } } + CTX_wm_operator_poll_msg_set(C, "No object, or not exclusively in pose mode"); return false; } +bool ED_operator_posemode_exclusive(bContext *C) +{ + Object *obact = ED_object_active_context(C); + + return ed_operator_posemode_exclusive_ex(C, obact); +} + +/** Object must be editable, fully local (i.e. not an override), and exclusively in Pose mode. */ +bool ED_operator_object_active_local_editable_posemode_exclusive(bContext *C) +{ + Object *obact = ED_object_active_context(C); + + if (!ed_operator_posemode_exclusive_ex(C, obact)) { + return false; + } + + if (ID_IS_OVERRIDE_LIBRARY(obact)) { + CTX_wm_operator_poll_msg_set(C, "Object is a local library override"); + return false; + } + + return true; +} + /* allows for pinned pose objects to be used in the object buttons * and the non-active pose object to be used in the 3D view */ bool ED_operator_posemode_context(bContext *C) @@ -1362,6 +1402,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) area->winx, area->winy, SPACE_EMPTY, + false, true, false, WIN_ALIGN_ABSOLUTE); @@ -4446,9 +4487,17 @@ static void screen_animation_region_tag_redraw(ScrArea *area, /* No need to do a full redraw as the current frame indicator is only updated. * We do need to redraw when this area is in full screen as no other areas * will be tagged for redrawing. */ - if ((region->regiontype == RGN_TYPE_WINDOW) && - (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) && !area->full) { - return; + if (region->regiontype == RGN_TYPE_WINDOW && !area->full) { + if (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) { + return; + } + + if (area->spacetype == SPACE_SEQ) { + const SpaceSeq *sseq = area->spacedata.first; + if (!ED_space_sequencer_has_playback_animation(sseq, scene)) { + return; + } + } } ED_region_tag_redraw(region); } @@ -4948,6 +4997,7 @@ static int userpref_show_exec(bContext *C, wmOperator *op) sizey, SPACE_USERPREF, false, + false, true, WIN_ALIGN_LOCATION_CENTER) != NULL) { /* The header only contains the editor switcher and looks empty. @@ -5014,6 +5064,7 @@ static int drivers_editor_show_exec(bContext *C, wmOperator *op) sizey, SPACE_GRAPH, false, + false, true, WIN_ALIGN_LOCATION_CENTER) != NULL) { ED_drivers_editor_init(C, CTX_wm_area(C)); @@ -5082,6 +5133,7 @@ static int info_log_show_exec(bContext *C, wmOperator *op) sizey, SPACE_INFO, false, + false, true, WIN_ALIGN_LOCATION_CENTER) != NULL) { return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 41db79bc134..c83e4de281a 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -122,7 +122,7 @@ BLI_INLINE uchar f_to_char(const float val) * * When 3 - a brush should have ~9 buckets under it at once * ...this helps for threading while painting as well as - * avoiding initializing pixels that wont touch the brush */ + * avoiding initializing pixels that won't touch the brush */ #define PROJ_BUCKET_BRUSH_DIV 4 #define PROJ_BUCKET_RECT_MIN 4 @@ -957,7 +957,7 @@ static int line_isect_y(const float p1[2], const float p2[2], const float y_leve return ISECT_TRUE_P2; } - /** yuck, horizontal line, we cant do much here. */ + /** yuck, horizontal line, we can't do much here. */ y_diff = fabsf(p1[1] - p2[1]); if (y_diff < 0.000001f) { @@ -991,10 +991,10 @@ static int line_isect_x(const float p1[2], const float p2[2], const float x_leve return ISECT_TRUE_P2; } - /* yuck, horizontal line, we cant do much here */ + /* yuck, horizontal line, we can't do much here */ x_diff = fabsf(p1[0] - p2[0]); - /* yuck, vertical line, we cant do much here */ + /* yuck, vertical line, we can't do much here */ if (x_diff < 0.000001f) { *y_isect = (p1[0] + p2[0]) * 0.5f; return ISECT_TRUE; @@ -3152,7 +3152,7 @@ static void project_paint_face_init(const ProjPaintState *ps, } //#if 0 else if (has_x_isect) { - /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ + /* assuming the face is not a bow-tie - we know we can't intersect again on the X */ break; } //#endif @@ -3239,7 +3239,7 @@ static void project_paint_face_init(const ProjPaintState *ps, uv_image_outset(ps, lt_uv_pxoffset, lt_puv, tri_index, ibuf->x, ibuf->y); } - /* ps->loopSeamUVs cant be modified when threading, now this is done we can unlock. */ + /* ps->loopSeamUVs can't be modified when threading, now this is done we can unlock. */ if (threaded) { /* Other threads could be modifying these vars */ BLI_thread_unlock(LOCK_CUSTOM1); @@ -3292,7 +3292,7 @@ static void project_paint_face_init(const ProjPaintState *ps, interp_v2_v2v2(seam_subsection[3], seam_uvs[0], seam_uvs[1], fac1); /* if the bucket_clip_edges values Z values was kept we could avoid this - * Inset needs to be added so occlusion tests wont hit adjacent faces */ + * Inset needs to be added so occlusion tests won't hit adjacent faces */ interp_v3_v3v3(edge_verts_inset_clip[0], insetCos[fidx1], insetCos[fidx2], fac1); interp_v3_v3v3(edge_verts_inset_clip[1], insetCos[fidx1], insetCos[fidx2], fac2); @@ -3648,7 +3648,7 @@ static void project_paint_delayed_face_init(ProjPaintState *ps, has_x_isect = has_isect = 1; } else if (has_x_isect) { - /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ + /* assuming the face is not a bow-tie - we know we can't intersect again on the X */ break; } } @@ -5514,7 +5514,7 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v if (tool == PAINT_TOOL_SMEAR) { - for (node = smearPixels; node; node = node->next) { /* this wont run for a float image */ + for (node = smearPixels; node; node = node->next) { /* this won't run for a float image */ projPixel = node->link; *projPixel->pixel.uint_pt = ((ProjPixelClone *)projPixel)->clonepx.uint; if (lock_alpha) { @@ -5534,7 +5534,7 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v } else if (tool == PAINT_TOOL_SOFTEN) { - for (node = softenPixels; node; node = node->next) { /* this wont run for a float image */ + for (node = softenPixels; node; node = node->next) { /* this won't run for a float image */ projPixel = node->link; *projPixel->pixel.uint_pt = projPixel->newColor.uint; if (lock_alpha) { @@ -5571,7 +5571,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } if (ps->thread_tot > 1) { - task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH); } image_pool = BKE_image_pool_new(); @@ -6468,6 +6468,8 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) alpha = RNA_boolean_get(op->ptr, "alpha"); RNA_string_get(op->ptr, "name", imagename); } + + /* TODO(lukas): Add option for tiled image. */ ima = BKE_image_add_generated(bmain, width, height, @@ -6478,7 +6480,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) color, false, is_data, - false); /* TODO(lukas): Add option */ + false); return ima; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index daccc6f228a..0a5f1975361 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -725,7 +725,7 @@ typedef struct WeightPaintInfo { * length of defbase_tot */ const bool *lock_flags; /* boolean array for selected bones, - * length of defbase_tot, cant be const because of how its passed */ + * length of defbase_tot, can't be const because of how it's passed */ const bool *defbase_sel; /* same as WeightPaintData.vgroup_validmap, * only added here for convenience */ @@ -917,7 +917,7 @@ static void do_weight_paint_vertex_single( * 'resist' so you couldn't instantly zero out other weights by painting 1.0 on the active. * * However this gave a problem since applying mirror, then normalize both verts - * the resulting weight wont match on both sides. + * the resulting weight won't match on both sides. * * If this 'resisting', slower normalize is nicer, we could call * do_weight_paint_normalize_all() and only use... @@ -941,13 +941,13 @@ static void do_weight_paint_vertex_single( * - Auto normalize is enabled. * - The group you are painting onto has a L / R version. * - * We want L/R vgroups to have the same weight but this cant be if both are over 0.5, + * We want L/R vgroups to have the same weight but this can't be if both are over 0.5, * We _could_ have special check for that, but this would need its own * normalize function which holds 2 groups from changing at once. * * So! just balance out the 2 weights, it keeps them equal and everything normalized. * - * While it wont hit the desired weight immediately as the user waggles their mouse, + * While it won't hit the desired weight immediately as the user waggles their mouse, * constant painting and re-normalizing will get there. this is also just simpler logic. * - campbell */ dw_mirr->weight = dw->weight = (dw_mirr->weight + dw->weight) * 0.5f; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index d6d54a1985d..ab5c46f4bc5 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1146,10 +1146,9 @@ void SCULPT_floodfill_add_active( v = SCULPT_active_vertex_get(ss); } else if (radius > 0.0f) { - float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius; float location[3]; flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), i); - v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false); + v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false); } if (v != -1) { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index b6205db6f45..71166b7c20c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -1189,9 +1189,7 @@ static SculptUndoNode *sculpt_undo_geometry_push(Object *object, SculptUndoType static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); - SculptUndoNode *unode = usculpt->nodes.first; - - unode = MEM_callocN(sizeof(*unode), __func__); + SculptUndoNode *unode = MEM_callocN(sizeof(*unode), __func__); BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); unode->type = type; diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 98e39520e99..fea01ab5330 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1872,12 +1872,13 @@ void ACTION_OT_clickselect(wmOperatorType *ot) /* properties */ WM_operator_properties_generic_select(ot); + /* Key-map: Enable with `Shift`. */ prop = RNA_def_boolean( ot->srna, "extend", 0, "Extend Select", - "Toggle keyframe selection instead of leaving newly selected keyframes only"); /* SHIFTKEY */ + "Toggle keyframe selection instead of leaving newly selected keyframes only"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, @@ -1887,20 +1888,21 @@ void ACTION_OT_clickselect(wmOperatorType *ot) "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + /* Key-map: Enable with `Alt`. */ prop = RNA_def_boolean( ot->srna, "column", 0, "Column Select", - "Select all keyframes that occur on the same frame as the one under the mouse"); /* ALTKEY */ + "Select all keyframes that occur on the same frame as the one under the mouse"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean( - ot->srna, - "channel", - 0, - "Only Channel", - "Select all the keyframes in the channel under the mouse"); /* CTRLKEY + ALTKEY */ + /* Key-map: Enable with `Ctrl-Alt`. */ + prop = RNA_def_boolean(ot->srna, + "channel", + 0, + "Only Channel", + "Select all the keyframes in the channel under the mouse"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index f6af2f79890..26b087168f9 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -153,6 +153,8 @@ static SpaceLink *action_duplicate(SpaceLink *sl) { SpaceAction *sactionn = MEM_dupallocN(sl); + memset(&sactionn->runtime, 0x0, sizeof(sactionn->runtime)); + /* clear or remove stuff from old */ return (SpaceLink *)sactionn; diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 9aa6a993c13..2da13646a8b 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -904,7 +904,7 @@ static void start_prefetch_threads(MovieClip *clip, queue.do_update = do_update; queue.progress = progress; - TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW); for (int 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 cd60ebf3031..f5e4c4d55d9 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1235,10 +1235,8 @@ static void do_movie_proxy(void *pjv, float *progress) { ProxyJob *pj = pjv; - Scene *scene = pj->scene; MovieClip *clip = pj->clip; struct MovieDistortion *distortion = NULL; - int cfra, sfra = SFRA, efra = EFRA; if (pj->index_context) { IMB_anim_index_rebuild(pj->index_context, stop, do_update, progress); @@ -1252,8 +1250,8 @@ static void do_movie_proxy(void *pjv, return; } - sfra = 1; - efra = clip->len; + const int sfra = 1; + const int efra = clip->len; if (build_undistort_count) { int threads = BLI_system_thread_count(); @@ -1265,7 +1263,7 @@ static void do_movie_proxy(void *pjv, BKE_tracking_distortion_set_threads(distortion, threads); } - for (cfra = sfra; cfra <= efra; cfra++) { + for (int cfra = sfra; cfra <= efra; cfra++) { BKE_movieclip_build_proxy_frame( clip, pj->clip_flag, distortion, cfra, build_undistort_sizes, build_undistort_count, 1); @@ -1431,7 +1429,7 @@ static void do_sequence_proxy(void *pjv, queue.do_update = do_update; queue.progress = progress; - TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW); handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles"); for (int i = 0; i < tot_thread; i++) { ProxyThread *handle = &handles[i]; diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index bcac4d94bf0..ff62bcf0cfa 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -52,7 +52,9 @@ #include "clip_intern.h" #include "tracking_ops_intern.h" -/********************** add marker operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Add Marker Operator + * \{ */ static bool add_marker(const bContext *C, float x, float y) { @@ -147,7 +149,11 @@ void CLIP_OT_add_marker(wmOperatorType *ot) 1.0f); } -/********************** add marker operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Marker Operator + * \{ */ static int add_marker_at_click_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { @@ -212,7 +218,11 @@ void CLIP_OT_add_marker_at_click(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; } -/********************** delete track operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Track Operator + * \{ */ static int delete_track_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -265,7 +275,11 @@ void CLIP_OT_delete_track(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** delete marker operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Marker Operator + * \{ */ static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -334,7 +348,11 @@ void CLIP_OT_delete_marker(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** slide marker operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Slide Marker Operator + * \{ */ enum { SLIDE_ACTION_POS = 0, @@ -1000,7 +1018,11 @@ void CLIP_OT_slide_marker(wmOperatorType *ot) FLT_MAX); } -/********************** clear track operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Track Operator + * \{ */ static int clear_track_path_exec(bContext *C, wmOperator *op) { @@ -1071,7 +1093,11 @@ void CLIP_OT_clear_track_path(wmOperatorType *ot) "Clear active track only instead of all selected tracks"); } -/********************** disable markers operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Disable Markers Operator + * \{ */ enum { MARKER_OP_DISABLE = 0, @@ -1137,7 +1163,11 @@ void CLIP_OT_disable_markers(wmOperatorType *ot) RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute"); } -/********************** set principal center operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set Principal Center Operator + * \{ */ static int set_center_principal_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1174,7 +1204,11 @@ void CLIP_OT_set_center_principal(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** hide tracks operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Hide Tracks Operator + * \{ */ static int hide_tracks_exec(bContext *C, wmOperator *op) { @@ -1241,7 +1275,11 @@ void CLIP_OT_hide_tracks(wmOperatorType *ot) RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected tracks"); } -/********************** hide tracks clear operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Hide Tracks Clear Operator + * \{ */ static int hide_tracks_clear_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1284,7 +1322,11 @@ void CLIP_OT_hide_tracks_clear(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** frame jump operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Frame Jump Operator + * \{ */ static bool frame_jump_poll(bContext *C) { @@ -1379,7 +1421,11 @@ void CLIP_OT_frame_jump(wmOperatorType *ot) RNA_def_enum(ot->srna, "position", position_items, 0, "Position", "Position to jump to"); } -/********************** join tracks operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Join Tracks Operator + * \{ */ static int join_tracks_exec(bContext *C, wmOperator *op) { @@ -1475,7 +1521,11 @@ void CLIP_OT_join_tracks(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Average tracks operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Average Tracks Operator + * \{ */ static int average_tracks_exec(bContext *C, wmOperator *op) { @@ -1566,7 +1616,11 @@ void CLIP_OT_average_tracks(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/********************** lock tracks operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lock Tracks Operator + * \{ */ enum { TRACK_ACTION_LOCK = 0, @@ -1628,7 +1682,11 @@ void CLIP_OT_lock_tracks(wmOperatorType *ot) RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Lock action to execute"); } -/********************** set keyframe operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set Keyframe Operator + * \{ */ enum { SOLVER_KEYFRAME_A = 0, @@ -1680,7 +1738,11 @@ void CLIP_OT_set_solver_keyframe(wmOperatorType *ot) RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe", "Keyframe to set"); } -/********************** track copy color operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Track Copy Color Operator + * \{ */ static int track_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1725,7 +1787,11 @@ void CLIP_OT_track_copy_color(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** clean tracks operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clean Tracks Operator + * \{ */ static bool is_track_clean(MovieTrackingTrack *track, int frames, int del) { @@ -1963,7 +2029,11 @@ void CLIP_OT_clean_tracks(wmOperatorType *ot) RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Cleanup action to execute"); } -/********************** add tracking object *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Tracking Object + * \{ */ static int tracking_object_new_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1994,7 +2064,11 @@ void CLIP_OT_tracking_object_new(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** remove tracking object *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Remove Tracking Object + * \{ */ static int tracking_object_remove_exec(bContext *C, wmOperator *op) { @@ -2033,7 +2107,11 @@ void CLIP_OT_tracking_object_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** copy tracks to clipboard operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Copy Tracks to Clipboard Operator + * \{ */ static int copy_tracks_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2064,7 +2142,11 @@ void CLIP_OT_copy_tracks(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER; } -/********************* paste tracks from clipboard operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Paste Tracks From Clipboard Operator + * \{ */ static bool paste_tracks_poll(bContext *C) { @@ -2106,7 +2188,11 @@ void CLIP_OT_paste_tracks(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Insert track keyframe operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Insert Track Keyframe Operator + * \{ */ static void keyframe_set_flag(bContext *C, bool set) { @@ -2181,7 +2267,11 @@ void CLIP_OT_keyframe_insert(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Delete track keyframe operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Track Keyframe Operator + * \{ */ static int keyframe_delete_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2203,3 +2293,5 @@ void CLIP_OT_keyframe_delete(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 189b9b4c874..5e09692b041 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -142,7 +142,8 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh color); } -static void file_draw_icon(uiBlock *block, +static void file_draw_icon(const SpaceFile *sfile, + uiBlock *block, const FileDirEntry *file, const char *path, int sx, @@ -173,14 +174,19 @@ static void file_draw_icon(uiBlock *block, if ((id = filelist_file_get_id(file))) { UI_but_drag_set_id(but, id); } - else if (file->typeflag & FILE_TYPE_ASSET) { + else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS && + (file->typeflag & FILE_TYPE_ASSET) != 0) { ImBuf *preview_image = filelist_file_getimage(file); char blend_path[FILE_MAX_LIBEXTRA]; if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { + const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + BLI_assert(asset_params != NULL); + UI_but_drag_set_asset(but, file->name, BLI_strdup(blend_path), file->blentype, + asset_params->import_type, icon, preview_image, UI_DPI_FAC); @@ -299,7 +305,8 @@ void file_calc_previews(const bContext *C, ARegion *region) UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height); } -static void file_draw_preview(uiBlock *block, +static void file_draw_preview(const SpaceFile *sfile, + uiBlock *block, const FileDirEntry *file, const char *path, int sx, @@ -484,9 +491,19 @@ static void file_draw_preview(uiBlock *block, /* path is no more static, cannot give it directly to but... */ else if (file->typeflag & FILE_TYPE_ASSET) { char blend_path[FILE_MAX_LIBEXTRA]; + if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { - UI_but_drag_set_asset( - but, file->name, BLI_strdup(blend_path), file->blentype, icon, imb, scale); + const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + BLI_assert(asset_params != NULL); + + UI_but_drag_set_asset(but, + file->name, + BLI_strdup(blend_path), + file->blentype, + asset_params->import_type, + icon, + imb, + scale); } } else { @@ -925,7 +942,8 @@ void file_draw_list(const bContext *C, ARegion *region) is_icon = 1; } - file_draw_preview(block, + file_draw_preview(sfile, + block, file, path, sx, @@ -940,7 +958,8 @@ void file_draw_list(const bContext *C, ARegion *region) is_link); } else { - file_draw_icon(block, + file_draw_icon(sfile, + block, file, path, sx, diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index f1d0197b9ae..a7c57459729 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -62,6 +62,7 @@ void FILE_OT_bookmark_cleanup(struct wmOperatorType *ot); void FILE_OT_bookmark_move(struct wmOperatorType *ot); void FILE_OT_reset_recent(wmOperatorType *ot); void FILE_OT_hidedot(struct wmOperatorType *ot); +void FILE_OT_associate_blend(struct wmOperatorType *ot); void FILE_OT_execute(struct wmOperatorType *ot); void FILE_OT_mouse_execute(struct wmOperatorType *ot); void FILE_OT_cancel(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 7c14f4659eb..0ac03c4c307 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1677,7 +1677,7 @@ void file_draw_check_ex(bContext *C, ScrArea *area) if (op->type->check(C, op)) { file_operator_to_sfile(bmain, sfile, op); - /* redraw, else the changed settings wont get updated */ + /* redraw, else the changed settings won't get updated */ ED_area_tag_redraw(area); } } @@ -2633,6 +2633,43 @@ void FILE_OT_hidedot(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Associate File Type Operator (Windows only) + * \{ */ + +static int associate_blend_exec(bContext *UNUSED(C), wmOperator *op) +{ +#ifdef WIN32 + WM_cursor_wait(true); + if (BLI_windows_register_blend_extension(true)) { + BKE_report(op->reports, RPT_INFO, "File association registered"); + WM_cursor_wait(false); + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_ERROR, "Unable to register file association"); + WM_cursor_wait(false); + return OPERATOR_CANCELLED; + } +#else + BKE_report(op->reports, RPT_WARNING, "Operator Not supported"); + return OPERATOR_CANCELLED; +#endif +} + +void FILE_OT_associate_blend(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Register File Association"; + ot->description = "Use this installation for .blend files and to display thumbnails"; + ot->idname = "FILE_OT_associate_blend"; + + /* api callbacks */ + ot->exec = associate_blend_exec; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Increment Filename Operator * \{ */ diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 8ea44e5c3ee..e43000d3f64 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1553,8 +1553,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) { - cache->previews_pool = BLI_task_pool_create_background( - cache, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW); cache->previews_done = BLI_thread_queue_init(); IMB_thumb_locks_acquire(); @@ -2895,7 +2894,7 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const } /* there we go */ - libfiledata = BLO_blendhandle_from_file(dir, NULL); + libfiledata = BLO_blendhandle_from_file(dir, &(BlendFileReadReport){.reports = NULL}); if (libfiledata == NULL) { return nbr_entries; } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 038b9c11bca..8e3fc36aa71 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -120,6 +120,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) asset_params->base_params.details_flags = U_default.file_space_data.details_flags; asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL; asset_params->asset_library.custom_library_index = -1; + asset_params->import_type = FILE_ASSET_IMPORT_APPEND; } FileSelectParams *base_params = &asset_params->base_params; diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 12bc0a68ca6..0418bb87768 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -675,6 +675,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_bookmark_move); WM_operatortype_append(FILE_OT_reset_recent); WM_operatortype_append(FILE_OT_hidedot); + WM_operatortype_append(FILE_OT_associate_blend); WM_operatortype_append(FILE_OT_filenum); WM_operatortype_append(FILE_OT_directory_new); WM_operatortype_append(FILE_OT_delete); diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index d88bf8750c2..ec5f443e2dc 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -1337,8 +1337,8 @@ static void do_graph_region_modifier_buttons(bContext *C, void *UNUSED(arg), int { switch (event) { case B_FMODIFIER_REDRAW: /* XXX this should send depsgraph updates too */ - WM_event_add_notifier( - C, NC_ANIMATION, NULL); /* XXX need a notifier specially for F-Modifiers */ + /* XXX: need a notifier specially for F-Modifiers */ + WM_event_add_notifier(C, NC_ANIMATION, NULL); break; } } diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 0e22a2fffe1..50d14d18be9 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -675,6 +675,16 @@ static short ok_bezier_always_ok(KeyframeEditData *UNUSED(ked), BezTriple *UNUSE return KEYFRAME_OK_KEY | KEYFRAME_OK_H1 | KEYFRAME_OK_H2; } +#define ABOVE 1 +#define INSIDE 0 +#define BELOW -1 +static int rectf_curve_zone_y( + FCurve *fcu, const rctf *rectf, const float offset, const float unit_scale, const float eval_x) +{ + const float fcurve_y = (evaluate_fcurve(fcu, eval_x) + offset) * unit_scale; + return fcurve_y < rectf->ymin ? BELOW : fcurve_y <= rectf->ymax ? INSIDE : ABOVE; +} + /* Checks whether the given rectangle intersects the given fcurve's calculated curve (i.e. not * only keyframes, but also all the interpolated values). This is done by sampling the curve at * different points between the xmin and the xmax of the rectangle. @@ -683,8 +693,7 @@ static bool rectf_curve_intersection( const float offset, const float unit_scale, const rctf *rectf, AnimData *adt, FCurve *fcu) { /* 30 sampling points. This worked well in tests. */ - const float num_steps = 30.0f; - const float step = (rectf->xmax - rectf->xmin) / num_steps; + int num_steps = 30; /* Remap the range at which to evaluate the fcurves. This enables us to avoid remapping * the keys themselves. */ @@ -692,27 +701,36 @@ static bool rectf_curve_intersection( const float mapped_min = BKE_nla_tweakedit_remap(adt, rectf->xmin, NLATIME_CONVERT_UNMAP); const float eval_step = (mapped_max - mapped_min) / num_steps; - float x = rectf->xmin; - float eval_x = mapped_min; /* Sample points on the given fcurve in the interval defined by the * mapped_min and mapped_max of the selected rectangle. * For each point, check if it is inside of the selection box. If it is, then select * all the keyframes of the curve, the curve, and stop the loop. */ - while (x < rectf->xmax) { - const float fcurve_y = (evaluate_fcurve(fcu, eval_x) + offset) * unit_scale; - /* Since rectf->xmin <= x < rectf->xmax is always true, there is no need to keep comparing the - * X-coordinate to the rectangle in every iteration. Therefore we do the comparisons manually - * instead of using BLI_rctf_isect_pt_v(rectf, current_point). - */ - if (rectf->ymin <= fcurve_y && fcurve_y <= rectf->ymax) { + struct { + float eval_x; + int zone; + } cur, prev; + + prev.eval_x = mapped_min; + prev.zone = rectf_curve_zone_y(fcu, rectf, offset, unit_scale, prev.eval_x); + if (prev.zone == INSIDE) { + return true; + } + + while (num_steps--) { + cur.eval_x = prev.eval_x + eval_step; + cur.zone = rectf_curve_zone_y(fcu, rectf, offset, unit_scale, cur.eval_x); + if (cur.zone != prev.zone) { return true; } - x += step; - eval_x += eval_step; + + prev = cur; } return false; } +#undef ABOVE +#undef INSIDE +#undef BELOW /* Perform a box selection of the curves themselves. This means this function tries * to select a curve by sampling it at various points instead of trying to select the @@ -1805,7 +1823,6 @@ static int graphkeys_mselect_column(bAnimContext *ac, KeyframeEditFunc select_cb, ok_cb; KeyframeEditData ked; tNearestVertInfo *nvi; - float selx = (float)ac->scene->r.cfra; /* find the beztriple that we're selecting, and the handle that was clicked on */ nvi = find_nearest_fcurve_vert(ac, mval); @@ -1817,7 +1834,7 @@ static int graphkeys_mselect_column(bAnimContext *ac, /* get frame number on which elements should be selected */ /* TODO: should we restrict to integer frames only? */ - selx = nvi->frame; + const float selx = nvi->frame; if (select_mode != SELECT_REPLACE) { /* Doesn't need to deselect anything -> Pass. */ @@ -1941,12 +1958,14 @@ void GRAPH_OT_clickselect(wmOperatorType *ot) /* properties */ WM_operator_properties_generic_select(ot); + + /* Key-map: Enable with `Shift`. */ prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Toggle keyframe selection instead of leaving newly selected " - "keyframes only"); /* SHIFTKEY */ + "keyframes only"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, @@ -1956,19 +1975,18 @@ void GRAPH_OT_clickselect(wmOperatorType *ot) "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + /* Key-map: Enable with `Alt`. */ prop = RNA_def_boolean(ot->srna, "column", 0, "Column Select", "Select all keyframes that occur on the same frame as the one under " - "the mouse"); /* ALTKEY */ + "the mouse"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, - "curves", - 0, - "Only Curves", - "Select all the keyframes in the curve"); /* CTRLKEY + ALTKEY */ + /* Key-map: Enable with `Ctrl-Atl`. */ + prop = RNA_def_boolean( + ot->srna, "curves", 0, "Only Curves", "Select all the keyframes in the curve"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 9f01773eadf..0c6d904de01 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -172,6 +172,8 @@ static SpaceLink *graph_duplicate(SpaceLink *sl) { SpaceGraph *sipon = MEM_dupallocN(sl); + memset(&sipon->runtime, 0x0, sizeof(sipon->runtime)); + /* clear or remove stuff from old */ BLI_duplicatelist(&sipon->runtime.ghost_curves, &((SpaceGraph *)sl)->runtime.ghost_curves); sipon->ads = MEM_dupallocN(sipon->ads); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 6053253790a..9e2bd1448b2 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1311,7 +1311,6 @@ static int image_open_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); ImageUser *iuser = NULL; - ImageOpenData *iod = op->customdata; Image *ima = NULL; int frame_seq_len = 0; int frame_ofs = 1; @@ -1345,7 +1344,7 @@ static int image_open_exec(bContext *C, wmOperator *op) } /* hook into UI */ - iod = op->customdata; + ImageOpenData *iod = op->customdata; if (iod->pprop.prop) { /* when creating new ID blocks, use is already 1, but RNA @@ -1608,7 +1607,7 @@ static int image_replace_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "filepath", str); - /* we cant do much if the str is longer than FILE_MAX :/ */ + /* we can't do much if the str is longer than FILE_MAX :/ */ BLI_strncpy(sima->image->filepath, str, sizeof(sima->image->filepath)); if (sima->image->source == IMA_SRC_GENERATED) { diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index feee268c6d3..09e30298e79 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -293,8 +293,9 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) SWAP(uint *, ptile->rect.uint, tmpibuf->rect); } - BKE_image_free_gputextures( - image); /* force OpenGL reload (maybe partial update will operate better?) */ + /* Force OpenGL reload (maybe partial update will operate better?) */ + BKE_image_free_gputextures(image); + if (ibuf->rect_float) { ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ } diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c index be3b60d581b..9524c2a1d8a 100644 --- a/source/blender/editors/space_info/info_draw.c +++ b/source/blender/editors/space_info/info_draw.c @@ -134,14 +134,12 @@ static void report_textview_end(TextViewContext *UNUSED(tvc)) static int report_textview_step(TextViewContext *tvc) { /* simple case, but no newline support */ - const Report *report = tvc->iter; - if (tvc->iter_char_begin <= 0) { tvc->iter = (void *)((Link *)tvc->iter)->prev; if (tvc->iter && report_textview_skip__internal(tvc)) { tvc->iter_tmp++; - report = tvc->iter; + const Report *report = tvc->iter; tvc->iter_char_end = report->len; /* reset start */ report_textview_init__internal(tvc); diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index ebdf32a1cdb..50eb96824d7 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -31,6 +31,7 @@ #include "DNA_mesh_types.h" #include "DNA_meta_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "DNA_windowmanager_types.h" #include "BLF_api.h" @@ -63,6 +64,8 @@ #include "ED_info.h" +#include "WM_api.h" + #include "UI_resources.h" #include "GPU_capabilities.h" @@ -123,12 +126,19 @@ static bool stats_mesheval(Mesh *me_eval, bool is_selected, SceneStats *stats) return true; } -static void stats_object(Object *ob, SceneStats *stats, GSet *objects_gset) +static void stats_object(Object *ob, + const View3D *v3d_local, + SceneStats *stats, + GSet *objects_gset) { if ((ob->base_flag & BASE_VISIBLE_VIEWLAYER) == 0) { return; } + if (v3d_local && !BKE_object_is_visible_in_viewport(v3d_local, ob)) { + return; + } + const bool is_selected = (ob->base_flag & BASE_SELECTED) != 0; stats->totobj++; @@ -334,7 +344,7 @@ static void stats_object_edit(Object *obedit, SceneStats *stats) } } -static void stats_object_pose(Object *ob, SceneStats *stats) +static void stats_object_pose(const Object *ob, SceneStats *stats) { if (ob->pose) { bArmature *arm = ob->data; @@ -351,16 +361,13 @@ static void stats_object_pose(Object *ob, SceneStats *stats) } } -static bool stats_is_object_dynamic_topology_sculpt(Object *ob) +static bool stats_is_object_dynamic_topology_sculpt(const Object *ob) { - if (ob == NULL) { - return false; - } - const eObjectMode object_mode = ob->mode; - return ((object_mode & OB_MODE_SCULPT) && ob->sculpt && ob->sculpt->bm); + BLI_assert(ob->mode & OB_MODE_SCULPT); + return (ob->sculpt && ob->sculpt->bm); } -static void stats_object_sculpt(Object *ob, SceneStats *stats) +static void stats_object_sculpt(const Object *ob, SceneStats *stats) { SculptSession *ss = ob->sculpt; @@ -386,81 +393,118 @@ static void stats_object_sculpt(Object *ob, SceneStats *stats) } /* Statistics displayed in info header. Called regularly on scene changes. */ -static void stats_update(Depsgraph *depsgraph, ViewLayer *view_layer) +static void stats_update(Depsgraph *depsgraph, + ViewLayer *view_layer, + View3D *v3d_local, + SceneStats *stats) { - SceneStats stats = {0}; - Object *ob = OBACT(view_layer); - Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + const Object *ob = OBACT(view_layer); + const Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + + memset(stats, 0x0, sizeof(*stats)); if (obedit) { - /* Edit Mode */ + /* Edit Mode. */ 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++; + if (ob_iter->mode & OB_MODE_EDIT) { + stats_object_edit(ob_iter, stats); + stats->totobjsel++; } - stats.totobj++; + else { + /* Skip hidden objects in local view that are not in edit-mode, + * an exception for edit-mode, in most other modes these would be considered hidden. */ + if ((v3d_local && !BKE_object_is_visible_in_viewport(v3d_local, ob_iter))) { + continue; + } + } + stats->totobj++; } } FOREACH_OBJECT_END; } else if (ob && (ob->mode & OB_MODE_POSE)) { - /* Pose Mode */ - stats_object_pose(ob, &stats); + /* Pose Mode. */ + FOREACH_OBJECT_BEGIN (view_layer, ob_iter) { + if (ob_iter->base_flag & BASE_VISIBLE_VIEWLAYER) { + if (ob_iter->mode & OB_MODE_POSE) { + stats_object_pose(ob_iter, stats); + stats->totobjsel++; + } + else { + /* See comment for edit-mode. */ + if ((v3d_local && !BKE_object_is_visible_in_viewport(v3d_local, ob_iter))) { + continue; + } + } + stats->totobj++; + } + } + FOREACH_OBJECT_END; } - else if (stats_is_object_dynamic_topology_sculpt(ob)) { - /* Dynamic topology. Do not count all vertices, dynamic topology stats are initialized later as - * part of sculpt stats. */ + else if (ob && (ob->mode & OB_MODE_SCULPT)) { + /* Sculpt Mode. */ + if (stats_is_object_dynamic_topology_sculpt(ob)) { + /* Dynamic topology. Do not count all vertices, + * dynamic topology stats are initialized later as part of sculpt stats. */ + } + else { + /* When dynamic topology is not enabled both sculpt stats and scene stats are collected. */ + stats_object_sculpt(ob, stats); + } } else { - /* Objects */ + /* Objects. */ GSet *objects_gset = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob_iter) { - stats_object(ob_iter, &stats, objects_gset); + stats_object(ob_iter, v3d_local, stats, objects_gset); } DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; BLI_gset_free(objects_gset, NULL); } - - if (ob && (ob->mode & OB_MODE_SCULPT)) { - /* Sculpt Mode. When dynamic topology is not enabled both sculpt stats and scene stats are - * collected. */ - stats_object_sculpt(ob, &stats); - } - - if (!view_layer->stats) { - view_layer->stats = MEM_callocN(sizeof(SceneStats), "SceneStats"); - } - - *(view_layer->stats) = stats; } -void ED_info_stats_clear(ViewLayer *view_layer) +void ED_info_stats_clear(wmWindowManager *wm, ViewLayer *view_layer) { if (view_layer->stats) { MEM_freeN(view_layer->stats); view_layer->stats = NULL; } + + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + ViewLayer *view_layer_test = WM_window_get_active_view_layer(win); + if (view_layer != view_layer_test) { + continue; + } + const bScreen *screen = WM_window_get_active_screen(win); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_VIEW3D) { + View3D *v3d = area->spacedata.first; + if (v3d->localvd) { + MEM_SAFE_FREE(v3d->runtime.local_stats); + } + } + } + } } -static bool format_stats(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - SceneStatsFmt *stats_fmt) +static bool format_stats( + Main *bmain, Scene *scene, ViewLayer *view_layer, View3D *v3d_local, SceneStatsFmt *stats_fmt) { /* Create stats if they don't already exist. */ - if (!view_layer->stats) { + SceneStats **stats_p = (v3d_local) ? &v3d_local->runtime.local_stats : &view_layer->stats; + if (*stats_p == NULL) { /* Do not not access dependency graph if interface is marked as locked. */ wmWindowManager *wm = bmain->wm.first; if (wm->is_interface_locked) { return false; } Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); - stats_update(depsgraph, view_layer); + *stats_p = MEM_mallocN(sizeof(SceneStats), __func__); + stats_update(depsgraph, view_layer, v3d_local, *stats_p); } - SceneStats *stats = view_layer->stats; + SceneStats *stats = *stats_p; /* Generate formatted numbers. */ #define SCENE_STATS_FMT_INT(_id) BLI_str_format_uint64_grouped(stats_fmt->_id, stats->_id) @@ -605,7 +649,7 @@ static const char *info_statusbar_string(Main *bmain, /* Scene statistics. */ if (statusbar_flag & STATUSBAR_SHOW_STATS) { SceneStatsFmt stats_fmt; - if (format_stats(bmain, scene, view_layer, &stats_fmt)) { + if (format_stats(bmain, scene, view_layer, NULL, &stats_fmt)) { get_stats_string(info + ofs, len, &ofs, view_layer, &stats_fmt); } } @@ -682,11 +726,17 @@ static void stats_row(int col1, BLF_draw_default(col2, *y, 0.0f, values, sizeof(values)); } +/** + * \param v3d_local: Pass this argument to calculate view-port local statistics. + * Note that this must only be used for local-view, otherwise report specific statistics + * will be written into the global scene statistics giving incorrect results. + */ void ED_info_draw_stats( - Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height) + Main *bmain, Scene *scene, ViewLayer *view_layer, View3D *v3d_local, int x, int *y, int height) { + BLI_assert(v3d_local == NULL || v3d_local->localvd != NULL); SceneStatsFmt stats_fmt; - if (!format_stats(bmain, scene, view_layer, &stats_fmt)) { + if (!format_stats(bmain, scene, view_layer, v3d_local, &stats_fmt)) { return; } @@ -747,8 +797,8 @@ void ED_info_draw_stats( } if (obedit) { + stats_row(col1, labels[OBJ], col2, stats_fmt.totobjsel, stats_fmt.totobj, y, height); 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); @@ -763,6 +813,7 @@ void ED_info_draw_stats( } } else if (ob && (object_mode & OB_MODE_POSE)) { + stats_row(col1, labels[OBJ], col2, stats_fmt.totobjsel, stats_fmt.totobj, y, height); stats_row(col1, labels[BONES], col2, stats_fmt.totbonesel, stats_fmt.totbone, y, height); } else if ((ob) && (ob->type == OB_GPENCIL)) { diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 7276688e986..6143af8ed70 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -591,6 +591,7 @@ static int node_add_texture_exec(bContext *C, wmOperator *op) snode_notify(C, snode); snode_dag_update(C, snode); + DEG_relations_tag_update(bmain); ED_node_tag_update_nodetree(bmain, ntree, texture_node); @@ -696,6 +697,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) snode_notify(C, snode); snode_dag_update(C, snode); + DEG_relations_tag_update(bmain); ED_node_tag_update_nodetree(bmain, ntree, collection_node); @@ -807,6 +809,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op) snode_notify(C, snode); snode_dag_update(C, snode); + DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; } @@ -902,6 +905,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) snode_notify(C, snode); snode_dag_update(C, snode); + DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index fd9c0f42f2d..1dc8e1412af 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -82,6 +82,7 @@ # include "COM_compositor.h" #endif +using blender::Map; using blender::Set; using blender::Span; using blender::Vector; @@ -1767,26 +1768,27 @@ static void node_update(const bContext *C, bNodeTree *ntree, bNode *node) static void count_mutli_input_socket_links(bNodeTree *ntree, SpaceNode *snode) { + Map<bNodeSocket *, int> counts; + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->tosock->flag & SOCK_MULTI_INPUT) { + int &count = counts.lookup_or_add(link->tosock, 0); + count++; + } + } + /* Count temporary links going into this socket. */ + LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) { + LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { + bNodeLink *link = (bNodeLink *)linkdata->data; + if (link->tosock && (link->tosock->flag & SOCK_MULTI_INPUT)) { + int &count = counts.lookup_or_add(link->tosock, 0); + count++; + } + } + } LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - LISTBASE_FOREACH (struct bNodeSocket *, socket, &node->inputs) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { if (socket->flag & SOCK_MULTI_INPUT) { - Set<bNodeSocket *> visited_from_sockets; - socket->total_inputs = 0; - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - if (link->tosock == socket) { - visited_from_sockets.add(link->fromsock); - } - } - /* Count temporary links going into this socket. */ - LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) { - LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = (bNodeLink *)linkdata->data; - if (link->tosock == socket) { - visited_from_sockets.add(link->fromsock); - } - } - } - socket->total_inputs = visited_from_sockets.size(); + socket->total_inputs = counts.lookup_default(socket, 0); } } } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index d86b069aac3..b5ef250f5a9 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -177,7 +177,7 @@ static int compo_breakjob(void *cjv) { CompoJob *cj = (CompoJob *)cjv; - /* without G.is_break 'ESC' wont quit - which annoys users */ + /* without G.is_break 'ESC' won't quit - which annoys users */ return (*(cj->stop) #ifdef USE_ESC_COMPO || G.is_break diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 2fcc59cde0b..8dfc43333e3 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -25,6 +25,7 @@ #include "BKE_node.h" #include "UI_interface.h" +#include "UI_view2d.h" #include <stddef.h> /* for size_t */ /* internal exports only */ @@ -64,6 +65,9 @@ typedef struct bNodeLinkDrag { /** Temporarily stores the last hovered socket for multi-input socket operator. * Store it to recalculate sorting after it is no longer hovered. */ struct bNode *last_node_hovered_while_dragging_a_link; + + /* Data for edge panning */ + View2DEdgePanData pan_data; } bNodeLinkDrag; typedef struct SpaceNode_Runtime { diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 57dc0b6fef2..a1b8e4e3395 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -973,6 +973,8 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) ARegion *region = CTX_wm_region(C); float cursor[2]; + UI_view2d_edge_pan_apply_event(C, &nldrag->pan_data, event); + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); switch (event->type) { @@ -1130,6 +1132,8 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) bNodeLinkDrag *nldrag = node_link_init(bmain, snode, cursor, detach); if (nldrag) { + UI_view2d_edge_pan_operator_init(C, &nldrag->pan_data, op); + op->customdata = nldrag; BLI_addtail(&snode->runtime->linkdrag, nldrag); @@ -1193,6 +1197,13 @@ void NODE_OT_link(wmOperatorType *ot) UI_PRECISION_FLOAT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); RNA_def_property_flag(prop, PROP_HIDDEN); + + UI_view2d_edge_pan_operator_properties_ex(ot, + NODE_EDGE_PAN_INSIDE_PAD, + NODE_EDGE_PAN_OUTSIDE_PAD, + NODE_EDGE_PAN_SPEED_RAMP, + NODE_EDGE_PAN_MAX_SPEED, + NODE_EDGE_PAN_DELAY); } /** \} */ diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 7d889eed612..8021b45ac77 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -1365,7 +1365,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true); PointerRNA op_ptr; WM_operator_properties_create_ptr(&op_ptr, ot); - RNA_int_set(&op_ptr, "outside_padding", OUTLINER_DRAG_SCOLL_OUTSIDE_PAD); + RNA_float_set(&op_ptr, "outside_padding", OUTLINER_DRAG_SCOLL_OUTSIDE_PAD); WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr); WM_operator_properties_free(&op_ptr); } diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index f809bb13b42..d59d04b6ac2 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -86,6 +86,8 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "../../blender/blenloader/BLO_readfile.h" + #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -930,8 +932,14 @@ static void id_override_library_resync_fn(bContext *C, te->store_elem->id->tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_resync( - bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true, reports); + BKE_lib_override_library_resync(bmain, + scene, + CTX_data_view_layer(C), + id_root, + NULL, + do_hierarchy_enforce, + true, + &(struct BlendFileReadReport){.reports = reports}); WM_event_add_notifier(C, NC_WINDOW, NULL); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 90389fc1be2..ae455d957cf 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -312,12 +312,8 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, outliner_add_element(space_outliner, &te->subtree, ob, te, TSE_ANIM_DATA, 0); } - outliner_add_element(space_outliner, - &te->subtree, - ob->poselib, - te, - TSE_SOME_ID, - 0); /* XXX FIXME.. add a special type for this. */ + /* FIXME: add a special type for this. */ + outliner_add_element(space_outliner, &te->subtree, ob->poselib, te, TSE_SOME_ID, 0); if (ob->proxy && !ID_IS_LINKED(ob)) { outliner_add_element(space_outliner, &te->subtree, ob->proxy, te, TSE_PROXY, 0); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 7f500597906..b6929f12cc0 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -367,8 +367,6 @@ static void draw_seq_waveform_overlay(View2D *v2d, static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2) { - /* Don't use SEQ_ALL_BEGIN/SEQ_ALL_END here, - * because it changes seq->depth, which is needed for transform. */ Sequence *seq; uchar col[4]; @@ -2365,6 +2363,31 @@ static void draw_cache_view(const bContext *C) } /* Draw sequencer timeline. */ +static void draw_overlap_frame_indicator(const struct Scene *scene, const View2D *v2d) +{ + int overlap_frame = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ? + scene->ed->over_cfra : + scene->r.cfra + scene->ed->over_ofs; + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + /* Shader may have color set from past usage - reset it. */ + immUniform1i("colors_len", 0); + immUniform1f("dash_width", 20.0f * U.pixelsize); + immUniform1f("dash_factor", 0.5f); + immUniformThemeColor(TH_CFRAME); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, overlap_frame, v2d->cur.ymin); + immVertex2f(pos, overlap_frame, v2d->cur.ymax); + immEnd(); + + immUnbindProgram(); +} + void draw_timeline_seq(const bContext *C, ARegion *region) { Scene *scene = CTX_data_scene(C); @@ -2424,31 +2447,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region) cfra_flag |= DRAWCFRA_UNIT_SECONDS; } - /* Draw overlap frame frame indicator. */ - if (scene->ed && scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) { - int overlap_frame = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ? - scene->ed->over_cfra : - scene->r.cfra + scene->ed->over_ofs; - - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - /* Shader may have color set from past usage - reset it. */ - immUniform1i("colors_len", 0); - immUniform1f("dash_width", 20.0f * U.pixelsize); - immUniform1f("dash_factor", 0.5f); - immUniformThemeColor(TH_CFRAME); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, overlap_frame, v2d->cur.ymin); - immVertex2f(pos, overlap_frame, v2d->cur.ymax); - immEnd(); - - immUnbindProgram(); - } - UI_view2d_view_orthoSpecial(region, v2d, 1); int marker_draw_flag = DRAW_MARKERS_MARGIN; if (sseq->flag & SEQ_SHOW_MARKERS) { @@ -2456,11 +2454,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region) } UI_view2d_view_ortho(v2d); - - if (ed) { - draw_cache_view(C); - } - ANIM_draw_previewrange(C, v2d, 1); /* Draw registered callbacks. */ @@ -2486,6 +2479,13 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region) const SpaceSeq *sseq = CTX_wm_space_seq(C); View2D *v2d = ®ion->v2d; + if (scene->ed && scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) { + UI_view2d_view_ortho(v2d); + draw_cache_view(C); + draw_overlap_frame_indicator(scene, v2d); + UI_view2d_view_restore(C); + } + ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true); UI_view2d_scrollers_draw(v2d, NULL); } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 11451091680..e17beb228de 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -34,6 +34,7 @@ #include "BLT_translation.h" +#include "DNA_anim_types.h" #include "DNA_scene_types.h" #include "DNA_sound_types.h" @@ -138,6 +139,48 @@ bool ED_space_sequencer_check_show_strip(SpaceSeq *sseq) ELEM(sseq->mainb, SEQ_DRAW_SEQUENCE, SEQ_DRAW_IMG_IMBUF)); } +static bool sequencer_fcurves_targets_color_strip(const FCurve *fcurve) +{ + if (!BLI_str_startswith(fcurve->rna_path, "sequence_editor.sequences_all[\"")) { + return false; + } + + if (!BLI_str_endswith(fcurve->rna_path, "\"].color")) { + return false; + } + + return true; +} + +/* + * Check if there is animation shown during playback. + * + * - Colors of color strips are displayed on the strip itself. + * - Backdrop is drawn. + */ +bool ED_space_sequencer_has_playback_animation(const struct SpaceSeq *sseq, + const struct Scene *scene) +{ + if (sseq->draw_flag & SEQ_DRAW_BACKDROP) { + return true; + } + + if (!scene->adt) { + return false; + } + if (!scene->adt->action) { + return false; + } + + LISTBASE_FOREACH (FCurve *, fcurve, &scene->adt->action->curves) { + if (sequencer_fcurves_targets_color_strip(fcurve)) { + return true; + } + } + + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -292,7 +335,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) /* Check meta-strips. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK) && + if (seq->flag & SELECT && !(seq->flag & SEQ_LOCK) && SEQ_transform_sequence_can_be_translated(seq)) { if ((seq->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) == 0) { SEQ_transform_translate_sequence( @@ -314,7 +357,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) /* Test for effects and overlap. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK)) { + if (seq->flag & SELECT && !(seq->flag & SEQ_LOCK)) { seq->flag &= ~SEQ_OVERLAP; if (SEQ_transform_test_overlap(ed->seqbasep, seq)) { SEQ_transform_seqbase_shuffle(ed->seqbasep, seq, scene); @@ -1940,7 +1983,7 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) seqm->machine = active_seq ? active_seq->machine : channel_max; strcpy(seqm->name + 2, "MetaStrip"); - SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seqm); + SEQ_sequence_base_unique_name_recursive(scene, &ed->seqbase, seqm); seqm->start = meta_start_frame; seqm->len = meta_end_frame - meta_start_frame; SEQ_time_update_sequence(scene, seqm); @@ -2144,7 +2187,7 @@ static Sequence *find_next_prev_sequence(Scene *scene, Sequence *test, int lr, i seq = ed->seqbasep->first; while (seq) { - if ((seq != test) && (test->machine == seq->machine) && (test->depth == seq->depth) && + if ((seq != test) && (test->machine == seq->machine) && ((sel == -1) || (sel == (seq->flag & SELECT)))) { dist = MAXFRAME * 2; @@ -2443,7 +2486,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) min_seq_startdisp = seq->startdisp; } } - /* Paste strips after playhead. */ + /* Paste strips relative to the current-frame. */ ofs = scene->r.cfra - min_seq_startdisp; } @@ -2496,7 +2539,11 @@ void SEQUENCER_OT_paste(wmOperatorType *ot) /* Properties. */ PropertyRNA *prop = RNA_def_boolean( - ot->srna, "keep_offset", false, "Keep Offset", "Keep strip offset to playhead when pasting"); + ot->srna, + "keep_offset", + false, + "Keep Offset", + "Keep strip offset relative to the current frame when pasting"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -2782,7 +2829,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) RNA_END; } - /* Reset these else we wont see all the images. */ + /* Reset these else we won't see all the images. */ seq->anim_startofs = seq->anim_endofs = 0; /* Correct start/end frames so we don't move. diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index b2a0905d4a2..7e3f3db9bc8 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../blentranslation ../../bmesh ../../depsgraph ../../functions @@ -40,6 +41,8 @@ set(SRC spreadsheet_draw.cc spreadsheet_layout.cc spreadsheet_ops.cc + spreadsheet_row_filter.cc + spreadsheet_row_filter_ui.cc spreadsheet_context.hh spreadsheet_cell_value.hh @@ -50,6 +53,8 @@ set(SRC spreadsheet_draw.hh spreadsheet_intern.hh spreadsheet_layout.hh + spreadsheet_row_filter.hh + spreadsheet_row_filter_ui.hh ) set(LIB diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 1f0b5d5d13e..8c42f28b5f4 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -49,6 +49,8 @@ #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" #include "spreadsheet_layout.hh" +#include "spreadsheet_row_filter.hh" +#include "spreadsheet_row_filter_ui.hh" using namespace blender; using namespace blender::ed::spreadsheet; @@ -59,6 +61,8 @@ static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *U "spreadsheet space"); spreadsheet_space->spacetype = SPACE_SPREADSHEET; + spreadsheet_space->filter_flag = SPREADSHEET_FILTER_ENABLE; + { /* Header. */ ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet header"); @@ -76,6 +80,15 @@ static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *U } { + /* Properties region. */ + ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet right region"); + BLI_addtail(&spreadsheet_space->regionbase, region); + region->regiontype = RGN_TYPE_UI; + region->alignment = RGN_ALIGN_RIGHT; + region->flag = RGN_FLAG_HIDDEN; + } + + { /* Main window. */ ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet main region"); BLI_addtail(&spreadsheet_space->regionbase, region); @@ -88,8 +101,12 @@ static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *U static void spreadsheet_free(SpaceLink *sl) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; + MEM_SAFE_FREE(sspreadsheet->runtime); + LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) { + spreadsheet_row_filter_free(row_filter); + } LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) { spreadsheet_column_free(column); } @@ -113,6 +130,11 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime); + BLI_listbase_clear(&sspreadsheet_new->row_filters); + LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) { + SpreadsheetRowFilter *new_filter = spreadsheet_row_filter_copy(src_filter); + BLI_addtail(&sspreadsheet_new->row_filters, new_filter); + } BLI_listbase_clear(&sspreadsheet_new->columns); LISTBASE_FOREACH (SpreadsheetColumn *, src_column, &sspreadsheet_old->columns) { SpreadsheetColumn *new_column = spreadsheet_column_copy(src_column); @@ -128,8 +150,10 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) return (SpaceLink *)sspreadsheet_new; } -static void spreadsheet_keymap(wmKeyConfig *UNUSED(keyconf)) +static void spreadsheet_keymap(wmKeyConfig *keyconf) { + /* Entire editor only. */ + WM_keymap_ensure(keyconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0); } static void spreadsheet_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) @@ -160,8 +184,15 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region) UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy); - wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0); - WM_event_add_keymap_handler(®ion->handlers, keymap); + { + wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + } + { + wmKeyMap *keymap = WM_keymap_ensure( + wm->defaultconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + } } ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet) @@ -346,24 +377,14 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) const ColumnValues *values = scope.add(std::move(values_ptr), __func__); const int width = get_column_width_in_pixels(*values); spreadsheet_layout.columns.append({values, width}); + + spreadsheet_column_assign_runtime_data(column, values->type(), values->name()); } const int tot_rows = data_source->tot_rows(); spreadsheet_layout.index_column_width = get_index_column_width(tot_rows); - spreadsheet_layout.row_indices = IndexRange(tot_rows).as_span(); - - if (const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>( - data_source.get())) { - Object *object_eval = geometry_data_source->object_eval(); - Object *object_orig = DEG_get_original_object(object_eval); - if (object_orig->type == OB_MESH) { - if (object_orig->mode == OB_MODE_EDIT) { - if (sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY) { - spreadsheet_layout.row_indices = geometry_data_source->get_selected_element_indices(); - } - } - } - } + spreadsheet_layout.row_indices = spreadsheet_filter_rows( + *sspreadsheet, spreadsheet_layout, *data_source, scope); sspreadsheet->runtime->tot_columns = spreadsheet_layout.columns.size(); sspreadsheet->runtime->tot_rows = tot_rows; @@ -372,9 +393,11 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_layout(spreadsheet_layout); draw_spreadsheet_in_region(C, region, *drawer); - /* Tag footer for redraw, because the main region updates data for the footer. */ + /* Tag other regions for redraw, because the main region updates data for them. */ ARegion *footer = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_FOOTER); ED_region_tag_redraw(footer); + ARegion *sidebar = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_UI); + ED_region_tag_redraw(sidebar); } static void spreadsheet_main_region_listener(const wmRegionListenerParams *params) @@ -511,6 +534,24 @@ static void spreadsheet_footer_region_listener(const wmRegionListenerParams *UNU { } +static void spreadsheet_sidebar_init(wmWindowManager *wm, ARegion *region) +{ + UI_panel_category_active_set_default(region, "Filters"); + ED_region_panels_init(wm, region); + + wmKeyMap *keymap = WM_keymap_ensure( + wm->defaultconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); +} + +static void spreadsheet_right_region_free(ARegion *UNUSED(region)) +{ +} + +static void spreadsheet_right_region_listener(const wmRegionListenerParams *UNUSED(params)) +{ +} + void ED_spacetype_spreadsheet(void) { SpaceType *st = (SpaceType *)MEM_callocN(sizeof(SpaceType), "spacetype spreadsheet"); @@ -563,5 +604,20 @@ void ED_spacetype_spreadsheet(void) art->listener = spreadsheet_footer_region_listener; BLI_addhead(&st->regiontypes, art); + /* regions: right panel buttons */ + art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spacetype spreadsheet right region"); + art->regionid = RGN_TYPE_UI; + art->prefsizex = UI_SIDEBAR_PANEL_WIDTH; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES; + + art->init = spreadsheet_sidebar_init; + art->layout = ED_region_panels_layout; + art->draw = ED_region_panels_draw; + art->free = spreadsheet_right_region_free; + art->listener = spreadsheet_right_region_listener; + BLI_addhead(&st->regiontypes, art); + + register_row_filter_panels(*art); + BKE_spacetype_register(st); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc index de40545fdae..ee08c86b29f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc @@ -56,16 +56,29 @@ SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id) return column; } +void spreadsheet_column_assign_runtime_data(SpreadsheetColumn *column, + const eSpreadsheetColumnValueType data_type, + const StringRefNull display_name) +{ + column->data_type = data_type; + MEM_SAFE_FREE(column->display_name); + column->display_name = BLI_strdup(display_name.c_str()); +} + SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column) { SpreadsheetColumnID *new_column_id = spreadsheet_column_id_copy(src_column->id); SpreadsheetColumn *new_column = spreadsheet_column_new(new_column_id); + if (src_column->display_name != nullptr) { + new_column->display_name = BLI_strdup(src_column->display_name); + } return new_column; } void spreadsheet_column_free(SpreadsheetColumn *column) { spreadsheet_column_id_free(column->id); + MEM_SAFE_FREE(column->display_name); MEM_freeN(column); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh index bb245851d55..1a03278acad 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh @@ -43,6 +43,9 @@ void spreadsheet_column_id_free(SpreadsheetColumnID *column_id); SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id); SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column); +void spreadsheet_column_assign_runtime_data(SpreadsheetColumn *column, + const eSpreadsheetColumnValueType data_type, + const StringRefNull display_name); void spreadsheet_column_free(SpreadsheetColumn *column); } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index 373c988a41c..68370cf6a44 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -16,6 +16,8 @@ #pragma once +#include "DNA_space_types.h" + #include "BLI_string_ref.hh" #include "spreadsheet_cell_value.hh" @@ -28,11 +30,13 @@ namespace blender::ed::spreadsheet { */ class ColumnValues { protected: + eSpreadsheetColumnValueType type_; std::string name_; int size_; public: - ColumnValues(std::string name, const int size) : name_(std::move(name)), size_(size) + ColumnValues(const eSpreadsheetColumnValueType type, std::string name, const int size) + : type_(type), name_(std::move(name)), size_(size) { } @@ -40,6 +44,11 @@ class ColumnValues { virtual void get_value(int index, CellValue &r_cell_value) const = 0; + eSpreadsheetColumnValueType type() const + { + return type_; + } + StringRefNull name() const { return name_; @@ -60,8 +69,11 @@ template<typename GetValueF> class LambdaColumnValues : public ColumnValues { GetValueF get_value_; public: - LambdaColumnValues(std::string name, int size, GetValueF get_value) - : ColumnValues(std::move(name), size), get_value_(std::move(get_value)) + LambdaColumnValues(const eSpreadsheetColumnValueType type, + std::string name, + int size, + GetValueF get_value) + : ColumnValues(type, std::move(name), size), get_value_(std::move(get_value)) { } @@ -73,13 +85,14 @@ template<typename GetValueF> class LambdaColumnValues : public ColumnValues { /* Utility function that simplifies creating a spreadsheet column from a lambda function. */ template<typename GetValueF> -std::unique_ptr<ColumnValues> column_values_from_function(std::string name, +std::unique_ptr<ColumnValues> column_values_from_function(const eSpreadsheetColumnValueType type, + std::string name, const int size, GetValueF get_value, const float default_width = 0.0f) { std::unique_ptr<ColumnValues> column_values = std::make_unique<LambdaColumnValues<GetValueF>>( - std::move(name), size, std::move(get_value)); + type, std::move(name), size, std::move(get_value)); column_values->default_width = default_width; return column_values; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh index de47109a144..2ea7fb5809f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh @@ -54,6 +54,15 @@ class DataSource { } /** + * Returns true if the data source has the ability to limit visible rows + * by user interface selection status. + */ + virtual bool has_selection_filter() const + { + return false; + } + + /** * Returns the number of rows in columns returned by #get_column_values. */ virtual int tot_rows() const diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 452885959f6..0c76c8b7a15 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -69,28 +69,35 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type()); switch (type) { case CD_PROP_FLOAT: - return column_values_from_function( - column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { - float value; - varray->get(index, &value); - r_cell_value.value_float = value; - }); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + float value; + varray->get(index, &value); + r_cell_value.value_float = value; + }); case CD_PROP_INT32: - return column_values_from_function( - column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { - int value; - varray->get(index, &value); - r_cell_value.value_int = value; - }); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_INT32, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + int value; + varray->get(index, &value); + r_cell_value.value_int = value; + }); case CD_PROP_BOOL: - return column_values_from_function( - column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { - bool value; - varray->get(index, &value); - r_cell_value.value_bool = value; - }); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_BOOL, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + bool value; + varray->get(index, &value); + r_cell_value.value_bool = value; + }); case CD_PROP_FLOAT2: { return column_values_from_function( + SPREADSHEET_VALUE_TYPE_FLOAT2, column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { @@ -102,6 +109,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( } case CD_PROP_FLOAT3: { return column_values_from_function( + SPREADSHEET_VALUE_TYPE_FLOAT3, column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { @@ -113,6 +121,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( } case CD_PROP_COLOR: { return column_values_from_function( + SPREADSHEET_VALUE_TYPE_COLOR, column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { @@ -137,55 +146,63 @@ using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>; static void get_selected_vertex_indices(const Mesh &mesh, const IsVertexSelectedFn is_vertex_selected_fn, - Vector<int64_t> &r_vertex_indices) + MutableSpan<bool> selection) { for (const int i : IndexRange(mesh.totvert)) { - if (is_vertex_selected_fn(i)) { - r_vertex_indices.append(i); + if (!selection[i]) { + continue; + } + if (!is_vertex_selected_fn(i)) { + selection[i] = false; } } } static void get_selected_corner_indices(const Mesh &mesh, const IsVertexSelectedFn is_vertex_selected_fn, - Vector<int64_t> &r_corner_indices) + MutableSpan<bool> selection) { for (const int i : IndexRange(mesh.totloop)) { const MLoop &loop = mesh.mloop[i]; - if (is_vertex_selected_fn(loop.v)) { - r_corner_indices.append(i); + if (!selection[i]) { + continue; + } + if (!is_vertex_selected_fn(loop.v)) { + selection[i] = false; } } } static void get_selected_face_indices(const Mesh &mesh, const IsVertexSelectedFn is_vertex_selected_fn, - Vector<int64_t> &r_face_indices) + MutableSpan<bool> selection) { for (const int poly_index : IndexRange(mesh.totpoly)) { + if (!selection[poly_index]) { + continue; + } const MPoly &poly = mesh.mpoly[poly_index]; - bool is_selected = true; for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { const MLoop &loop = mesh.mloop[loop_index]; if (!is_vertex_selected_fn(loop.v)) { - is_selected = false; + selection[poly_index] = false; break; } } - if (is_selected) { - r_face_indices.append(poly_index); - } } } static void get_selected_edge_indices(const Mesh &mesh, const IsVertexSelectedFn is_vertex_selected_fn, - Vector<int64_t> &r_edge_indices) + MutableSpan<bool> selection) { for (const int i : IndexRange(mesh.totedge)) { + if (!selection[i]) { + continue; + } const MEdge &edge = mesh.medge[i]; - if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) { - r_edge_indices.append(i); + if (!is_vertex_selected_fn(edge.v1) || !is_vertex_selected_fn(edge.v2)) { + selection[i] = false; } } } @@ -193,30 +210,48 @@ static void get_selected_edge_indices(const Mesh &mesh, static void get_selected_indices_on_domain(const Mesh &mesh, const AttributeDomain domain, const IsVertexSelectedFn is_vertex_selected_fn, - Vector<int64_t> &r_indices) + MutableSpan<bool> selection) { switch (domain) { case ATTR_DOMAIN_POINT: - return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices); + return get_selected_vertex_indices(mesh, is_vertex_selected_fn, selection); case ATTR_DOMAIN_FACE: - return get_selected_face_indices(mesh, is_vertex_selected_fn, r_indices); + return get_selected_face_indices(mesh, is_vertex_selected_fn, selection); case ATTR_DOMAIN_CORNER: - return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices); + return get_selected_corner_indices(mesh, is_vertex_selected_fn, selection); case ATTR_DOMAIN_EDGE: - return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices); + return get_selected_edge_indices(mesh, is_vertex_selected_fn, selection); default: return; } } -Span<int64_t> GeometryDataSource::get_selected_element_indices() const +/** + * Only data sets corresponding to mesh objects in edit mode currently support selection filtering. + */ +bool GeometryDataSource::has_selection_filter() const +{ + Object *object_orig = DEG_get_original_object(object_eval_); + if (object_orig->type != OB_MESH) { + return false; + } + if (object_orig->mode != OB_MODE_EDIT) { + return false; + } + if (component_->type() != GEO_COMPONENT_TYPE_MESH) { + return false; + } + + return true; +} + +void GeometryDataSource::apply_selection_filter(MutableSpan<bool> rows_included) const { std::lock_guard lock{mutex_}; BLI_assert(object_eval_->mode == OB_MODE_EDIT); BLI_assert(component_->type() == GEO_COMPONENT_TYPE_MESH); Object *object_orig = DEG_get_original_object(object_eval_); - Vector<int64_t> &indices = scope_.construct<Vector<int64_t>>(__func__); const MeshComponent *mesh_component = static_cast<const MeshComponent *>(component_); const Mesh *mesh_eval = mesh_component->get_for_read(); Mesh *mesh_orig = (Mesh *)object_orig->data; @@ -237,7 +272,7 @@ Span<int64_t> GeometryDataSource::get_selected_element_indices() const BMVert *vert = bm->vtable[i_orig]; return BM_elem_flag_test(vert, BM_ELEM_SELECT); }; - get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, indices); + get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, rows_included); } else if (mesh_eval->totvert == bm->totvert) { /* Use a simple heuristic to match original vertices to evaluated ones. */ @@ -245,10 +280,8 @@ Span<int64_t> GeometryDataSource::get_selected_element_indices() const BMVert *vert = bm->vtable[vertex_index]; return BM_elem_flag_test(vert, BM_ELEM_SELECT); }; - get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, indices); + get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, rows_included); } - - return indices; } void InstancesDataSource::foreach_default_column_ids( @@ -279,7 +312,10 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( Span<int> reference_handles = component_->instance_reference_handles(); Span<InstanceReference> references = component_->references(); std::unique_ptr<ColumnValues> values = column_values_from_function( - "Name", size, [reference_handles, references](int index, CellValue &r_cell_value) { + SPREADSHEET_VALUE_TYPE_INSTANCES, + "Name", + size, + [reference_handles, references](int index, CellValue &r_cell_value) { const InstanceReference &reference = references[reference_handles[index]]; switch (reference.type()) { case InstanceReference::Type::Object: { @@ -303,6 +339,7 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( Span<float4x4> transforms = component_->instance_transforms(); if (STREQ(column_id.name, "Position")) { return column_values_from_function( + SPREADSHEET_VALUE_TYPE_FLOAT3, column_id.name, size, [transforms](int index, CellValue &r_cell_value) { @@ -312,6 +349,7 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( } if (STREQ(column_id.name, "Rotation")) { return column_values_from_function( + SPREADSHEET_VALUE_TYPE_FLOAT3, column_id.name, size, [transforms](int index, CellValue &r_cell_value) { @@ -321,6 +359,7 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( } if (STREQ(column_id.name, "Scale")) { return column_values_from_function( + SPREADSHEET_VALUE_TYPE_FLOAT3, column_id.name, size, [transforms](int index, CellValue &r_cell_value) { @@ -332,6 +371,7 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( if (STREQ(column_id.name, "ID")) { /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ return column_values_from_function( + SPREADSHEET_VALUE_TYPE_INT32, column_id.name, size, [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index 273d39f27bf..d1b5dc6845e 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -58,7 +58,8 @@ class GeometryDataSource : public DataSource { return object_eval_; } - Span<int64_t> get_selected_element_indices() const; + bool has_selection_filter() const override; + void apply_selection_filter(MutableSpan<bool> rows_included) const; void foreach_default_column_ids( FunctionRef<void(const SpreadsheetColumnID &)> fn) const override; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc index 770bd207e8d..fcbc37346e6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc @@ -14,8 +14,83 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_listbase.h" + +#include "MEM_guardedalloc.h" + +#include "BKE_context.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + #include "spreadsheet_intern.hh" +#include "spreadsheet_row_filter.hh" + +using namespace blender::ed::spreadsheet; + +static int row_filter_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + + SpreadsheetRowFilter *row_filter = spreadsheet_row_filter_new(); + BLI_addtail(&sspreadsheet->row_filters, row_filter); + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SPREADSHEET, sspreadsheet); + + return OPERATOR_FINISHED; +} + +static void SPREADSHEET_OT_add_row_filter_rule(wmOperatorType *ot) +{ + ot->name = "Add Row Filter"; + ot->description = "Add a filter to remove rows from the displayed data"; + ot->idname = "SPREADSHEET_OT_add_row_filter_rule"; + + ot->exec = row_filter_add_exec; + ot->poll = ED_operator_spreadsheet_active; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int row_filter_remove_exec(bContext *C, wmOperator *op) +{ + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + + SpreadsheetRowFilter *row_filter = (SpreadsheetRowFilter *)BLI_findlink( + &sspreadsheet->row_filters, RNA_int_get(op->ptr, "index")); + if (row_filter == nullptr) { + return OPERATOR_CANCELLED; + } + + BLI_remlink(&sspreadsheet->row_filters, row_filter); + spreadsheet_row_filter_free(row_filter); + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SPREADSHEET, sspreadsheet); + + return OPERATOR_FINISHED; +} + +static void SPREADSHEET_OT_remove_row_filter_rule(wmOperatorType *ot) +{ + ot->name = "Remove Row Filter"; + ot->description = "Remove a row filter from the rules"; + ot->idname = "SPREADSHEET_OT_remove_row_filter_rule"; + + ot->exec = row_filter_remove_exec; + ot->poll = ED_operator_spreadsheet_active; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX); +} void spreadsheet_operatortypes() { + WM_operatortype_append(SPREADSHEET_OT_add_row_filter_rule); + WM_operatortype_append(SPREADSHEET_OT_remove_row_filter_rule); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc new file mode 100644 index 00000000000..ae336edfead --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -0,0 +1,366 @@ +/* + * 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 <cstring> + +#include "BLI_listbase.h" + +#include "DNA_collection_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "DEG_depsgraph_query.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" + +#include "spreadsheet_intern.hh" + +#include "spreadsheet_data_source_geometry.hh" +#include "spreadsheet_intern.hh" +#include "spreadsheet_layout.hh" +#include "spreadsheet_row_filter.hh" + +namespace blender::ed::spreadsheet { + +template<typename OperationFn> +static void apply_filter_operation(const ColumnValues &values, + OperationFn check_fn, + MutableSpan<bool> rows_included) +{ + for (const int i : rows_included.index_range()) { + if (!rows_included[i]) { + continue; + } + CellValue cell_value; + values.get_value(i, cell_value); + if (!check_fn(cell_value)) { + rows_included[i] = false; + } + } +} + +static void apply_row_filter(const SpreadsheetLayout &spreadsheet_layout, + const SpreadsheetRowFilter &row_filter, + MutableSpan<bool> rows_included) +{ + for (const ColumnLayout &column : spreadsheet_layout.columns) { + const ColumnValues &values = *column.values; + if (values.name() != row_filter.column_name) { + continue; + } + + switch (values.type()) { + case SPREADSHEET_VALUE_TYPE_INT32: { + const int value = row_filter.value_int; + switch (row_filter.operation) { + case SPREADSHEET_ROW_FILTER_EQUAL: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return *cell_value.value_int == value; + }, + rows_included); + break; + } + case SPREADSHEET_ROW_FILTER_GREATER: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return *cell_value.value_int > value; + }, + rows_included); + break; + } + case SPREADSHEET_ROW_FILTER_LESS: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return *cell_value.value_int < value; + }, + rows_included); + break; + } + } + break; + } + case SPREADSHEET_VALUE_TYPE_FLOAT: { + const float value = row_filter.value_float; + switch (row_filter.operation) { + case SPREADSHEET_ROW_FILTER_EQUAL: { + const float threshold = row_filter.threshold; + apply_filter_operation( + values, + [value, threshold](const CellValue &cell_value) -> bool { + return std::abs(*cell_value.value_float - value) < threshold; + }, + rows_included); + break; + } + case SPREADSHEET_ROW_FILTER_GREATER: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return *cell_value.value_float > value; + }, + rows_included); + break; + } + case SPREADSHEET_ROW_FILTER_LESS: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return *cell_value.value_float < value; + }, + rows_included); + break; + } + } + break; + } + case SPREADSHEET_VALUE_TYPE_FLOAT2: { + const float2 value = row_filter.value_float2; + switch (row_filter.operation) { + case SPREADSHEET_ROW_FILTER_EQUAL: { + const float threshold_squared = row_filter.threshold * row_filter.threshold; + apply_filter_operation( + values, + [value, threshold_squared](const CellValue &cell_value) -> bool { + return float2::distance_squared(*cell_value.value_float2, value) < + threshold_squared; + }, + rows_included); + break; + } + case SPREADSHEET_ROW_FILTER_GREATER: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return cell_value.value_float2->x > value.x && + cell_value.value_float2->y > value.y; + }, + rows_included); + break; + } + case SPREADSHEET_ROW_FILTER_LESS: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return cell_value.value_float2->x < value.x && + cell_value.value_float2->y < value.y; + }, + rows_included); + break; + } + } + break; + } + case SPREADSHEET_VALUE_TYPE_FLOAT3: { + const float3 value = row_filter.value_float3; + switch (row_filter.operation) { + case SPREADSHEET_ROW_FILTER_EQUAL: { + const float threshold_squared = row_filter.threshold * row_filter.threshold; + apply_filter_operation( + values, + [value, threshold_squared](const CellValue &cell_value) -> bool { + return float3::distance_squared(*cell_value.value_float3, value) < + threshold_squared; + }, + rows_included); + break; + } + case SPREADSHEET_ROW_FILTER_GREATER: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return cell_value.value_float3->x > value.x && + cell_value.value_float3->y > value.y && + cell_value.value_float3->z > value.z; + }, + rows_included); + break; + } + case SPREADSHEET_ROW_FILTER_LESS: { + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return cell_value.value_float3->x < value.x && + cell_value.value_float3->y < value.y && + cell_value.value_float3->z < value.z; + }, + rows_included); + break; + } + } + break; + } + case SPREADSHEET_VALUE_TYPE_COLOR: { + const ColorGeometry4f value = row_filter.value_color; + switch (row_filter.operation) { + case SPREADSHEET_ROW_FILTER_EQUAL: { + const float threshold_squared = row_filter.threshold * row_filter.threshold; + apply_filter_operation( + values, + [value, threshold_squared](const CellValue &cell_value) -> bool { + return len_squared_v4v4(value, *cell_value.value_color) < threshold_squared; + }, + rows_included); + break; + } + } + break; + } + case SPREADSHEET_VALUE_TYPE_BOOL: { + const bool value = (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) != 0; + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + return *cell_value.value_bool == value; + }, + rows_included); + break; + } + case SPREADSHEET_VALUE_TYPE_INSTANCES: { + const StringRef value = row_filter.value_string; + apply_filter_operation( + values, + [value](const CellValue &cell_value) -> bool { + const ID *id = nullptr; + if (cell_value.value_object) { + id = &cell_value.value_object->object->id; + } + else if (cell_value.value_collection) { + id = &cell_value.value_collection->collection->id; + } + if (id == nullptr) { + return false; + } + + return value == id->name + 2; + }, + rows_included); + break; + } + default: + break; + } + + /* Only one column should have this name. */ + break; + } +} + +static void index_vector_from_bools(Span<bool> selection, Vector<int64_t> &indices) +{ + for (const int i : selection.index_range()) { + if (selection[i]) { + indices.append(i); + } + } +} + +static bool use_row_filters(const SpaceSpreadsheet &sspreadsheet) +{ + if (!(sspreadsheet.filter_flag & SPREADSHEET_FILTER_ENABLE)) { + return false; + } + if (BLI_listbase_is_empty(&sspreadsheet.row_filters)) { + return false; + } + return true; +} + +static bool use_selection_filter(const SpaceSpreadsheet &sspreadsheet, + const DataSource &data_source) +{ + if (!(sspreadsheet.filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY)) { + return false; + } + if (!data_source.has_selection_filter()) { + return false; + } + return true; +} + +Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet, + const SpreadsheetLayout &spreadsheet_layout, + const DataSource &data_source, + ResourceScope &scope) +{ + const int tot_rows = data_source.tot_rows(); + + const bool use_selection = use_selection_filter(sspreadsheet, data_source); + const bool use_filters = use_row_filters(sspreadsheet); + + /* Avoid allocating an array if no row filtering is necessary. */ + if (!(use_filters || use_selection)) { + return IndexRange(tot_rows).as_span(); + } + + Array<bool> rows_included(tot_rows, true); + + if (use_filters) { + LISTBASE_FOREACH (const SpreadsheetRowFilter *, row_filter, &sspreadsheet.row_filters) { + if (row_filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) { + apply_row_filter(spreadsheet_layout, *row_filter, rows_included); + } + } + } + + if (use_selection) { + const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>( + &data_source); + geometry_data_source->apply_selection_filter(rows_included); + } + + Vector<int64_t> &indices = scope.construct<Vector<int64_t>>(__func__); + index_vector_from_bools(rows_included, indices); + + return indices; +} + +SpreadsheetRowFilter *spreadsheet_row_filter_new() +{ + SpreadsheetRowFilter *row_filter = (SpreadsheetRowFilter *)MEM_callocN( + sizeof(SpreadsheetRowFilter), __func__); + row_filter->flag = (SPREADSHEET_ROW_FILTER_UI_EXPAND | SPREADSHEET_ROW_FILTER_ENABLED); + row_filter->operation = SPREADSHEET_ROW_FILTER_LESS; + row_filter->threshold = 0.01f; + row_filter->column_name[0] = '\0'; + + return row_filter; +} + +SpreadsheetRowFilter *spreadsheet_row_filter_copy(const SpreadsheetRowFilter *src_row_filter) +{ + SpreadsheetRowFilter *new_filter = spreadsheet_row_filter_new(); + + memcpy(new_filter, src_row_filter, sizeof(SpreadsheetRowFilter)); + new_filter->next = nullptr; + new_filter->prev = nullptr; + + return new_filter; +} + +void spreadsheet_row_filter_free(SpreadsheetRowFilter *row_filter) +{ + MEM_SAFE_FREE(row_filter->value_string); + MEM_freeN(row_filter); +} + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.hh b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.hh new file mode 100644 index 00000000000..0a5783e318d --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.hh @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "BLI_resource_scope.hh" + +#include "spreadsheet_data_source.hh" +#include "spreadsheet_layout.hh" + +namespace blender::ed::spreadsheet { + +Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet, + const SpreadsheetLayout &spreadsheet_layout, + const DataSource &data_source, + ResourceScope &scope); + +SpreadsheetRowFilter *spreadsheet_row_filter_new(); +SpreadsheetRowFilter *spreadsheet_row_filter_copy(const SpreadsheetRowFilter *src_row_filter); +void spreadsheet_row_filter_free(SpreadsheetRowFilter *row_filter); + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc new file mode 100644 index 00000000000..dbd2ef157af --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -0,0 +1,347 @@ +/* + * 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 <cstring> + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_ref.hh" + +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BKE_screen.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLT_translation.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "spreadsheet_column.hh" +#include "spreadsheet_intern.hh" +#include "spreadsheet_row_filter.hh" +#include "spreadsheet_row_filter_ui.hh" + +using namespace blender; +using namespace blender::ed::spreadsheet; + +static void filter_panel_id_fn(void *UNUSED(row_filter_v), char *r_name) +{ + /* All row filters use the same panel ID. */ + BLI_snprintf(r_name, BKE_ST_MAXNAME, "SPREADSHEET_PT_filter"); +} + +static std::string operation_string(const eSpreadsheetColumnValueType data_type, + const eSpreadsheetFilterOperation operation) +{ + if (ELEM(data_type, + SPREADSHEET_VALUE_TYPE_BOOL, + SPREADSHEET_VALUE_TYPE_INSTANCES, + SPREADSHEET_VALUE_TYPE_COLOR)) { + return "="; + } + + switch (operation) { + case SPREADSHEET_ROW_FILTER_EQUAL: + return "="; + case SPREADSHEET_ROW_FILTER_GREATER: + return ">"; + case SPREADSHEET_ROW_FILTER_LESS: + return "<"; + } + BLI_assert_unreachable(); + return ""; +} + +static std::string value_string(const SpreadsheetRowFilter &row_filter, + const eSpreadsheetColumnValueType data_type) +{ + switch (data_type) { + case SPREADSHEET_VALUE_TYPE_INT32: + return std::to_string(row_filter.value_int); + case SPREADSHEET_VALUE_TYPE_FLOAT: { + std::ostringstream result; + result.precision(3); + result << std::fixed << row_filter.value_float; + return result.str(); + } + case SPREADSHEET_VALUE_TYPE_FLOAT2: { + std::ostringstream result; + result.precision(3); + result << std::fixed << "(" << row_filter.value_float2[0] << ", " + << row_filter.value_float2[1] << ")"; + return result.str(); + } + case SPREADSHEET_VALUE_TYPE_FLOAT3: { + std::ostringstream result; + result.precision(3); + result << std::fixed << "(" << row_filter.value_float3[0] << ", " + << row_filter.value_float3[1] << ", " << row_filter.value_float3[2] << ")"; + return result.str(); + } + case SPREADSHEET_VALUE_TYPE_BOOL: + return (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) ? IFACE_("True") : + IFACE_("False"); + case SPREADSHEET_VALUE_TYPE_INSTANCES: + if (row_filter.value_string != nullptr) { + return row_filter.value_string; + } + return ""; + case SPREADSHEET_VALUE_TYPE_COLOR: + std::ostringstream result; + result.precision(3); + result << std::fixed << "(" << row_filter.value_color[0] << ", " << row_filter.value_color[1] + << ", " << row_filter.value_color[2] << ", " << row_filter.value_color[3] << ")"; + return result.str(); + } + BLI_assert_unreachable(); + return ""; +} + +static SpreadsheetColumn *lookup_visible_column_for_filter(const SpaceSpreadsheet &sspreadsheet, + const StringRef column_name) +{ + LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet.columns) { + if (column->display_name == column_name) { + return column; + } + } + return nullptr; +} + +static void spreadsheet_filter_panel_draw_header(const bContext *C, Panel *panel) +{ + uiLayout *layout = panel->layout; + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + PointerRNA *filter_ptr = UI_panel_custom_data_get(panel); + const SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data; + const StringRef column_name = filter->column_name; + const eSpreadsheetFilterOperation operation = (eSpreadsheetFilterOperation)filter->operation; + + const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name); + if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) || + (column == nullptr && !column_name.is_empty())) { + uiLayoutSetActive(layout, false); + } + + uiLayout *row = uiLayoutRow(layout, true); + uiLayoutSetEmboss(row, UI_EMBOSS_NONE); + uiItemR(row, filter_ptr, "enabled", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); + + if (column_name.is_empty()) { + uiItemL(row, IFACE_("Filter"), ICON_NONE); + } + else if (column == nullptr) { + uiItemL(row, column_name.data(), ICON_NONE); + } + else { + const eSpreadsheetColumnValueType data_type = (eSpreadsheetColumnValueType)column->data_type; + std::stringstream ss; + ss << column_name; + ss << " "; + ss << operation_string(data_type, operation); + ss << " "; + ss << value_string(*filter, data_type); + uiItemL(row, ss.str().c_str(), ICON_NONE); + } + + row = uiLayoutRow(layout, true); + uiLayoutSetEmboss(row, UI_EMBOSS_NONE); + const int current_index = BLI_findindex(&sspreadsheet->row_filters, filter); + uiItemIntO(row, "", ICON_X, "SPREADSHEET_OT_remove_row_filter_rule", "index", current_index); + + /* Some padding so the X isn't too close to the drag icon. */ + uiItemS_ex(layout, 0.25f); +} + +static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *layout = panel->layout; + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + PointerRNA *filter_ptr = UI_panel_custom_data_get(panel); + SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data; + const StringRef column_name = filter->column_name; + const eSpreadsheetFilterOperation operation = (eSpreadsheetFilterOperation)filter->operation; + + const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name); + if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) || + !(filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) || + (column == nullptr && !column_name.is_empty())) { + uiLayoutSetActive(layout, false); + } + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + uiItemR(layout, filter_ptr, "column_name", 0, IFACE_("Column"), ICON_NONE); + + /* Don't draw settings for filters with no corresponding visible column. */ + if (column == nullptr || column_name.is_empty()) { + return; + } + + switch (static_cast<eSpreadsheetColumnValueType>(column->data_type)) { + case SPREADSHEET_VALUE_TYPE_INT32: + uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE); + uiItemR(layout, filter_ptr, "value_int", 0, IFACE_("Value"), ICON_NONE); + break; + case SPREADSHEET_VALUE_TYPE_FLOAT: + uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE); + uiItemR(layout, filter_ptr, "value_float", 0, IFACE_("Value"), ICON_NONE); + if (operation == SPREADSHEET_ROW_FILTER_EQUAL) { + uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE); + } + break; + case SPREADSHEET_VALUE_TYPE_FLOAT2: + uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE); + uiItemR(layout, filter_ptr, "value_float2", 0, IFACE_("Value"), ICON_NONE); + if (operation == SPREADSHEET_ROW_FILTER_EQUAL) { + uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE); + } + break; + case SPREADSHEET_VALUE_TYPE_FLOAT3: + uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE); + uiItemR(layout, filter_ptr, "value_float3", 0, IFACE_("Value"), ICON_NONE); + if (operation == SPREADSHEET_ROW_FILTER_EQUAL) { + uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE); + } + break; + case SPREADSHEET_VALUE_TYPE_BOOL: + uiItemR(layout, filter_ptr, "value_boolean", 0, IFACE_("Value"), ICON_NONE); + break; + case SPREADSHEET_VALUE_TYPE_INSTANCES: + uiItemR(layout, filter_ptr, "value_string", 0, IFACE_("Value"), ICON_NONE); + break; + case SPREADSHEET_VALUE_TYPE_COLOR: + uiItemR(layout, filter_ptr, "value_color", 0, IFACE_("Value"), ICON_NONE); + uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE); + break; + } +} + +static void spreadsheet_row_filters_layout(const bContext *C, Panel *panel) +{ + uiLayout *layout = panel->layout; + ARegion *region = CTX_wm_region(C); + bScreen *screen = CTX_wm_screen(C); + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + ListBase *row_filters = &sspreadsheet->row_filters; + + if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE)) { + uiLayoutSetActive(layout, false); + } + + uiItemO(layout, nullptr, ICON_ADD, "SPREADSHEET_OT_add_row_filter_rule"); + + const bool panels_match = UI_panel_list_matches_data(region, row_filters, filter_panel_id_fn); + + if (!panels_match) { + UI_panels_free_instanced(C, region); + LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) { + char panel_idname[MAX_NAME]; + filter_panel_id_fn(row_filter, panel_idname); + + PointerRNA *filter_ptr = (PointerRNA *)MEM_mallocN(sizeof(PointerRNA), "panel customdata"); + RNA_pointer_create(&screen->id, &RNA_SpreadsheetRowFilter, row_filter, filter_ptr); + + UI_panel_add_instanced(C, region, ®ion->panels, panel_idname, filter_ptr); + } + } + else { + /* Assuming there's only one group of instanced panels, update the custom data pointers. */ + Panel *panel = (Panel *)region->panels.first; + LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) { + + /* Move to the next instanced panel corresponding to the next filter. */ + while ((panel->type == nullptr) || !(panel->type->flag & PANEL_TYPE_INSTANCED)) { + panel = panel->next; + BLI_assert(panel != nullptr); /* There shouldn't be fewer panels than filters. */ + } + + PointerRNA *filter_ptr = (PointerRNA *)MEM_mallocN(sizeof(PointerRNA), "panel customdata"); + RNA_pointer_create(&screen->id, &RNA_SpreadsheetRowFilter, row_filter, filter_ptr); + UI_panel_custom_data_set(panel, filter_ptr); + + panel = panel->next; + } + } +} + +static void filter_reorder(bContext *C, Panel *panel, int new_index) +{ + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + ListBase *row_filters = &sspreadsheet->row_filters; + PointerRNA *filter_ptr = UI_panel_custom_data_get(panel); + SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data; + + int current_index = BLI_findindex(row_filters, filter); + BLI_assert(current_index >= 0); + BLI_assert(new_index >= 0); + + BLI_listbase_link_move(row_filters, filter, new_index - current_index); +} + +static short get_filter_expand_flag(const bContext *UNUSED(C), Panel *panel) +{ + PointerRNA *filter_ptr = UI_panel_custom_data_get(panel); + SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data; + + return (short)filter->flag & SPREADSHEET_ROW_FILTER_UI_EXPAND; +} + +static void set_filter_expand_flag(const bContext *UNUSED(C), Panel *panel, short expand_flag) +{ + PointerRNA *filter_ptr = UI_panel_custom_data_get(panel); + SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data; + + SET_FLAG_FROM_TEST(filter->flag, + expand_flag & SPREADSHEET_ROW_FILTER_UI_EXPAND, + SPREADSHEET_ROW_FILTER_UI_EXPAND); +} + +void register_row_filter_panels(ARegionType ®ion_type) +{ + { + PanelType *panel_type = (PanelType *)MEM_callocN(sizeof(PanelType), __func__); + strcpy(panel_type->idname, "SPREADSHEET_PT_row_filters"); + strcpy(panel_type->label, N_("Filters")); + strcpy(panel_type->category, "Filters"); + strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + panel_type->flag = PANEL_TYPE_NO_HEADER; + panel_type->draw = spreadsheet_row_filters_layout; + BLI_addtail(®ion_type.paneltypes, panel_type); + } + + { + PanelType *panel_type = (PanelType *)MEM_callocN(sizeof(PanelType), __func__); + strcpy(panel_type->idname, "SPREADSHEET_PT_filter"); + strcpy(panel_type->label, ""); + strcpy(panel_type->category, "Filters"); + strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_HEADER_EXPAND; + panel_type->draw_header = spreadsheet_filter_panel_draw_header; + panel_type->draw = spreadsheet_filter_panel_draw; + panel_type->get_list_data_expand_flag = get_filter_expand_flag; + panel_type->set_list_data_expand_flag = set_filter_expand_flag; + panel_type->reorder = filter_reorder; + BLI_addtail(®ion_type.paneltypes, panel_type); + } +} diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh new file mode 100644 index 00000000000..e22178b63ea --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh @@ -0,0 +1,21 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +struct ARegionType; + +void register_row_filter_panels(ARegionType ®ion_type); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index b5274c2357e..c9f345b3123 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -187,7 +187,7 @@ bool ED_view3d_area_user_region(const ScrArea *area, const View3D *v3d, ARegion * view3d_project_short_clip and view3d_project_short_noclip in cases where * these functions are not used during draw_object */ -void ED_view3d_init_mats_rv3d(struct Object *ob, struct RegionView3D *rv3d) +void ED_view3d_init_mats_rv3d(const struct Object *ob, struct RegionView3D *rv3d) { /* local viewmat and persmat, to calculate projections */ mul_m4_m4m4(rv3d->viewmatob, rv3d->viewmat, ob->obmat); @@ -197,7 +197,7 @@ void ED_view3d_init_mats_rv3d(struct Object *ob, struct RegionView3D *rv3d) ED_view3d_clipping_local(rv3d, ob->obmat); } -void ED_view3d_init_mats_rv3d_gl(struct Object *ob, struct RegionView3D *rv3d) +void ED_view3d_init_mats_rv3d_gl(const struct Object *ob, struct RegionView3D *rv3d) { ED_view3d_init_mats_rv3d(ob, rv3d); @@ -331,6 +331,8 @@ static void view3d_free(SpaceLink *sl) MEM_freeN(vd->localvd); } + MEM_SAFE_FREE(vd->runtime.local_stats); + if (vd->runtime.properties_storage) { MEM_freeN(vd->runtime.properties_storage); } @@ -346,19 +348,25 @@ static void view3d_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(area)) { } +static void view3d_exit(wmWindowManager *UNUSED(wm), ScrArea *area) +{ + BLI_assert(area->spacetype == SPACE_VIEW3D); + View3D *v3d = area->spacedata.first; + MEM_SAFE_FREE(v3d->runtime.local_stats); +} + static SpaceLink *view3d_duplicate(SpaceLink *sl) { View3D *v3do = (View3D *)sl; View3D *v3dn = MEM_dupallocN(sl); + memset(&v3dn->runtime, 0x0, sizeof(v3dn->runtime)); + /* clear or remove stuff from old */ if (v3dn->localvd) { v3dn->localvd = NULL; - v3dn->runtime.properties_storage = NULL; } - /* Only one View3D is allowed to have this flag! */ - v3dn->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; v3dn->local_collections_uuid = 0; v3dn->flag &= ~(V3D_LOCAL_COLLECTIONS | V3D_XR_SESSION_MIRROR); @@ -373,8 +381,6 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) /* copy or clear inside new stuff */ - v3dn->runtime.properties_storage = NULL; - return (SpaceLink *)v3dn; } @@ -625,6 +631,7 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB); RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_boolean_set(drop->ptr, "duplicate", false); } static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) @@ -779,12 +786,6 @@ static void view3d_main_region_free(ARegion *region) RE_engine_free(rv3d->render_engine); } - if (rv3d->depths) { - if (rv3d->depths->depths) { - MEM_freeN(rv3d->depths->depths); - } - MEM_freeN(rv3d->depths); - } if (rv3d->sms) { MEM_freeN(rv3d->sms); } @@ -808,7 +809,6 @@ static void *view3d_main_region_duplicate(void *poin) new->clipbb = MEM_dupallocN(rv3d->clipbb); } - new->depths = NULL; new->render_engine = NULL; new->sms = NULL; new->smooth_timer = NULL; @@ -1583,7 +1583,7 @@ static void space_view3d_listener(const wmSpaceTypeListenerParams *params) } } -static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(area)) +static void space_view3d_refresh(const bContext *C, ScrArea *area) { Scene *scene = CTX_data_scene(C); LightCache *lcache = scene->eevee.light_cache_data; @@ -1592,6 +1592,9 @@ static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(area)) lcache->flag &= ~LIGHTCACHE_UPDATE_AUTO; view3d_lightcache_update((bContext *)C); } + + View3D *v3d = (View3D *)area->spacedata.first; + MEM_SAFE_FREE(v3d->runtime.local_stats); } const char *view3d_context_dir[] = { @@ -1699,6 +1702,7 @@ void ED_spacetype_view3d(void) st->create = view3d_create; st->free = view3d_free; st->init = view3d_init; + st->exit = view3d_exit; st->listener = space_view3d_listener; st->refresh = space_view3d_refresh; st->duplicate = view3d_duplicate; diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index e42f6b5faac..60e1c780b9e 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -927,7 +927,9 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float } if (apply_vcos) { - EDBM_mesh_normals_update(em); + /* TODO: use the #BKE_editmesh_looptri_and_normals_calc_with_partial + * This requires begin/end states for UI interaction (which currently aren't supported). */ + BKE_editmesh_looptri_and_normals_calc(em); } /* Edges */ diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c index 0edd6aeb2ca..0b232d525fa 100644 --- a/source/blender/editors/space_view3d/view3d_camera_control.c +++ b/source/blender/editors/space_view3d/view3d_camera_control.c @@ -140,7 +140,7 @@ struct View3DCameraControl *ED_view3d_cameracontrol_acquire(Depsgraph *depsgraph vctrl->persp_backup = rv3d->persp; vctrl->dist_backup = rv3d->dist; - /* check for flying ortho camera - which we cant support well + /* check for flying ortho camera - which we can't support well * we _could_ also check for an ortho camera but this is easier */ if ((rv3d->persp == RV3D_CAMOB) && (rv3d->is_persp == false)) { ((Camera *)v3d->camera->data)->type = CAM_PERSP; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 4a595c716b6..2e46deea0e8 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1537,7 +1537,9 @@ void view3d_draw_region_info(const bContext *C, ARegion *region) } if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0 && (v3d->overlay.flag & V3D_OVERLAY_STATS)) { - ED_info_draw_stats(bmain, scene, view_layer, xoffset, &yoffset, VIEW3D_OVERLAY_LINEHEIGHT); + View3D *v3d_local = v3d->localvd ? v3d : NULL; + ED_info_draw_stats( + bmain, scene, view_layer, v3d_local, xoffset, &yoffset, VIEW3D_OVERLAY_LINEHEIGHT); } BLF_batch_draw_end(); @@ -2219,7 +2221,7 @@ int ED_view3d_backbuf_sample_size_clamp(ARegion *region, const float dist) /** \name Z-Depth Utilities * \{ */ -void view3d_update_depths_rect(ARegion *region, ViewDepths *d, rcti *rect) +void view3d_depths_rect_create(ARegion *region, rcti *rect, ViewDepths *r_d) { /* clamp rect by region */ rcti r = { @@ -2240,70 +2242,44 @@ void view3d_update_depths_rect(ARegion *region, ViewDepths *d, rcti *rect) int h = BLI_rcti_size_y(rect); if (w <= 0 || h <= 0) { - if (d->depths) { - MEM_freeN(d->depths); - } - d->depths = NULL; - - d->damaged = false; + r_d->depths = NULL; + return; } - else if (d->w != w || d->h != h || d->x != x || d->y != y || d->depths == NULL) { - d->x = x; - d->y = y; - d->w = w; - d->h = h; - if (d->depths) { - MEM_freeN(d->depths); - } + r_d->x = x; + r_d->y = y; + r_d->w = w; + r_d->h = h; - d->depths = MEM_mallocN(sizeof(float) * d->w * d->h, "View depths Subset"); + r_d->depths = MEM_mallocN(sizeof(float) * w * h, "View depths Subset"); - d->damaged = true; - } - - if (d->damaged) { + { GPUViewport *viewport = WM_draw_region_get_viewport(region); - view3d_opengl_read_Z_pixels(viewport, rect, d->depths); + view3d_opengl_read_Z_pixels(viewport, rect, r_d->depths); /* Range is assumed to be this as they are never changed. */ - d->depth_range[0] = 0.0; - d->depth_range[1] = 1.0; - d->damaged = false; + r_d->depth_range[0] = 0.0; + r_d->depth_range[1] = 1.0; } } /* Note, with nouveau drivers the glReadPixels() is very slow. T24339. */ -static void view3d_depth_cache_update(ARegion *region) +static ViewDepths *view3d_depths_create(ARegion *region) { - RegionView3D *rv3d = region->regiondata; - - /* Create storage for, and, if necessary, copy depth buffer. */ - if (!rv3d->depths) { - rv3d->depths = MEM_callocN(sizeof(ViewDepths), "ViewDepths"); - } - if (rv3d->depths) { - ViewDepths *d = rv3d->depths; - if (d->w != region->winx || d->h != region->winy || !d->depths) { - d->w = region->winx; - d->h = region->winy; - if (d->depths) { - MEM_freeN(d->depths); - } - d->depths = MEM_mallocN(sizeof(float) * d->w * d->h, "View depths"); - d->damaged = true; - } + ViewDepths *d = MEM_callocN(sizeof(ViewDepths), "ViewDepths"); + d->w = region->winx; + d->h = region->winy; + d->depths = MEM_mallocN(sizeof(float) * d->w * d->h, "View depths"); - if (d->damaged) { - GPUViewport *viewport = WM_draw_region_get_viewport(region); - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); - GPU_framebuffer_read_depth(fbl->depth_only_fb, 0, 0, d->w, d->h, GPU_DATA_FLOAT, d->depths); + { + GPUViewport *viewport = WM_draw_region_get_viewport(region); + DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); + GPU_framebuffer_read_depth(fbl->depth_only_fb, 0, 0, d->w, d->h, GPU_DATA_FLOAT, d->depths); - /* Assumed to be this as they are never changed. */ - d->depth_range[0] = 0.0; - d->depth_range[1] = 1.0; - d->damaged = false; - } + /* Assumed to be this as they are never changed. */ + d->depth_range[0] = 0.0; + d->depth_range[1] = 1.0; } + return d; } /* Utility function to find the closest Z value, use for auto-depth. */ @@ -2343,10 +2319,13 @@ void ED_view3d_depth_override(Depsgraph *depsgraph, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, - bool update_cache) + ViewDepths **r_depths) { if (v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN) { - return; + /* Force redraw if `r_depths` is required. */ + if (!r_depths || *r_depths != NULL) { + return; + } } struct bThemeState theme_state; Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -2388,12 +2367,11 @@ void ED_view3d_depth_override(Depsgraph *depsgraph, break; } - if (rv3d->depths != NULL) { - rv3d->depths->damaged = true; - /* TODO: Clear cache? */ - } - if (update_cache) { - view3d_depth_cache_update(region); + if (r_depths) { + if (*r_depths) { + ED_view3d_depths_free(*r_depths); + } + *r_depths = view3d_depths_create(region); } } @@ -2407,6 +2385,14 @@ void ED_view3d_depth_override(Depsgraph *depsgraph, UI_Theme_Restore(&theme_state); } +void ED_view3d_depths_free(ViewDepths *depths) +{ + if (depths->depths) { + MEM_freeN(depths->depths); + } + MEM_freeN(depths); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 8b6d0e9ee04..53fc7eed43b 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -509,7 +509,7 @@ static void viewops_data_create(bContext *C, negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */ /* Set the dist value to be the distance from this 3d point this means you'll - * always be able to zoom into it and panning wont go bad when dist was zero. */ + * always be able to zoom into it and panning won't go bad when dist was zero. */ /* remove dist value */ upvec[0] = upvec[1] = 0; @@ -895,7 +895,7 @@ static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2]) copy_qt_qt(rv3d->viewquat, vod->curr.viewquat); /* check for view snap, - * note: don't apply snap to vod->viewquat so the view wont jam up */ + * note: don't apply snap to vod->viewquat so the view won't jam up */ if (vod->axis_snap) { viewrotate_apply_snap(vod); } @@ -953,7 +953,6 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (event_code == VIEW_CONFIRM) { - ED_view3d_depth_tag_update(vod->rv3d); use_autokey = true; ret = OPERATOR_FINISHED; } @@ -1014,7 +1013,6 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) } viewrotate_apply(vod, event_xy); - ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); @@ -1799,7 +1797,6 @@ static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (event_code == VIEW_CONFIRM) { - ED_view3d_depth_tag_update(vod->rv3d); use_autokey = true; ret = OPERATOR_FINISHED; } @@ -1840,7 +1837,6 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEPAN) { /* invert it, trackpad scroll follows same principle as 2d windows this way */ viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy); - ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); @@ -2254,7 +2250,6 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (event_code == VIEW_CONFIRM) { - ED_view3d_depth_tag_update(vod->rv3d); use_autokey = true; ret = OPERATOR_FINISHED; } @@ -2341,8 +2336,6 @@ static int viewzoom_exec(bContext *C, wmOperator *op) view3d_boxview_sync(area, region); } - ED_view3d_depth_tag_update(rv3d); - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true); @@ -2398,8 +2391,6 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); - ED_view3d_depth_tag_update(vod->rv3d); - viewops_data_free(C, op); return OPERATOR_FINISHED; } @@ -2579,7 +2570,6 @@ static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (event_code == VIEW_CONFIRM) { - ED_view3d_depth_tag_update(vod->rv3d); use_autokey = true; ret = OPERATOR_FINISHED; } @@ -2636,8 +2626,6 @@ static int viewdolly_exec(bContext *C, wmOperator *op) view3d_boxview_sync(area, region); } - ED_view3d_depth_tag_update(rv3d); - ED_view3d_camera_lock_sync(CTX_data_ensure_evaluated_depsgraph(C), v3d, rv3d); ED_region_tag_redraw(region); @@ -2718,7 +2706,6 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->prevx; } viewdolly_apply(vod, &event->prevx, (U.uiflag & USER_ZOOM_INVERT) == 0); - ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); return OPERATOR_FINISHED; @@ -3629,13 +3616,13 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) ED_view3d_dist_range_get(v3d, dist_range); ED_view3d_depth_override( - CTX_data_ensure_evaluated_depsgraph(C), region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); + CTX_data_ensure_evaluated_depsgraph(C), region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); { /* avoid allocating the whole depth buffer */ ViewDepths depth_temp = {0}; /* avoid view3d_update_depths() for speed. */ - view3d_update_depths_rect(region, &depth_temp, &rect); + view3d_depths_rect_create(region, &rect, &depth_temp); /* find the closest Z pixel */ depth_close = view3d_depth_near(&depth_temp); @@ -3660,7 +3647,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) if (rv3d->is_persp) { float p_corner[3]; - /* no depths to use, we cant do anything! */ + /* no depths to use, we can't do anything! */ if (depth_close == FLT_MAX) { BKE_report(op->reports, RPT_ERROR, "Depth too large"); return OPERATOR_CANCELLED; @@ -4433,7 +4420,6 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (event_code == VIEW_CONFIRM) { - ED_view3d_depth_tag_update(vod->rv3d); use_autokey = true; ret = OPERATOR_FINISHED; } @@ -4541,7 +4527,6 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEROTATE) { vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; viewroll_apply(vod, event->prevx, event->prevy); - ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); return OPERATOR_FINISHED; @@ -4638,7 +4623,6 @@ static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewmove_apply(vod, vod->prev.event_xy[0] + x, vod->prev.event_xy[1] + y); - ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index 2d499cf85c7..d42746c168c 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -772,7 +772,7 @@ static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm) float moffset[2]; /* mouse offset from the views center */ float tmp_quat[4]; /* used for rotating the view */ - /* x and y margin are define the safe area where the mouses movement wont rotate the view */ + /* x and y margin defining the safe area where the mouse's movement won't rotate the view */ int xmargin, ymargin; #ifdef NDOF_FLY_DEBUG diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c index 4d8102af6ff..16c83b45924 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c @@ -86,12 +86,12 @@ static void gizmo_bbone_offset_get(const wmGizmo *UNUSED(gz), if (bh->index == 0) { bh->co[1] = pchan->bone->ease1 / BBONE_SCALE_Y; bh->co[0] = pchan->curve_in_x; - bh->co[2] = pchan->curve_in_y; + bh->co[2] = pchan->curve_in_z; } else { bh->co[1] = -pchan->bone->ease2 / BBONE_SCALE_Y; bh->co[0] = pchan->curve_out_x; - bh->co[2] = pchan->curve_out_y; + bh->co[2] = pchan->curve_out_z; } copy_v3_v3(value, bh->co); } @@ -111,12 +111,12 @@ static void gizmo_bbone_offset_set(const wmGizmo *UNUSED(gz), if (bh->index == 0) { pchan->bone->ease1 = max_ff(0.0f, bh->co[1] * BBONE_SCALE_Y); pchan->curve_in_x = bh->co[0]; - pchan->curve_in_y = bh->co[2]; + pchan->curve_in_z = bh->co[2]; } else { pchan->bone->ease2 = max_ff(0.0f, -bh->co[1] * BBONE_SCALE_Y); pchan->curve_out_x = bh->co[0]; - pchan->curve_out_y = bh->co[2]; + pchan->curve_out_z = bh->co[2]; } } diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 6f07cb8b44d..0964c2dcbcc 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -137,7 +137,7 @@ void ED_view3d_draw_depth_loop(struct Depsgraph *depsgraph, struct ARegion *region, View3D *v3d); -void view3d_update_depths_rect(struct ARegion *region, struct ViewDepths *d, struct rcti *rect); +void view3d_depths_rect_create(struct ARegion *region, struct rcti *rect, struct ViewDepths *r_d); float view3d_depth_near(struct ViewDepths *d); /* view3d_select.c */ diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c index bdb2c3874db..2a381bb96cb 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.c +++ b/source/blender/editors/space_view3d/view3d_iterators.c @@ -50,15 +50,179 @@ #include "ED_screen.h" #include "ED_view3d.h" +/* -------------------------------------------------------------------- */ +/** \name Internal Clipping Utilities + * \{ */ + +/** + * Calculate clipping planes to use when #V3D_PROJ_TEST_CLIP_CONTENT is enabled. + * + * Planes are selected from the viewpoint using `clip_flag` + * to detect which planes should be applied (maximum 6). + * + * \return The number of planes written into `planes`. + */ +static int content_planes_from_clip_flag(const ARegion *region, + const Object *ob, + const eV3DProjTest clip_flag, + float planes[6][4]) +{ + BLI_assert(clip_flag & V3D_PROJ_TEST_CLIP_CONTENT); + + float *clip_xmin = NULL, *clip_xmax = NULL; + float *clip_ymin = NULL, *clip_ymax = NULL; + float *clip_zmin = NULL, *clip_zmax = NULL; + + int planes_len = 0; + + /* The order of `planes` has been selected based on the likelihood of points being fully + * outside the plane to increase the chance of an early exit in #clip_segment_v3_plane_n. + * With "near" being most likely and "far" being unlikely. + * + * Otherwise the order of axes in `planes` isn't significant. */ + + if (clip_flag & V3D_PROJ_TEST_CLIP_NEAR) { + clip_zmin = planes[planes_len++]; + } + if (clip_flag & V3D_PROJ_TEST_CLIP_WIN) { + clip_xmin = planes[planes_len++]; + clip_xmax = planes[planes_len++]; + clip_ymin = planes[planes_len++]; + clip_ymax = planes[planes_len++]; + } + if (clip_flag & V3D_PROJ_TEST_CLIP_FAR) { + clip_zmax = planes[planes_len++]; + } + + BLI_assert(planes_len <= 6); + if (planes_len != 0) { + RegionView3D *rv3d = region->regiondata; + float projmat[4][4]; + ED_view3d_ob_project_mat_get(rv3d, ob, projmat); + planes_from_projmat(projmat, clip_xmin, clip_xmax, clip_ymin, clip_ymax, clip_zmin, clip_zmax); + } + return planes_len; +} + +/** + * Edge projection is more involved since part of the edge may be behind the view + * or extend beyond the far limits. In the case of single points, these can be ignored. + * However it just may still be visible on screen, so constrained the edge to planes + * defined by the port to ensure both ends of the edge can be projected, see T32214. + * + * \note This is unrelated to #V3D_PROJ_TEST_CLIP_BB which must be checked separately. + */ +static bool view3d_project_segment_to_screen_with_content_clip_planes( + const ARegion *region, + const float v_a[3], + const float v_b[3], + const eV3DProjTest clip_flag, + const rctf *win_rect, + const float content_planes[][4], + const int content_planes_len, + /* Output. */ + float r_screen_co_a[2], + float r_screen_co_b[2]) +{ + /* Clipping already handled, no need to check in projection. */ + eV3DProjTest clip_flag_nowin = clip_flag & ~V3D_PROJ_TEST_CLIP_WIN; + + const eV3DProjStatus status_a = ED_view3d_project_float_object( + region, v_a, r_screen_co_a, clip_flag_nowin); + const eV3DProjStatus status_b = ED_view3d_project_float_object( + region, v_b, r_screen_co_b, clip_flag_nowin); + + if ((status_a == V3D_PROJ_RET_OK) && (status_b == V3D_PROJ_RET_OK)) { + if (clip_flag & V3D_PROJ_TEST_CLIP_WIN) { + if (!BLI_rctf_isect_segment(win_rect, r_screen_co_a, r_screen_co_b)) { + return false; + } + } + } + else { + if (content_planes_len == 0) { + return false; + } + + /* Both too near, ignore. */ + if ((status_a & V3D_PROJ_TEST_CLIP_NEAR) && (status_b & V3D_PROJ_TEST_CLIP_NEAR)) { + return false; + } + + /* Both too far, ignore. */ + if ((status_a & V3D_PROJ_TEST_CLIP_FAR) && (status_b & V3D_PROJ_TEST_CLIP_FAR)) { + return false; + } + + /* Simple cases have been ruled out, clip by viewport planes, then re-project. */ + float v_a_clip[3], v_b_clip[3]; + if (!clip_segment_v3_plane_n( + v_a, v_b, content_planes, content_planes_len, v_a_clip, v_b_clip)) { + return false; + } + + if ((ED_view3d_project_float_object(region, v_a_clip, r_screen_co_a, clip_flag_nowin) != + V3D_PROJ_RET_OK) || + (ED_view3d_project_float_object(region, v_b_clip, r_screen_co_b, clip_flag_nowin) != + V3D_PROJ_RET_OK)) { + return false; + } + + /* No need for #V3D_PROJ_TEST_CLIP_WIN check here, + * clipping the segment by planes handle this. */ + } + + return true; +} + +/** + * Project an edge, points that fail to project are tagged with #IS_CLIPPED. + */ +static bool view3d_project_segment_to_screen_with_clip_tag(const ARegion *region, + const float v_a[3], + const float v_b[3], + const eV3DProjTest clip_flag, + /* Output. */ + float r_screen_co_a[2], + float r_screen_co_b[2]) +{ + int count = 0; + + if (ED_view3d_project_float_object(region, v_a, r_screen_co_a, clip_flag) == V3D_PROJ_RET_OK) { + count++; + } + else { + r_screen_co_a[0] = IS_CLIPPED; /* weak */ + /* screen_co_a[1]: intentionally don't set this so we get errors on misuse */ + } + + if (ED_view3d_project_float_object(region, v_b, r_screen_co_b, clip_flag) == V3D_PROJ_RET_OK) { + count++; + } + else { + r_screen_co_b[0] = IS_CLIPPED; /* weak */ + /* screen_co_b[1]: intentionally don't set this so we get errors on misuse */ + } + + /* Caller may want to know this value, for now it's not needed. */ + return count != 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Private User Data Structures + * \{ */ + typedef struct foreachScreenObjectVert_userData { - void (*func)(void *userData, MVert *mv, const float screen_co_b[2], int index); + void (*func)(void *userData, MVert *mv, const float screen_co[2], int index); void *userData; ViewContext vc; eV3DProjTest clip_flag; } foreachScreenObjectVert_userData; typedef struct foreachScreenVert_userData { - void (*func)(void *userData, BMVert *eve, const float screen_co_b[2], int index); + void (*func)(void *userData, BMVert *eve, const float screen_co[2], int index); void *userData; ViewContext vc; eV3DProjTest clip_flag; @@ -73,8 +237,16 @@ typedef struct foreachScreenEdge_userData { int index); void *userData; ViewContext vc; - rctf win_rect; /* copy of: vc.region->winx/winy, use for faster tests, minx/y will always be 0 */ eV3DProjTest clip_flag; + + rctf win_rect; /* copy of: vc.region->winx/winy, use for faster tests, minx/y will always be 0 */ + + /** + * Clip plans defined by the the view bounds, + * use when #V3D_PROJ_TEST_CLIP_CONTENT is enabled. + */ + float content_planes[6][4]; + int content_planes_len; } foreachScreenEdge_userData; typedef struct foreachScreenFace_userData { @@ -91,7 +263,11 @@ typedef struct foreachScreenFace_userData { * use the object matrix in the usual way. */ -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit-Mesh: For Each Screen Vertex + * \{ */ static void meshobject_foreachScreenVert__mapFunc(void *userData, int index, @@ -120,6 +296,7 @@ void meshobject_foreachScreenVert( void *userData, eV3DProjTest clip_flag) { + BLI_assert((clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) == 0); foreachScreenObjectVert_userData data; Mesh *me; @@ -150,17 +327,17 @@ static void mesh_foreachScreenVert__mapFunc(void *userData, { foreachScreenVert_userData *data = userData; BMVert *eve = BM_vert_at_index(data->vc.em->bm, index); + if (UNLIKELY(BM_elem_flag_test(eve, BM_ELEM_HIDDEN))) { + return; + } - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - float screen_co[2]; - - if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) != - V3D_PROJ_RET_OK) { - return; - } - - data->func(data->userData, eve, screen_co, index); + float screen_co[2]; + if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) != + V3D_PROJ_RET_OK) { + return; } + + data->func(data->userData, eve, screen_co, index); } void mesh_foreachScreenVert( @@ -189,38 +366,37 @@ void mesh_foreachScreenVert( BKE_mesh_foreach_mapped_vert(me, mesh_foreachScreenVert__mapFunc, &data, MESH_FOREACH_NOP); } -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit-Mesh: For Each Screen Mesh Edge + * \{ */ static void mesh_foreachScreenEdge__mapFunc(void *userData, int index, - const float v0co[3], - const float v1co[3]) + const float v_a[3], + const float v_b[3]) { foreachScreenEdge_userData *data = userData; BMEdge *eed = BM_edge_at_index(data->vc.em->bm, index); + if (UNLIKELY(BM_elem_flag_test(eed, BM_ELEM_HIDDEN))) { + return; + } - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - float screen_co_a[2]; - float screen_co_b[2]; - eV3DProjTest clip_flag_nowin = data->clip_flag & ~V3D_PROJ_TEST_CLIP_WIN; - - if (ED_view3d_project_float_object(data->vc.region, v0co, screen_co_a, clip_flag_nowin) != - V3D_PROJ_RET_OK) { - return; - } - if (ED_view3d_project_float_object(data->vc.region, v1co, screen_co_b, clip_flag_nowin) != - V3D_PROJ_RET_OK) { - return; - } - - if (data->clip_flag & V3D_PROJ_TEST_CLIP_WIN) { - if (!BLI_rctf_isect_segment(&data->win_rect, screen_co_a, screen_co_b)) { - return; - } - } - - data->func(data->userData, eed, screen_co_a, screen_co_b, index); + float screen_co_a[2], screen_co_b[2]; + if (!view3d_project_segment_to_screen_with_content_clip_planes(data->vc.region, + v_a, + v_b, + data->clip_flag, + &data->win_rect, + data->content_planes, + data->content_planes_len, + screen_co_a, + screen_co_b)) { + return; } + + data->func(data->userData, eed, screen_co_a, screen_co_b, index); } void mesh_foreachScreenEdge(ViewContext *vc, @@ -254,11 +430,23 @@ void mesh_foreachScreenEdge(ViewContext *vc, ED_view3d_clipping_local(vc->rv3d, vc->obedit->obmat); /* for local clipping lookups */ } + if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) { + data.content_planes_len = content_planes_from_clip_flag( + vc->region, vc->obedit, clip_flag, data.content_planes); + } + else { + data.content_planes_len = 0; + } + BM_mesh_elem_table_ensure(vc->em->bm, BM_EDGE); BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge__mapFunc, &data); } -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit-Mesh: For Each Screen Edge (Bounding Box Clipped) + * \{ */ /** * Only call for bound-box clipping. @@ -266,46 +454,36 @@ void mesh_foreachScreenEdge(ViewContext *vc, */ static void mesh_foreachScreenEdge_clip_bb_segment__mapFunc(void *userData, int index, - const float v0co[3], - const float v1co[3]) + const float v_a[3], + const float v_b[3]) { foreachScreenEdge_userData *data = userData; BMEdge *eed = BM_edge_at_index(data->vc.em->bm, index); + if (UNLIKELY(BM_elem_flag_test(eed, BM_ELEM_HIDDEN))) { + return; + } BLI_assert(data->clip_flag & V3D_PROJ_TEST_CLIP_BB); - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - float v0co_clip[3]; - float v1co_clip[3]; - - if (!clip_segment_v3_plane_n(v0co, v1co, data->vc.rv3d->clip_local, 4, v0co_clip, v1co_clip)) { - return; - } - - float screen_co_a[2]; - float screen_co_b[2]; - - /* Clipping already handled, no need to check in projection. */ - eV3DProjTest clip_flag_nowin = data->clip_flag & - ~(V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_BB); - - if (ED_view3d_project_float_object(data->vc.region, v0co_clip, screen_co_a, clip_flag_nowin) != - V3D_PROJ_RET_OK) { - return; - } - if (ED_view3d_project_float_object(data->vc.region, v1co_clip, screen_co_b, clip_flag_nowin) != - V3D_PROJ_RET_OK) { - return; - } - - if (data->clip_flag & V3D_PROJ_TEST_CLIP_WIN) { - if (!BLI_rctf_isect_segment(&data->win_rect, screen_co_a, screen_co_b)) { - return; - } - } + float v_a_clip[3], v_b_clip[3]; + if (!clip_segment_v3_plane_n(v_a, v_b, data->vc.rv3d->clip_local, 4, v_a_clip, v_b_clip)) { + return; + } - data->func(data->userData, eed, screen_co_a, screen_co_b, index); + float screen_co_a[2], screen_co_b[2]; + if (!view3d_project_segment_to_screen_with_content_clip_planes(data->vc.region, + v_a_clip, + v_b_clip, + data->clip_flag, + &data->win_rect, + data->content_planes, + data->content_planes_len, + screen_co_a, + screen_co_b)) { + return; } + + data->func(data->userData, eed, screen_co_a, screen_co_b, index); } /** @@ -339,6 +517,14 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc, data.userData = userData; data.clip_flag = clip_flag; + if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) { + data.content_planes_len = content_planes_from_clip_flag( + vc->region, vc->obedit, clip_flag, data.content_planes); + } + else { + data.content_planes_len = 0; + } + BM_mesh_elem_table_ensure(vc->em->bm, BM_EDGE); if ((clip_flag & V3D_PROJ_TEST_CLIP_BB) && (vc->rv3d->clipbb != NULL)) { @@ -350,7 +536,11 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc, } } -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit-Mesh: For Each Screen Face Center + * \{ */ static void mesh_foreachScreenFace__mapFunc(void *userData, int index, @@ -359,14 +549,17 @@ static void mesh_foreachScreenFace__mapFunc(void *userData, { foreachScreenFace_userData *data = userData; BMFace *efa = BM_face_at_index(data->vc.em->bm, index); + if (UNLIKELY(BM_elem_flag_test(efa, BM_ELEM_HIDDEN))) { + return; + } - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - float screen_co[2]; - if (ED_view3d_project_float_object(data->vc.region, cent, screen_co, data->clip_flag) == - V3D_PROJ_RET_OK) { - data->func(data->userData, efa, screen_co, index); - } + float screen_co[2]; + if (ED_view3d_project_float_object(data->vc.region, cent, screen_co, data->clip_flag) != + V3D_PROJ_RET_OK) { + return; } + + data->func(data->userData, efa, screen_co, index); } void mesh_foreachScreenFace( @@ -375,6 +568,7 @@ void mesh_foreachScreenFace( void *userData, const eV3DProjTest clip_flag) { + BLI_assert((clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) == 0); foreachScreenFace_userData data; Mesh *me = editbmesh_get_eval_cage_from_orig( @@ -398,7 +592,11 @@ void mesh_foreachScreenFace( } } -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit-Nurbs: For Each Screen Vertex + * \{ */ void nurbs_foreachScreenVert(ViewContext *vc, void (*func)(void *userData, @@ -486,7 +684,11 @@ void nurbs_foreachScreenVert(ViewContext *vc, } } -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit-Meta: For Each Screen Meta-Element + * \{ */ /* ED_view3d_init_mats_rv3d must be called first */ void mball_foreachScreenElem(struct ViewContext *vc, @@ -510,7 +712,11 @@ void mball_foreachScreenElem(struct ViewContext *vc, } } -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit-Lattice: For Each Screen Vertex + * \{ */ void lattice_foreachScreenVert(ViewContext *vc, void (*func)(void *userData, BPoint *bp, const float screen_co[2]), @@ -543,7 +749,11 @@ void lattice_foreachScreenVert(ViewContext *vc, } } -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit-Armature: For Each Screen Bone + * \{ */ /* ED_view3d_init_mats_rv3d must be called first */ void armature_foreachScreenBone(struct ViewContext *vc, @@ -559,39 +769,59 @@ void armature_foreachScreenBone(struct ViewContext *vc, ED_view3d_check_mats_rv3d(vc->rv3d); - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - float screen_co_a[2], screen_co_b[2]; - int points_proj_tot = 0; + float content_planes[6][4]; + int content_planes_len; + rctf win_rect; + + if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) { + content_planes_len = content_planes_from_clip_flag( + vc->region, vc->obedit, clip_flag, content_planes); + win_rect.xmin = 0; + win_rect.ymin = 0; + win_rect.xmax = vc->region->winx; + win_rect.ymax = vc->region->winy; + } + else { + content_planes_len = 0; + } - /* project head location to screenspace */ - if (ED_view3d_project_float_object(vc->region, ebone->head, screen_co_a, clip_flag) == - V3D_PROJ_RET_OK) { - points_proj_tot++; - } - else { - screen_co_a[0] = IS_CLIPPED; /* weak */ - /* screen_co_a[1]: intentionally don't set this so we get errors on misuse */ - } + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (!EBONE_VISIBLE(arm, ebone)) { + continue; + } - /* project tail location to screenspace */ - if (ED_view3d_project_float_object(vc->region, ebone->tail, screen_co_b, clip_flag) == - V3D_PROJ_RET_OK) { - points_proj_tot++; - } - else { - screen_co_b[0] = IS_CLIPPED; /* weak */ - /* screen_co_b[1]: intentionally don't set this so we get errors on misuse */ + float screen_co_a[2], screen_co_b[2]; + const float *v_a = ebone->head, *v_b = ebone->tail; + + if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) { + if (!view3d_project_segment_to_screen_with_content_clip_planes(vc->region, + v_a, + v_b, + clip_flag, + &win_rect, + content_planes, + content_planes_len, + screen_co_a, + screen_co_b)) { + continue; } - - if (points_proj_tot) { /* at least one point's projection worked */ - func(userData, ebone, screen_co_a, screen_co_b); + } + else { + if (!view3d_project_segment_to_screen_with_clip_tag( + vc->region, v_a, v_b, clip_flag, screen_co_a, screen_co_b)) { + continue; } } + + func(userData, ebone, screen_co_a, screen_co_b); } } -/* ------------------------------------------------------------------------ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pose: For Each Screen Bone + * \{ */ /* ED_view3d_init_mats_rv3d must be called first */ /* almost _exact_ copy of #armature_foreachScreenBone */ @@ -610,35 +840,53 @@ void pose_foreachScreenBone(struct ViewContext *vc, ED_view3d_check_mats_rv3d(vc->rv3d); + float content_planes[6][4]; + int content_planes_len; + rctf win_rect; + + if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) { + content_planes_len = content_planes_from_clip_flag( + vc->region, ob_eval, clip_flag, content_planes); + win_rect.xmin = 0; + win_rect.ymin = 0; + win_rect.xmax = vc->region->winx; + win_rect.ymax = vc->region->winy; + } + else { + content_planes_len = 0; + } + for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - if (PBONE_VISIBLE(arm_eval, pchan->bone)) { - bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name); - float screen_co_a[2], screen_co_b[2]; - int points_proj_tot = 0; - - /* project head location to screenspace */ - if (ED_view3d_project_float_object( - vc->region, pchan_eval->pose_head, screen_co_a, clip_flag) == V3D_PROJ_RET_OK) { - points_proj_tot++; - } - else { - screen_co_a[0] = IS_CLIPPED; /* weak */ - /* screen_co_a[1]: intentionally don't set this so we get errors on misuse */ - } + if (!PBONE_VISIBLE(arm_eval, pchan->bone)) { + continue; + } - /* project tail location to screenspace */ - if (ED_view3d_project_float_object( - vc->region, pchan_eval->pose_tail, screen_co_b, clip_flag) == V3D_PROJ_RET_OK) { - points_proj_tot++; + bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name); + float screen_co_a[2], screen_co_b[2]; + const float *v_a = pchan_eval->pose_head, *v_b = pchan_eval->pose_tail; + + if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) { + if (!view3d_project_segment_to_screen_with_content_clip_planes(vc->region, + v_a, + v_b, + clip_flag, + &win_rect, + content_planes, + content_planes_len, + screen_co_a, + screen_co_b)) { + continue; } - else { - screen_co_b[0] = IS_CLIPPED; /* weak */ - /* screen_co_b[1]: intentionally don't set this so we get errors on misuse */ - } - - if (points_proj_tot) { /* at least one point's projection worked */ - func(userData, pchan, screen_co_a, screen_co_b); + } + else { + if (!view3d_project_segment_to_screen_with_clip_tag( + vc->region, v_a, v_b, clip_flag, screen_co_a, screen_co_b)) { + continue; } } + + func(userData, pchan, screen_co_a, screen_co_b); } } + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index e602521f6a2..04f382de56b 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -961,7 +961,7 @@ static void view3d_interactive_add_calc_plane(bContext *C, const float view_axis_dot = fabsf(dot_v3v3(rv3d->viewinv[2], r_matrix_orient[plane_axis])); if (view_axis_dot < eps_view_align) { /* In this case, just project onto the view plane as it's important the location - * is _always_ under the mouse cursor, even if it turns out that wont lie on + * is _always_ under the mouse cursor, even if it turns out that won't lie on * the original 'plane' that's been calculated for us. */ plane_normal = rv3d->viewinv[2]; } @@ -974,7 +974,7 @@ static void view3d_interactive_add_calc_plane(bContext *C, /* Even if the calculation works, it's possible the point found is behind the view, * or very far away (past the far clipping). - * In either case creating objects wont be useful. */ + * In either case creating objects won't be useful. */ if (rv3d->is_persp) { float dir[3]; sub_v3_v3v3(dir, rv3d->viewinv[3], r_co_src); diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 7547f8ee434..49da1764660 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -788,7 +788,7 @@ bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, /** \name Utility functions for projection * \{ */ -void ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, Object *ob, float r_pmat[4][4]) +void ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob, float r_pmat[4][4]) { float vmat[4][4]; diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 757ed13ac28..02ed9f6d791 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -520,42 +520,16 @@ static void do_lasso_select_pose__do_tag(void *userData, const float screen_co_b[2]) { LassoSelectUserData *data = userData; - bArmature *arm = data->vc->obact->data; - - if (PBONE_SELECTABLE(arm, pchan->bone)) { - bool is_point_done = false; - int points_proj_tot = 0; - - /* project head location to screenspace */ - 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->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) { - is_point_done = true; - } - } - - /* project tail location to screenspace */ - 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->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) { - is_point_done = true; - } - } + const bArmature *arm = data->vc->obact->data; + if (!PBONE_SELECTABLE(arm, pchan->bone)) { + return; + } - /* 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->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; + if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) && + 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 = true; } } static void do_lasso_tag_pose(ViewContext *vc, @@ -580,7 +554,11 @@ static void do_lasso_tag_pose(ViewContext *vc, ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d); - pose_foreachScreenBone(&vc_tmp, do_lasso_select_pose__do_tag, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + /* Treat bones as clipped segments (no joints). */ + pose_foreachScreenBone(&vc_tmp, + do_lasso_select_pose__do_tag, + &data, + V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); } static bool do_lasso_select_objects(ViewContext *vc, @@ -876,11 +854,16 @@ static bool do_lasso_select_mesh(ViewContext *vc, const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR | (use_zbuf ? 0 : V3D_PROJ_TEST_CLIP_BB); + /* Fully inside. */ mesh_foreachScreenEdge_clip_bb_segment( vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag); if (data.is_done == false) { - mesh_foreachScreenEdge_clip_bb_segment( - vc, do_lasso_select_mesh__doSelectEdge_pass1, &data_for_edge, clip_flag); + /* Fall back to partially inside. + * Clip content to account for edges partially behind the view. */ + mesh_foreachScreenEdge_clip_bb_segment(vc, + do_lasso_select_mesh__doSelectEdge_pass1, + &data_for_edge, + clip_flag | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); } } @@ -1022,46 +1005,76 @@ static void do_lasso_select_armature__doSelectBone(void *userData, const float screen_co_b[2]) { LassoSelectUserData *data = userData; - bArmature *arm = data->vc->obedit->data; - if (EBONE_VISIBLE(arm, ebone)) { - int is_ignore_flag = 0; - int is_inside_flag = 0; - - if (screen_co_a[0] != IS_CLIPPED) { - if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && - BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) { - is_inside_flag |= BONESEL_ROOT; - } - } - else { - is_ignore_flag |= BONESEL_ROOT; - } + const bArmature *arm = data->vc->obedit->data; + if (!EBONE_VISIBLE(arm, ebone)) { + return; + } - if (screen_co_b[0] != IS_CLIPPED) { - if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && - BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) { - is_inside_flag |= BONESEL_TIP; - } + int is_ignore_flag = 0; + int is_inside_flag = 0; + + if (screen_co_a[0] != IS_CLIPPED) { + if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) { + is_inside_flag |= BONESEL_ROOT; } - else { - is_ignore_flag |= BONESEL_TIP; + } + else { + is_ignore_flag |= BONESEL_ROOT; + } + + if (screen_co_b[0] != IS_CLIPPED) { + if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) { + is_inside_flag |= BONESEL_TIP; } + } + else { + is_ignore_flag |= BONESEL_TIP; + } - if (is_ignore_flag == 0) { - if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) || - 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; - } + if (is_ignore_flag == 0) { + if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) || + 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; } + } + + ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16); +} +static void do_lasso_select_armature__doSelectBone_clip_content(void *userData, + EditBone *ebone, + const float screen_co_a[2], + const float screen_co_b[2]) +{ + LassoSelectUserData *data = userData; + bArmature *arm = data->vc->obedit->data; + if (!EBONE_VISIBLE(arm, ebone)) { + return; + } + + const int is_ignore_flag = ebone->temp.i << 16; + int is_inside_flag = ebone->temp.i & ~0xFFFF; - ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16); + /* - When #BONESEL_BONE is set, there is nothing to do. + * - When #BONE_ROOTSEL or #BONE_TIPSEL have been set - they take priority over bone selection. + */ + if (is_inside_flag & (BONESEL_BONE | BONE_ROOTSEL | BONE_TIPSEL)) { + return; + } + + if (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; } + + ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16); } static bool do_lasso_select_armature(ViewContext *vc, @@ -1086,9 +1099,18 @@ static bool do_lasso_select_armature(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); + /* Operate on fully visible (non-clipped) points. */ armature_foreachScreenBone( vc, do_lasso_select_armature__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + /* Operate on bones as segments clipped to the viewport bounds + * (needed to handle bones with both points outside the view). + * A separate pass is needed since clipped coordinates can't be used for selecting joints. */ + armature_foreachScreenBone(vc, + do_lasso_select_armature__doSelectBone_clip_content, + &data, + V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); + data.is_changed |= ED_armature_edit_select_op_from_tagged(vc->obedit->data, sel_op); if (data.is_changed) { @@ -3071,6 +3093,9 @@ struct BoxSelectUserData_ForMeshEdge { struct EditSelectBuf_Cache *esel; uint backbuf_offset; }; +/** + * Pass 0 operates on edges when fully inside. + */ static void do_mesh_box_select__doSelectEdge_pass0( void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) { @@ -3092,6 +3117,9 @@ static void do_mesh_box_select__doSelectEdge_pass0( data->is_changed = true; } } +/** + * Pass 1 operates on edges when partially inside. + */ static void do_mesh_box_select__doSelectEdge_pass1( void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) { @@ -3181,11 +3209,16 @@ static bool do_mesh_box_select(ViewContext *vc, const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR | (use_zbuf ? 0 : V3D_PROJ_TEST_CLIP_BB); + /* Fully inside. */ mesh_foreachScreenEdge_clip_bb_segment( vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag); if (data.is_done == false) { - mesh_foreachScreenEdge_clip_bb_segment( - vc, do_mesh_box_select__doSelectEdge_pass1, &cb_data, clip_flag); + /* Fall back to partially inside. + * Clip content to account for edges partially behind the view. */ + mesh_foreachScreenEdge_clip_bb_segment(vc, + do_mesh_box_select__doSelectEdge_pass1, + &cb_data, + clip_flag | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); } } @@ -3725,6 +3758,9 @@ static bool mesh_circle_select(ViewContext *vc, if (SEL_OP_USE_PRE_DESELECT(sel_op)) { if (vc->em->bm->totvertsel) { EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT); + vc->em->bm->totvertsel = 0; + vc->em->bm->totedgesel = 0; + vc->em->bm->totfacesel = 0; changed = true; } } @@ -3771,7 +3807,10 @@ static bool mesh_circle_select(ViewContext *vc, } else { mesh_foreachScreenEdge_clip_bb_segment( - vc, mesh_circle_doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + vc, + mesh_circle_doSelectEdge, + &data, + (V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT)); } } @@ -3790,7 +3829,8 @@ static bool mesh_circle_select(ViewContext *vc, changed |= data.is_changed; if (changed) { - EDBM_selectmode_flush(vc->em); + BM_mesh_select_mode_flush_ex( + vc->em->bm, vc->em->selectmode, BM_SELECT_LEN_FLUSH_RECALC_NOTHING); } return changed; } @@ -4016,47 +4056,48 @@ static void do_circle_select_pose__doSelectBone(void *userData, { CircleSelectUserData *data = userData; bArmature *arm = data->vc->obact->data; + if (!PBONE_SELECTABLE(arm, pchan->bone)) { + return; + } - if (PBONE_SELECTABLE(arm, pchan->bone)) { - bool is_point_done = false; - int points_proj_tot = 0; + bool is_point_done = false; + int points_proj_tot = 0; - /* project head location to screenspace */ - if (screen_co_a[0] != IS_CLIPPED) { - points_proj_tot++; - if (pchan_circle_doSelectJoint(data, pchan, screen_co_a)) { - is_point_done = true; - } + /* project head location to screenspace */ + if (screen_co_a[0] != IS_CLIPPED) { + points_proj_tot++; + if (pchan_circle_doSelectJoint(data, pchan, screen_co_a)) { + is_point_done = true; } + } - /* project tail location to screenspace */ - if (screen_co_b[0] != IS_CLIPPED) { - points_proj_tot++; - if (pchan_circle_doSelectJoint(data, pchan, screen_co_b)) { - is_point_done = true; - } + /* project tail location to screenspace */ + if (screen_co_b[0] != IS_CLIPPED) { + points_proj_tot++; + if (pchan_circle_doSelectJoint(data, pchan, screen_co_b)) { + is_point_done = true; } + } - /* check if the head and/or tail is in the circle - * - the call to check also does the selection already - */ + /* check if the head and/or tail is in the circle + * - the call to check also does the selection already + */ - /* only if the endpoints didn't get selected, deal with the middle of the bone too - * It works nicer to only do this if the head or tail are not in the circle, - * otherwise there is no way to circle select joints alone */ - if ((is_point_done == false) && (points_proj_tot == 2) && - edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { - if (data->select) { - pchan->bone->flag |= BONE_SELECTED; - } - else { - pchan->bone->flag &= ~BONE_SELECTED; - } - data->is_changed = true; + /* only if the endpoints didn't get selected, deal with the middle of the bone too + * It works nicer to only do this if the head or tail are not in the circle, + * otherwise there is no way to circle select joints alone */ + if ((is_point_done == false) && (points_proj_tot == 2) && + edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { + if (data->select) { + pchan->bone->flag |= BONE_SELECTED; } - - data->is_changed |= is_point_done; + else { + pchan->bone->flag &= ~BONE_SELECTED; + } + data->is_changed = true; } + + data->is_changed |= is_point_done; } static bool pose_circle_select(ViewContext *vc, const eSelectOp sel_op, @@ -4075,8 +4116,11 @@ static bool pose_circle_select(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */ - pose_foreachScreenBone( - vc, do_circle_select_pose__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + /* Treat bones as clipped segments (no joints). */ + pose_foreachScreenBone(vc, + do_circle_select_pose__doSelectBone, + &data, + V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); if (data.is_changed) { ED_pose_bone_select_tag_update(vc->obact); @@ -4118,47 +4162,74 @@ static void do_circle_select_armature__doSelectBone(void *userData, const float screen_co_b[2]) { CircleSelectUserData *data = userData; - bArmature *arm = data->vc->obedit->data; + const bArmature *arm = data->vc->obedit->data; + if (!(data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone))) { + return; + } - if (data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone)) { - bool is_point_done = false; - int points_proj_tot = 0; + /* When true, ignore in the next pass. */ + ebone->temp.i = false; - /* project head location to screenspace */ - if (screen_co_a[0] != IS_CLIPPED) { - points_proj_tot++; - if (armature_circle_doSelectJoint(data, ebone, screen_co_a, true)) { - is_point_done = true; - } + bool is_point_done = false; + bool is_edge_done = false; + int points_proj_tot = 0; + + /* project head location to screenspace */ + if (screen_co_a[0] != IS_CLIPPED) { + points_proj_tot++; + if (armature_circle_doSelectJoint(data, ebone, screen_co_a, true)) { + is_point_done = true; } + } - /* project tail location to screenspace */ - if (screen_co_b[0] != IS_CLIPPED) { - points_proj_tot++; - if (armature_circle_doSelectJoint(data, ebone, screen_co_b, false)) { - is_point_done = true; - } + /* project tail location to screenspace */ + if (screen_co_b[0] != IS_CLIPPED) { + points_proj_tot++; + if (armature_circle_doSelectJoint(data, ebone, screen_co_b, false)) { + is_point_done = true; } + } - /* check if the head and/or tail is in the circle - * - the call to check also does the selection already - */ + /* check if the head and/or tail is in the circle + * - the call to check also does the selection already + */ - /* only if the endpoints didn't get selected, deal with the middle of the bone too - * It works nicer to only do this if the head or tail are not in the circle, - * otherwise there is no way to circle select joints alone */ - if ((is_point_done == false) && (points_proj_tot == 2) && - edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { - if (data->select) { - ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - data->is_changed = true; - } + /* only if the endpoints didn't get selected, deal with the middle of the bone too + * It works nicer to only do this if the head or tail are not in the circle, + * otherwise there is no way to circle select joints alone */ + if ((is_point_done == false) && (points_proj_tot == 2) && + edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { + SET_FLAG_FROM_TEST(ebone->flag, data->select, BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + is_edge_done = true; + data->is_changed = true; + } + + if (is_point_done || is_edge_done) { + ebone->temp.i = true; + } + + data->is_changed |= is_point_done; +} +static void do_circle_select_armature__doSelectBone_clip_content(void *userData, + struct EditBone *ebone, + const float screen_co_a[2], + const float screen_co_b[2]) +{ + CircleSelectUserData *data = userData; + bArmature *arm = data->vc->obedit->data; - data->is_changed |= is_point_done; + if (!(data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone))) { + return; + } + + /* Set in the first pass, needed so circle select prioritizes joints. */ + if (ebone->temp.i == true) { + return; + } + + if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { + SET_FLAG_FROM_TEST(ebone->flag, data->select, BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + data->is_changed = true; } } static bool armature_circle_select(ViewContext *vc, @@ -4179,9 +4250,18 @@ static bool armature_circle_select(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); + /* Operate on fully visible (non-clipped) points. */ armature_foreachScreenBone( vc, do_circle_select_armature__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + /* Operate on bones as segments clipped to the viewport bounds + * (needed to handle bones with both points outside the view). + * A separate pass is needed since clipped coordinates can't be used for selecting joints. */ + armature_foreachScreenBone(vc, + do_circle_select_armature__doSelectBone_clip_content, + &data, + V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); + if (data.is_changed) { ED_armature_edit_sync_selection(arm->edbo); ED_armature_edit_validate_active(arm); @@ -4362,7 +4442,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) FOREACH_OBJECT_IN_MODE_END; } else if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) { - if (PE_circle_select(C, sel_op, mval, (float)radius)) { + if (PE_circle_select(C, wm_userdata, sel_op, mval, (float)radius)) { return OPERATOR_FINISHED; } return OPERATOR_CANCELLED; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 8ae5d4a29e9..ea0b1f396c3 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1017,7 +1017,7 @@ static float view_autodist_depth_margin(ARegion *region, const int mval[2], int } ViewDepths depth_temp = {0}; - view3d_update_depths_rect(region, &depth_temp, &rect); + view3d_depths_rect_create(region, &rect, &depth_temp); float depth_close = view3d_depth_near(&depth_temp); MEM_SAFE_FREE(depth_temp.depths); return depth_close; @@ -1044,7 +1044,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph, bool depth_ok = false; /* Get Z Depths, needed for perspective, nice for ortho */ - ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); + ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); /* Attempt with low margin's first */ int i = 0; @@ -1694,7 +1694,8 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd, return false; } -bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, +bool ED_view3d_depth_read_cached_normal(const ARegion *region, + const ViewDepths *depths, const int mval[2], float r_normal[3]) { @@ -1705,9 +1706,6 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, bool depths_valid[9] = {false}; float coords[9][3] = {{0}}; - ARegion *region = vc->region; - const ViewDepths *depths = vc->rv3d->depths; - for (int x = 0, i = 0; x < 2; x++) { for (int y = 0; y < 2; y++) { const int mval_ofs[2] = {mval[0] + (x - 1), mval[1] + (y - 1)}; @@ -1761,11 +1759,4 @@ bool ED_view3d_depth_unproject_v3(const ARegion *region, return ED_view3d_unproject_v3(region, centx, centy, depth, r_location_world); } -void ED_view3d_depth_tag_update(RegionView3D *rv3d) -{ - if (rv3d->depths) { - rv3d->depths->damaged = true; - } -} - /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 21cb8560e9b..056b2507745 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -235,7 +235,7 @@ void ED_view3d_smooth_view_ex( /* grid draw as floor */ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { /* use existing if exists, means multiple calls to smooth view - * wont lose the original 'view' setting */ + * won't lose the original 'view' setting */ rv3d->view = RV3D_VIEW_USER; } @@ -244,7 +244,7 @@ void ED_view3d_smooth_view_ex( /* if this is view rotation only * we can decrease the time allowed by * the angle between quats - * this means small rotations wont lag */ + * this means small rotations won't lag */ if (sview->quat && !sview->ofs && !sview->dist) { /* scale the time allowed by the rotation */ /* 180deg == 1.0 */ @@ -1393,6 +1393,7 @@ static void view3d_localview_exit(const Depsgraph *depsgraph, MEM_freeN(v3d->localvd); v3d->localvd = NULL; + MEM_SAFE_FREE(v3d->runtime.local_stats); LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { @@ -1516,7 +1517,7 @@ static int localview_remove_from_exec(bContext *C, wmOperator *op) } if (changed) { - DEG_on_visible_update(bmain, false); + DEG_tag_on_visible_update(bmain, false); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index f0ced665679..2b26ef3b3e4 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -149,6 +149,9 @@ typedef enum { T_AUTOMERGE = 1 << 20, /** Runs auto-merge & splits. */ T_AUTOSPLIT = 1 << 21, + + /** No cursor wrapping on region bounds */ + T_NO_CURSOR_WRAP = 1 << 23, } eTFlag; /** #TransInfo.modifiers */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index aaea9d05f84..f29f323558a 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -1449,9 +1449,9 @@ void recalcData_pose(TransInfo *t) /* TODO: autokeyframe calls need some setting to specify to add samples * (FPoints) instead of keyframes? */ if ((t->animtimer) && (t->context) && IS_AUTOKEY_ON(t->scene)) { - int targetless_ik = - (t->flag & - T_AUTOIK); /* XXX this currently doesn't work, since flags aren't set yet! */ + + /* XXX: this currently doesn't work, since flags aren't set yet! */ + int targetless_ik = (t->flag & T_AUTOIK); animrecord_check_state(t, ob); autokeyframe_pose(t->context, t->scene, ob, t->mode, targetless_ik); diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c index e57fd85470f..cc3091ae70e 100644 --- a/source/blender/editors/transform/transform_convert_curve.c +++ b/source/blender/editors/transform/transform_convert_curve.c @@ -453,7 +453,7 @@ void recalcData_curve(TransInfo *t) if (t->state == TRANS_CANCEL) { while (nu) { - /* Cant do testhandlesNurb here, it messes up the h1 and h2 flags */ + /* Can't do testhandlesNurb here, it messes up the h1 and h2 flags */ BKE_nurb_handles_calc(nu); nu = nu->next; } diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 422370cb13b..4c674136b6a 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1842,13 +1842,11 @@ void recalcData_mesh(TransInfo *t) * It's impractical to calculate this ahead of time. * Further, the down side of using partial updates when their not needed is negligible. */ if (em->bm->totvert == em->bm->totvertsel) { - EDBM_mesh_normals_update(em); - BKE_editmesh_looptri_calc(em); + BKE_editmesh_looptri_and_normals_calc(em); } else { BMPartialUpdate *partial_update_cache = tc_mesh_ensure_partial_update(t, tc); - BM_mesh_normals_update_with_partial(em->bm, partial_update_cache); - BKE_editmesh_looptri_calc_with_partial(em, partial_update_cache); + BKE_editmesh_looptri_and_normals_calc_with_partial(em, partial_update_cache); } } } diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c index 7c61da31f72..69b44998980 100644 --- a/source/blender/editors/transform/transform_convert_mesh_skin.c +++ b/source/blender/editors/transform/transform_convert_mesh_skin.c @@ -300,8 +300,7 @@ void recalcData_mesh_skin(TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - EDBM_mesh_normals_update(em); - BKE_editmesh_looptri_calc(em); + BKE_editmesh_looptri_and_normals_calc(em); } } /** \} */ diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index 12c4d0816ae..9d2d3713bf0 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -27,6 +27,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_rect.h" #include "BKE_context.h" #include "BKE_node.h" @@ -35,6 +36,7 @@ #include "ED_node.h" #include "UI_interface.h" +#include "UI_view2d.h" #include "transform.h" #include "transform_convert.h" @@ -44,6 +46,12 @@ /** \name Node Transform Creation * \{ */ +typedef struct NodeTransCustomData { + /* Initial rect of the view2d, used for computing offset during edge panning */ + rctf initial_v2d_cur; + View2DEdgePanData edge_pan; +} NodeTransCustomData; + /* transcribe given node into TransData2D for Transforming */ static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const float dpi_fac) { @@ -107,6 +115,24 @@ void createTransNodeData(TransInfo *t) const float dpi_fac = UI_DPI_FAC; SpaceNode *snode = t->area->spacedata.first; + if (t->mode == TFM_TRANSLATION) { + /* Disable cursor wrapping in the node editor for edge pan */ + t->flag |= T_NO_CURSOR_WRAP; + } + + /* Custom data to enable edge panning during the node transform */ + NodeTransCustomData *customdata = MEM_callocN(sizeof(*customdata), __func__); + UI_view2d_edge_pan_init(t->context, + &customdata->edge_pan, + NODE_EDGE_PAN_INSIDE_PAD, + NODE_EDGE_PAN_OUTSIDE_PAD, + NODE_EDGE_PAN_SPEED_RAMP, + NODE_EDGE_PAN_MAX_SPEED, + NODE_EDGE_PAN_DELAY); + customdata->initial_v2d_cur = t->region->v2d.cur; + t->custom.type.data = customdata; + t->custom.type.use_free = true; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); tc->data_len = 0; @@ -150,6 +176,19 @@ void flushTransNodes(TransInfo *t) { const float dpi_fac = UI_DPI_FAC; + NodeTransCustomData *customdata = (NodeTransCustomData *)t->custom.type.data; + + if (t->mode == TFM_TRANSLATION) { + /* Edge panning functions expect window coordinates, mval is relative to region */ + const float x = t->region->winrct.xmin + t->mval[0]; + const float y = t->region->winrct.ymin + t->mval[1]; + UI_view2d_edge_pan_apply(t->context, &customdata->edge_pan, x, y); + } + + /* Initial and current view2D rects for additional transform due to view panning and zooming */ + const rctf *rect_src = &customdata->initial_v2d_cur; + const rctf *rect_dst = &t->region->v2d.cur; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { applyGridAbsolute(t); @@ -159,23 +198,28 @@ void flushTransNodes(TransInfo *t) TransData2D *td2d = &tc->data_2d[i]; bNode *node = td->extra; - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + float loc[2]; + copy_v2_v2(loc, td2d->loc); + + /* additional offset due to change in view2D rect */ + BLI_rctf_transform_pt_v(rect_dst, rect_src, loc, loc); + #ifdef USE_NODE_CENTER - float locx = (td2d->loc[0] - (BLI_rctf_size_x(&node->totr)) * +0.5f) / dpi_fac; - float locy = (td2d->loc[1] - (BLI_rctf_size_y(&node->totr)) * -0.5f) / dpi_fac; -#else - float locx = td2d->loc[0] / dpi_fac; - float locy = td2d->loc[1] / dpi_fac; + loc[0] -= 0.5f * BLI_rctf_size_x(&node->totr); + loc[1] += 0.5f * BLI_rctf_size_y(&node->totr); #endif + /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + loc[0] /= dpi_fac; + loc[1] /= dpi_fac; + /* account for parents (nested nodes) */ if (node->parent) { - nodeFromView(node->parent, locx, locy, &node->locx, &node->locy); - } - else { - node->locx = locx; - node->locy = locy; + nodeFromView(node->parent, loc[0], loc[1], &loc[0], &loc[1]); } + + node->locx = loc[0]; + node->locy = loc[1]; } /* handle intersection with noodles */ diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 6a09008e657..a67d18db37b 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -33,6 +33,7 @@ #include "ED_markers.h" +#include "SEQ_iterator.h" #include "SEQ_relations.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" @@ -76,12 +77,9 @@ typedef struct TransSeq { /* This function applies the rules for transforming a strip so duplicate * checks don't need to be added in multiple places. * - * recursive, count and flag MUST be set. - * - * seq->depth must be set before running this function so we know if the strips - * are root level or not + * count and flag MUST be set. */ -static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_count, int *r_flag) +static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_count, int *r_flag) { /* for extend we need to do some tricks */ if (t->mode == TFM_TIME_EXTEND) { @@ -93,13 +91,11 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c int left = SEQ_transform_get_left_handle_frame(seq); int right = SEQ_transform_get_right_handle_frame(seq); - if (seq->depth == 0 && ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK))) { - *r_recursive = false; + if (((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK))) { *r_count = 0; *r_flag = 0; } else { - *r_recursive = false; *r_count = 1; /* unless its set to 0, extend will never set 2 handles at once */ *r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL); @@ -131,61 +127,34 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c /* *** Normal Transform *** */ - if (seq->depth == 0) { - - /* Count */ + /* Count */ - /* Non nested strips (resect selection and handles) */ - if ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK)) { - *r_recursive = false; - *r_count = 0; - *r_flag = 0; + /* Non nested strips (resect selection and handles) */ + if ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK)) { + *r_count = 0; + *r_flag = 0; + } + else { + if ((seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == (SEQ_LEFTSEL | SEQ_RIGHTSEL)) { + *r_flag = seq->flag; + *r_count = 2; /* we need 2 transdata's */ } else { - if ((seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == (SEQ_LEFTSEL | SEQ_RIGHTSEL)) { - *r_flag = seq->flag; - *r_count = 2; /* we need 2 transdata's */ - } - else { - *r_flag = seq->flag; - *r_count = 1; /* selected or with a handle selected */ - } - - /* Recursive */ - - if ((seq->type == SEQ_TYPE_META) && ((seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == 0)) { - /* if any handles are selected, don't recurse */ - *r_recursive = true; - } - else { - *r_recursive = false; - } + *r_flag = seq->flag; + *r_count = 1; /* selected or with a handle selected */ } } - else { - /* Nested, different rules apply */ - - *r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL); - *r_count = 1; /* ignore the selection for nested */ - *r_recursive = (seq->type == SEQ_TYPE_META); - } } } -static int SeqTransCount(TransInfo *t, ListBase *seqbase, int depth) +static int SeqTransCount(TransInfo *t, ListBase *seqbase) { Sequence *seq; - int tot = 0, recursive, count, flag; + int tot = 0, count, flag; for (seq = seqbase->first; seq; seq = seq->next) { - seq->depth = depth; - - SeqTransInfo(t, seq, &recursive, &count, &flag); /* ignore the flag */ + SeqTransInfo(t, seq, &count, &flag); /* ignore the flag */ tot += count; - - if (recursive) { - tot += SeqTransCount(t, &seq->seqbase, depth + 1); - } } return tot; @@ -251,27 +220,16 @@ static TransData *SeqToTransData( return td; } -static int SeqToTransData_Recursive( +static int SeqToTransData_build( TransInfo *t, ListBase *seqbase, TransData *td, TransData2D *td2d, TransDataSeq *tdsq) { Sequence *seq; - int recursive, count, flag; + int count, flag; int tot = 0; for (seq = seqbase->first; seq; seq = seq->next) { - SeqTransInfo(t, seq, &recursive, &count, &flag); - - /* add children first so recalculating metastrips does nested strips first */ - if (recursive) { - int tot_children = SeqToTransData_Recursive(t, &seq->seqbase, td, td2d, tdsq); - - td = td + tot_children; - td2d = td2d + tot_children; - tdsq = tdsq + tot_children; - - tot += tot_children; - } + SeqTransInfo(t, seq, &count, &flag); /* use 'flag' which is derived from seq->flag but modified for special cases */ if (flag & SELECT) { @@ -297,13 +255,13 @@ static int SeqToTransData_Recursive( static void SeqTransDataBounds(TransInfo *t, ListBase *seqbase, TransSeq *ts) { Sequence *seq; - int recursive, count, flag; + int count, flag; int max = INT32_MIN, min = INT32_MAX; for (seq = seqbase->first; seq; seq = seq->next) { /* just to get the flag since there are corner cases where this isn't totally obvious */ - SeqTransInfo(t, seq, &recursive, &count, &flag); + SeqTransInfo(t, seq, &count, &flag); /* use 'flag' which is derived from seq->flag but modified for special cases */ if (flag & SELECT) { @@ -330,188 +288,199 @@ static void SeqTransDataBounds(TransInfo *t, ListBase *seqbase, TransSeq *ts) } } -static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data) +static void free_transform_custom_data(TransCustomData *custom_data) { - Editing *ed = SEQ_editing_get(t->scene, false); + if ((custom_data->data != NULL) && custom_data->use_free) { + TransSeq *ts = custom_data->data; + MEM_freeN(ts->tdseq); + MEM_freeN(custom_data->data); + custom_data->data = NULL; + } +} + +/* Canceled, need to update the strips display. */ +static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + SEQ_time_update_sequence_bounds(t->scene, seq); + } +} - if (ed != NULL) { +static bool seq_transform_check_overlap(SeqCollection *transformed_strips) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + if (seq->flag & SEQ_OVERLAP) { + return true; + } + } + return false; +} - ListBase *seqbasep = ed->seqbasep; - TransData *td = tc->data; - int a; +static SeqCollection *extract_standalone_strips(SeqCollection *transformed_strips) +{ + SeqCollection *collection = SEQ_collection_create(); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + if ((seq->type & SEQ_TYPE_EFFECT) == 0 || seq->seq1 == NULL) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} - /* prevent updating the same seq twice - * if the transdata order is changed this will mess up - * but so will TransDataSeq */ - Sequence *seq_prev = NULL; +/* Query strips positioned after left edge of transformed strips boundbox. */ +static SeqCollection *query_right_side_strips(ListBase *seqbase, SeqCollection *transformed_strips) +{ + int minframe = MAXFRAME; + { Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + minframe = min_ii(minframe, seq->startdisp); + } + } - if (!(t->state == TRANS_CANCEL)) { + SeqCollection *collection = SEQ_collection_create(); + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if ((seq->flag & SELECT) == 0 && seq->startdisp >= minframe) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} -#if 0 /* Default 2.4 behavior. */ +static void seq_transform_update_effects(TransInfo *t, SeqCollection *collection) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + if ((seq->type & SEQ_TYPE_EFFECT) && (seq->seq1 || seq->seq2 || seq->seq3)) { + SEQ_time_update_sequence(t->scene, seq); + } + } +} - /* flush to 2d vector from internally used 3d vector */ - for (a = 0; a < t->total; a++, td++) { - if ((seq != seq_prev) && (seq->depth == 0) && (seq->flag & SEQ_OVERLAP)) { - seq = ((TransDataSeq *)td->extra)->seq; - SEQ_transform_seqbase_shuffle(seqbasep, seq, t->scene); - } +/* Check if effect strips with input are transformed. */ +static bool seq_transform_check_strip_effects(SeqCollection *transformed_strips) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + if ((seq->type & SEQ_TYPE_EFFECT) && (seq->seq1 || seq->seq2 || seq->seq3)) { + return true; + } + } + return false; +} - seq_prev = seq; - } +/* Offset all strips positioned after left edge of transformed strips boundbox by amount equal + * to overlap of transformed strips. */ +static void seq_transform_handle_expand_to_fit(TransInfo *t, SeqCollection *transformed_strips) +{ + Editing *ed = SEQ_editing_get(t->scene, false); + ListBase *seqbasep = SEQ_active_seqbase_get(ed); + ListBase *markers = &t->scene->markers; + const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & + SEQ_MARKER_TRANS) != 0; -#else /* durian hack */ - { - int overlap = 0; + SeqCollection *right_side_strips = query_right_side_strips(seqbasep, transformed_strips); - for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { - seq = ((TransDataSeq *)td->extra)->seq; - if ((seq != seq_prev) && (seq->depth == 0) && (seq->flag & SEQ_OVERLAP)) { - overlap = 1; - break; - } - } + /* Temporarily move right side strips beyond timeline boundary. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, right_side_strips) { + seq->machine += MAXSEQ * 2; + } - if (overlap) { - const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & - SEQ_MARKER_TRANS) != 0; - ListBase *markers = &t->scene->markers; + /* Shuffle transformed standalone strips. This is because transformed strips can overlap with + * strips on left side. */ + SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); + SEQ_transform_seqbase_shuffle_time( + standalone_strips, seqbasep, t->scene, markers, use_sync_markers); + SEQ_collection_free(standalone_strips); - bool has_effect_root = false, has_effect_any = false; - for (seq = seqbasep->first; seq; seq = seq->next) { - seq->tmp = NULL; - } + /* Move temporarily moved strips back to their original place and tag for shuffling. */ + SEQ_ITERATOR_FOREACH (seq, right_side_strips) { + seq->machine -= MAXSEQ * 2; + } + /* Shuffle again to displace strips on right side. Final effect shuffling is done in + * seq_transform_handle_overlap. */ + SEQ_transform_seqbase_shuffle_time( + right_side_strips, seqbasep, t->scene, markers, use_sync_markers); + seq_transform_update_effects(t, right_side_strips); + SEQ_collection_free(right_side_strips); +} - td = tc->data; - for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { - seq = ((TransDataSeq *)td->extra)->seq; - if ((seq != seq_prev)) { - /* check effects strips, we cant change their time */ - if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) { - has_effect_any = true; - if (seq->depth == 0) { - has_effect_root = true; - } - } - else { - /* Tag seq with a non zero value, used by - * SEQ_transform_seqbase_shuffle_time to identify the ones to shuffle */ - if (seq->depth == 0) { - seq->tmp = (void *)1; - } - } - } - } +static void seq_transform_handle_overlap(TransInfo *t, SeqCollection *transformed_strips) +{ + Editing *ed = SEQ_editing_get(t->scene, false); + ListBase *seqbasep = SEQ_active_seqbase_get(ed); - if (t->flag & T_ALT_TRANSFORM) { - int minframe = MAXFRAME; - td = tc->data; - for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { - seq = ((TransDataSeq *)td->extra)->seq; - if ((seq != seq_prev) && (seq->depth == 0)) { - minframe = min_ii(minframe, seq->startdisp); - } - } - - for (seq = seqbasep->first; seq; seq = seq->next) { - if (!(seq->flag & SELECT)) { - if (seq->startdisp >= minframe) { - seq->machine += MAXSEQ * 2; - } - } - } - - SEQ_transform_seqbase_shuffle_time(seqbasep, t->scene, markers, use_sync_markers); - - for (seq = seqbasep->first; seq; seq = seq->next) { - if (seq->machine >= MAXSEQ * 2) { - seq->machine -= MAXSEQ * 2; - seq->tmp = (void *)1; - } - else { - seq->tmp = NULL; - } - } - - SEQ_transform_seqbase_shuffle_time(seqbasep, t->scene, markers, use_sync_markers); - } - else { - SEQ_transform_seqbase_shuffle_time(seqbasep, t->scene, markers, use_sync_markers); - } + if (t->flag & T_ALT_TRANSFORM) { + seq_transform_handle_expand_to_fit(t, transformed_strips); + } + else { + ListBase *markers = &t->scene->markers; + const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & + SEQ_MARKER_TRANS) != 0; + /* Shuffle non strips with no effects attached. */ + SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); + SEQ_transform_seqbase_shuffle_time( + standalone_strips, seqbasep, t->scene, markers, use_sync_markers); + SEQ_collection_free(standalone_strips); + } - if (has_effect_any) { - /* update effects strips based on strips just moved in time */ - td = tc->data; - for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { - seq = ((TransDataSeq *)td->extra)->seq; - if ((seq != seq_prev)) { - if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) { - SEQ_time_update_sequence(t->scene, seq); - } - } - } - } + if (seq_transform_check_strip_effects(transformed_strips)) { + /* Update effect strips based on strips just moved in time. */ + seq_transform_update_effects(t, transformed_strips); - if (has_effect_root) { - /* now if any effects _still_ overlap, we need to move them up */ - td = tc->data; - for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { - seq = ((TransDataSeq *)td->extra)->seq; - if ((seq != seq_prev) && (seq->depth == 0)) { - if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) { - if (SEQ_transform_test_overlap(seqbasep, seq)) { - SEQ_transform_seqbase_shuffle(seqbasep, seq, t->scene); - } - } - } - } - /* done with effects */ - } + /* If any effects still overlap, we need to move them up. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) { + if (SEQ_transform_test_overlap(seqbasep, seq)) { + SEQ_transform_seqbase_shuffle(seqbasep, seq, t->scene); } } -#endif + } + } +} - for (seq = seqbasep->first; seq; seq = seq->next) { - /* We might want to build a list of effects that need to be updated during transform */ - if (seq->type & SEQ_TYPE_EFFECT) { - if (seq->seq1 && seq->seq1->flag & SELECT) { - SEQ_time_update_sequence(t->scene, seq); - } - else if (seq->seq2 && seq->seq2->flag & SELECT) { - SEQ_time_update_sequence(t->scene, seq); - } - else if (seq->seq3 && seq->seq3->flag & SELECT) { - SEQ_time_update_sequence(t->scene, seq); - } - } - } +static SeqCollection *seq_transform_collection_from_transdata(TransDataContainer *tc) +{ + SeqCollection *collection = SEQ_collection_create(); + TransData *td = tc->data; + for (int a = 0; a < tc->data_len; a++, td++) { + Sequence *seq = ((TransDataSeq *)td->extra)->seq; + SEQ_collection_append_strip(seq, collection); + } + return collection; +} - SEQ_sort(seqbasep); - } - else { - /* Canceled, need to update the strips display */ - for (a = 0; a < tc->data_len; a++, td++) { - seq = ((TransDataSeq *)td->extra)->seq; - if ((seq != seq_prev) && (seq->depth == 0)) { - if (seq->flag & SEQ_OVERLAP) { - SEQ_transform_seqbase_shuffle(seqbasep, seq, t->scene); - } +static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data) +{ + Editing *ed = SEQ_editing_get(t->scene, false); + if (ed == NULL) { + free_transform_custom_data(custom_data); + return; + } - SEQ_time_update_sequence_bounds(t->scene, seq); - } - seq_prev = seq; - } - } + SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc); + + if ((t->state == TRANS_CANCEL)) { + seq_transform_cancel(t, transformed_strips); + free_transform_custom_data(custom_data); + return; } - if ((custom_data->data != NULL) && custom_data->use_free) { - TransSeq *ts = custom_data->data; - MEM_freeN(ts->tdseq); - MEM_freeN(custom_data->data); - custom_data->data = NULL; + if (seq_transform_check_overlap(transformed_strips)) { + seq_transform_handle_overlap(t, transformed_strips); } + seq_transform_update_effects(t, transformed_strips); + SEQ_collection_free(transformed_strips); + + SEQ_sort(ed->seqbasep); DEG_id_tag_update(&t->scene->id, ID_RECALC_SEQUENCER_STRIPS); + free_transform_custom_data(custom_data); } void createTransSeqData(TransInfo *t) @@ -557,7 +526,7 @@ void createTransSeqData(TransInfo *t) } #endif - count = SeqTransCount(t, ed->seqbasep, 0); + count = SeqTransCount(t, ed->seqbasep); /* allocate memory for data */ tc->data_len = count; @@ -574,7 +543,7 @@ void createTransSeqData(TransInfo *t) ts->tdseq = tdsq = MEM_callocN(tc->data_len * sizeof(TransDataSeq), "TransSeq TransDataSeq"); /* loop 2: build transdata array */ - SeqToTransData_Recursive(t, ed->seqbasep, td, td2d, tdsq); + SeqToTransData_build(t, ed->seqbasep, td, td2d, tdsq); SeqTransDataBounds(t, ed->seqbasep, ts); if (t->flag & T_MODAL) { @@ -607,15 +576,10 @@ void createTransSeqData(TransInfo *t) BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int sel_flag) { - if (seq->depth == 0) { - /* Calculate this strip and all nested strips. - * Children are ALWAYS transformed first so we don't need to do this in another loop. - */ - SEQ_time_update_sequence(sce, seq); - } - else { - SEQ_time_update_sequence_bounds(sce, seq); - } + /* Calculate this strip and all nested strips. + * Children are ALWAYS transformed first so we don't need to do this in another loop. + */ + SEQ_time_update_sequence(sce, seq); if (sel_flag == SELECT) { SEQ_offset_animdata(sce, seq, seq->start - old_start); @@ -635,102 +599,49 @@ static void flushTransSeq(TransInfo *t) TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); - /* prevent updating the same seq twice - * if the transdata order is changed this will mess up - * but so will TransDataSeq */ - Sequence *seq_prev = NULL; - int old_start_prev = 0, sel_flag_prev = 0; - /* flush to 2d vector from internally used 3d vector */ for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { - int old_start; tdsq = (TransDataSeq *)td->extra; seq = tdsq->seq; - old_start = seq->start; new_frame = round_fl_to_int(td2d->loc[0]); switch (tdsq->sel_flag) { case SELECT: - if ((seq->depth != 0 || SEQ_transform_sequence_can_be_translated(seq))) { - /* for meta's, their children move */ - seq->start = new_frame - tdsq->start_offset; - } - if (seq->depth == 0) { - seq->machine = round_fl_to_int(td2d->loc[1]); - CLAMP(seq->machine, 1, MAXSEQ); + if (SEQ_transform_sequence_can_be_translated(seq)) { + const int offset = new_frame - tdsq->start_offset - seq->start; + SEQ_transform_translate_sequence(t->scene, seq, offset); } + seq->machine = round_fl_to_int(td2d->loc[1]); + CLAMP(seq->machine, 1, MAXSEQ); break; + case SEQ_LEFTSEL: /* no vertical transform */ SEQ_transform_set_left_handle_frame(seq, new_frame); SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); - - /* todo - move this into aftertrans update? - old seq tx needed it anyway */ SEQ_transform_fix_single_image_seq_offsets(seq); + SEQ_time_update_sequence(t->scene, seq); break; case SEQ_RIGHTSEL: /* no vertical transform */ SEQ_transform_set_right_handle_frame(seq, new_frame); SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); - - /* todo - move this into aftertrans update? - old seq tx needed it anyway */ SEQ_transform_fix_single_image_seq_offsets(seq); + SEQ_time_update_sequence(t->scene, seq); break; } - - /* Update *previous* seq! Else, we would update a seq after its first transform, - * and if it has more than one (like e.g. SEQ_LEFTSEL and SEQ_RIGHTSEL), - * the others are not updated! See T38469. - */ - if (seq != seq_prev) { - if (seq_prev) { - trans_update_seq(t->scene, seq_prev, old_start_prev, sel_flag_prev); - } - - seq_prev = seq; - old_start_prev = old_start; - sel_flag_prev = tdsq->sel_flag; - } - else { - /* We want to accumulate *all* sel_flags for this seq! */ - sel_flag_prev |= tdsq->sel_flag; - } } - /* Don't forget to update the last seq! */ - if (seq_prev) { - trans_update_seq(t->scene, seq_prev, old_start_prev, sel_flag_prev); - } - - /* originally TFM_TIME_EXTEND, transform changes */ + /* Update all effects. */ if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) { - /* Special annoying case here, need to calc meta-strips with TFM_TIME_EXTEND only */ - - /* calc all meta's then effects T27953. */ - for (seq = seqbasep->first; seq; seq = seq->next) { - if (seq->type == SEQ_TYPE_META && seq->flag & SELECT) { - SEQ_time_update_sequence(t->scene, seq); - } - } for (seq = seqbasep->first; seq; seq = seq->next) { if (seq->seq1 || seq->seq2 || seq->seq3) { SEQ_time_update_sequence(t->scene, seq); } } - - /* update effects inside meta's */ - for (a = 0, seq_prev = NULL, td = tc->data, td2d = tc->data_2d; a < tc->data_len; - a++, td++, td2d++, seq_prev = seq) { - tdsq = (TransDataSeq *)td->extra; - seq = tdsq->seq; - if ((seq != seq_prev) && (seq->depth != 0)) { - if (seq->seq1 || seq->seq2 || seq->seq3) { - SEQ_time_update_sequence(t->scene, seq); - } - } - } } /* need to do the overlap check in a new loop otherwise adjacent strips * will not be updated and we'll get false positives */ + Sequence *seq_prev = NULL; seq_prev = NULL; for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { @@ -738,12 +649,10 @@ static void flushTransSeq(TransInfo *t) seq = tdsq->seq; if (seq != seq_prev) { - if (seq->depth == 0) { - /* test overlap, displays red outline */ - seq->flag &= ~SEQ_OVERLAP; - if (SEQ_transform_test_overlap(seqbasep, seq)) { - seq->flag |= SEQ_OVERLAP; - } + /* test overlap, displays red outline */ + seq->flag &= ~SEQ_OVERLAP; + if (SEQ_transform_test_overlap(seqbasep, seq)) { + seq->flag |= SEQ_OVERLAP; } } seq_prev = seq; @@ -787,7 +696,7 @@ void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t) return; } /* freeSeqData in transform_conversions.c does this - * keep here so the else at the end wont run... */ + * keep here so the else at the end won't run... */ SpaceSeq *sseq = (SpaceSeq *)t->area->spacedata.first; diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index ed6c3eb0255..ca4ed01c0f6 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -367,7 +367,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup) } } - /* TODO: skip calculating axis which wont be used (above). */ + /* TODO: skip calculating axis which won't be used (above). */ switch (axis_type) { case EXTRUDE_AXIS_NORMAL: for (int i = 0; i < 3; i++) { diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 350be247014..6a946994e06 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -734,9 +734,25 @@ void ElementRotation_ex(TransInfo *t, /* can be called for texture space translate for example, then opt out */ if (td->ext->quat) { mul_m3_series(fmat, td->smtx, mat, td->mtx); + + if (!is_zero_v3(td->ext->dquat)) { + /* Correct for delta quat */ + float tmp_mat[3][3]; + quat_to_mat3(tmp_mat, td->ext->dquat); + mul_m3_m3m3(fmat, fmat, tmp_mat); + } + mat3_to_quat(quat, fmat); /* Actual transform */ + if (!is_zero_v4(td->ext->dquat)) { + /* Correct back for delta quat. */ + float idquat[4]; + invert_qt_qt_normalized(idquat, td->ext->dquat); + mul_qt_qtqt(quat, idquat, quat); + } + mul_qt_qtqt(td->ext->quat, quat, td->ext->iquat); + /* this function works on end result */ protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat); } @@ -761,21 +777,28 @@ void ElementRotation_ex(TransInfo *t, td->ext->irotAngle); } else { + /* Calculate the total rotation in eulers. */ float obmat[3][3]; mul_m3_m3m3(totmat, mat, td->mtx); mul_m3_m3m3(smat, td->smtx, totmat); - /* Calculate the total rotation in eulers. */ - add_v3_v3v3(eul, td->ext->irot, td->ext->drot); /* correct for delta rot */ + if (!is_zero_v3(td->ext->drot)) { + /* Correct for delta rot */ + add_eul_euleul(eul, td->ext->irot, td->ext->drot, td->ext->rotOrder); + } + else { + copy_v3_v3(eul, td->ext->irot); + } + eulO_to_mat3(obmat, eul, td->ext->rotOrder); - /* mat = transform, obmat = object rotation */ mul_m3_m3m3(fmat, smat, obmat); - mat3_to_compatible_eulO(eul, td->ext->rot, td->ext->rotOrder, fmat); - /* correct back for delta rot */ - sub_v3_v3v3(eul, eul, td->ext->drot); + if (!is_zero_v3(td->ext->drot)) { + /* Correct back for delta rot. */ + sub_eul_euleul(eul, eul, td->ext->drot, td->ext->rotOrder); + } /* and apply */ protectedRotateBits(td->protectflag, eul, td->ext->irot); diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index 857ee37f0ad..cfbd6030788 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -89,7 +89,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) /* apply shrink/fatten */ FOREACH_TRANS_DATA_CONTAINER (t, tc) { - TransData *td = tc->data; + TransData *td; for (td = tc->data, i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_SKIP) { continue; diff --git a/source/blender/editors/transform/transform_mode_push_pull.c b/source/blender/editors/transform/transform_mode_push_pull.c index 8a92978f33f..b08e479a3d0 100644 --- a/source/blender/editors/transform/transform_mode_push_pull.c +++ b/source/blender/editors/transform/transform_mode_push_pull.c @@ -77,6 +77,8 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) t->con.applyRot(t, NULL, NULL, axis_global, NULL); } + const bool is_data_space = (t->options & CTX_POSE_BONE) != 0; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData *td = tc->data; for (i = 0; i < tc->data_len; i++, td++) { @@ -101,6 +103,9 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) } } normalize_v3_length(vec, distance * td->factor); + if (is_data_space) { + mul_m3_v3(td->smtx, vec); + } add_v3_v3v3(td->loc, td->iloc, vec); } diff --git a/source/blender/editors/transform/transform_mode_tosphere.c b/source/blender/editors/transform/transform_mode_tosphere.c index 15906f2c90c..9bca9b2e9e6 100644 --- a/source/blender/editors/transform/transform_mode_tosphere.c +++ b/source/blender/editors/transform/transform_mode_tosphere.c @@ -55,8 +55,10 @@ static void to_sphere_radius_update(TransInfo *t) { struct ToSphereInfo *data = t->custom.mode.data; float radius = 0.0f; + float vec[3]; const bool is_local_center = transdata_check_local_center(t, t->around); + const bool is_data_space = (t->options & CTX_POSE_BONE) != 0; if (t->flag & T_PROP_EDIT_ALL) { int factor_accum = 0.0f; @@ -67,7 +69,15 @@ static void to_sphere_radius_update(TransInfo *t) continue; } const float *center = is_local_center ? td->center : tc->center_local; - radius += td->factor * len_v3v3(center, td->iloc); + if (is_data_space) { + copy_v3_v3(vec, td->center); + } + else { + copy_v3_v3(vec, td->iloc); + } + + sub_v3_v3(vec, center); + radius += td->factor * len_v3(vec); factor_accum += td->factor; } } @@ -80,7 +90,15 @@ static void to_sphere_radius_update(TransInfo *t) TransData *td = tc->data; for (int i = 0; i < tc->data_len; i++, td++) { const float *center = is_local_center ? td->center : tc->center_local; - radius += len_v3v3(center, td->iloc); + if (is_data_space) { + copy_v3_v3(vec, td->center); + } + else { + copy_v3_v3(vec, td->iloc); + } + + sub_v3_v3(vec, center); + radius += len_v3(vec); } } radius /= (float)t->data_len_all; @@ -99,6 +117,7 @@ static void to_sphere_radius_update(TransInfo *t) static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) { const bool is_local_center = transdata_check_local_center(t, t->around); + const bool is_data_space = (t->options & CTX_POSE_BONE) != 0; float vec[3]; float ratio, radius; @@ -142,16 +161,26 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) } const float *center = is_local_center ? td->center : tc->center_local; + if (is_data_space) { + copy_v3_v3(vec, td->center); + } + else { + copy_v3_v3(vec, td->iloc); + } - sub_v3_v3v3(vec, td->iloc, center); - + sub_v3_v3(vec, center); radius = normalize_v3(vec); - tratio = ratio * td->factor; - mul_v3_fl(vec, radius * (1.0f - tratio) + data->radius * tratio); + add_v3_v3(vec, center); + + if (is_data_space) { + sub_v3_v3(vec, td->center); + mul_m3_v3(td->smtx, vec); + add_v3_v3(vec, td->iloc); + } - add_v3_v3v3(td->loc, center, vec); + copy_v3_v3(td->loc, vec); } } diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 3088f6a7776..0e734b3b74b 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -52,12 +52,26 @@ /** \name Transform (Translation) * \{ */ +static void translate_dist_to_str(char *r_str, + const int len_max, + const float val, + const UnitSettings *unit) +{ + if (unit) { + BKE_unit_value_as_string( + r_str, len_max, val * unit->scale_length, 4, B_UNIT_LENGTH, unit, false); + } + else { + /* Check range to prevent string buffer overflow. */ + BLI_snprintf(r_str, len_max, IN_RANGE_INCL(val, -1e10f, 1e10f) ? "%.4f" : "%.4e", val); + } +} + static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) { size_t ofs = 0; - char tvec[NUM_STR_REP_LEN * 3]; - char distvec[NUM_STR_REP_LEN]; - char autoik[NUM_STR_REP_LEN]; + char dvec_str[3][NUM_STR_REP_LEN]; + char dist_str[NUM_STR_REP_LEN]; float dist; UnitSettings *unit = NULL; @@ -66,7 +80,7 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ } if (hasNumInput(&t->num)) { - outputNumInput(&(t->num), tvec, &t->scene->unit); + outputNumInput(&(t->num), dvec_str[0], &t->scene->unit); dist = len_v3(t->num.val); } else { @@ -94,84 +108,37 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ dist = len_v3(dvec); - if (unit) { - for (int i = 0; i < 3; i++) { - BKE_unit_value_as_string(&tvec[NUM_STR_REP_LEN * i], - NUM_STR_REP_LEN, - dvec[i] * unit->scale_length, - 4, - B_UNIT_LENGTH, - unit, - true); - } + for (int i = 0; i < 3; i++) { + translate_dist_to_str(dvec_str[i], sizeof(dvec_str[i]), dvec[i], unit); } - else { - BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", dvec[0]); - BLI_snprintf(&tvec[NUM_STR_REP_LEN], NUM_STR_REP_LEN, "%.4f", dvec[1]); - BLI_snprintf(&tvec[NUM_STR_REP_LEN * 2], NUM_STR_REP_LEN, "%.4f", dvec[2]); - } - } - - if (unit) { - BKE_unit_value_as_string( - distvec, sizeof(distvec), dist * unit->scale_length, 4, B_UNIT_LENGTH, unit, false); - } - else if (dist > 1e10f || dist < -1e10f) { - /* prevent string buffer overflow */ - BLI_snprintf(distvec, NUM_STR_REP_LEN, "%.4e", dist); - } - else { - BLI_snprintf(distvec, NUM_STR_REP_LEN, "%.4f", dist); } - if (t->flag & T_AUTOIK) { - short chainlen = t->settings->autoik_chainlen; - - if (chainlen) { - BLI_snprintf(autoik, NUM_STR_REP_LEN, TIP_("AutoIK-Len: %d"), chainlen); - } - else { - autoik[0] = '\0'; - } - } - else { - autoik[0] = '\0'; - } + translate_dist_to_str(dist_str, sizeof(dist_str), dist, unit); if (t->con.mode & CON_APPLY) { switch (t->num.idx_max) { case 0: - ofs += BLI_snprintf_rlen(str + ofs, - UI_MAX_DRAW_STR - ofs, - "D: %s (%s)%s %s %s", - &tvec[0], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen( + str + ofs, UI_MAX_DRAW_STR - ofs, "D: %s (%s)%s", dvec_str[0], dist_str, t->con.text); break; case 1: ofs += BLI_snprintf_rlen(str + ofs, UI_MAX_DRAW_STR - ofs, - "D: %s D: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - distvec, - t->con.text, - t->proptext, - autoik); + "D: %s D: %s (%s)%s", + dvec_str[0], + dvec_str[1], + dist_str, + t->con.text); break; case 2: ofs += BLI_snprintf_rlen(str + ofs, UI_MAX_DRAW_STR - ofs, - "D: %s D: %s D: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - distvec, - t->con.text, - t->proptext, - autoik); + "D: %s D: %s D: %s (%s)%s", + dvec_str[0], + dvec_str[1], + dvec_str[2], + dist_str, + t->con.text); break; } } @@ -179,30 +146,42 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ if (t->flag & T_2D_EDIT) { ofs += BLI_snprintf_rlen(str + ofs, UI_MAX_DRAW_STR - ofs, - "Dx: %s Dy: %s (%s)%s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - distvec, - t->con.text, - t->proptext); + "Dx: %s Dy: %s (%s)%s", + dvec_str[0], + dvec_str[1], + dist_str, + t->con.text); } else { ofs += BLI_snprintf_rlen(str + ofs, UI_MAX_DRAW_STR - ofs, - "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - distvec, - t->con.text, - t->proptext, - autoik); + "Dx: %s Dy: %s Dz: %s (%s)%s", + dvec_str[0], + dvec_str[1], + dvec_str[2], + dist_str, + t->con.text); } } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf_rlen( - str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + char prop_str[NUM_STR_REP_LEN]; + translate_dist_to_str(prop_str, sizeof(prop_str), t->prop_size, unit); + + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + " %s %s: %s", + TIP_("Proportional Size"), + t->proptext, + prop_str); + } + + if (t->flag & T_AUTOIK) { + short chainlen = t->settings->autoik_chainlen; + if (chainlen) { + ofs += BLI_strncpy_rlen(str + ofs, " ", UI_MAX_DRAW_STR - ofs); + ofs += BLI_snprintf_rlen(str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("AutoIK-Len: %d"), chainlen); + } } if (t->spacetype == SPACE_NODE) { diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 9b5f15a2574..2c424d8ace3 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -519,10 +519,11 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* add temp handler */ WM_event_add_modal_handler(C, op); - op->flag |= OP_IS_MODAL_GRAB_CURSOR; /* XXX maybe we want this with the gizmo only? */ - /* Use when modal input has some transformation to begin with. */ TransInfo *t = op->customdata; + if ((t->flag & T_NO_CURSOR_WRAP) == 0) { + op->flag |= OP_IS_MODAL_GRAB_CURSOR; /* XXX maybe we want this with the gizmo only? */ + } if (UNLIKELY(!is_zero_v4(t->values_modal_offset))) { transformApply(C, t); } @@ -538,7 +539,7 @@ static bool transform_poll_property(const bContext *UNUSED(C), /* Orientation/Constraints. */ { - /* Hide orientation axis if no constraints are set, since it wont be used. */ + /* Hide orientation axis if no constraints are set, since it won't be used. */ PropertyRNA *prop_con = RNA_struct_find_property(op->ptr, "orient_type"); if (!ELEM(prop_con, NULL, prop)) { if (STRPREFIX(prop_id, "constraint")) { diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 193954fffb6..bebef049718 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -1072,7 +1072,7 @@ static void TargetSnapClosest(TransInfo *t) if (t->options & CTX_OBJECT) { int i; FOREACH_TRANS_DATA_CONTAINER (t, tc) { - TransData *td = tc->data; + TransData *td; for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) { const BoundBox *bb = NULL; diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 7811509ab40..df4e6f893ec 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -492,7 +492,7 @@ bool ED_undo_is_legacy_compatible_for_property(struct bContext *C, ID *id) } /** - * Ideally we wont access the stack directly, + * Ideally we won't access the stack directly, * this is needed for modes which handle undo themselves (bypassing #ED_undo_push). * * Using global isn't great, this just avoids doing inline, @@ -693,9 +693,9 @@ int ED_undo_operator_repeat(bContext *C, wmOperator *op) } if ((WM_operator_repeat_check(C, op)) && (WM_operator_poll(C, op->type)) && - /* note, undo/redo cant run if there are jobs active, + /* note, undo/redo can't run if there are jobs active, * check for screen jobs only so jobs like material/texture/world preview - * (which copy their data), wont stop redo, see T29579], + * (which copy their data), won't stop redo, see T29579], * * note, - WM_operator_check_ui_enabled() jobs test _must_ stay in sync with this */ (WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY) == 0)) { diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c index 9189adaf4d1..7c6ce56eab0 100644 --- a/source/blender/editors/undo/memfile_undo.c +++ b/source/blender/editors/undo/memfile_undo.c @@ -224,9 +224,21 @@ static void memfile_undosys_step_decode(struct bContext *C, /* Tag depsgraph to update data-block for changes that happened between the * current and the target state, see direct_link_id_restore_recalc(). */ - if (id->recalc) { + if (id->recalc != 0) { DEG_id_tag_update_ex(bmain, id, id->recalc); } + + bNodeTree *nodetree = ntreeFromID(id); + if (nodetree != NULL && nodetree->id.recalc != 0) { + DEG_id_tag_update_ex(bmain, &nodetree->id, nodetree->id.recalc); + } + if (GS(id->name) == ID_SCE) { + Scene *scene = (Scene *)id; + if (scene->master_collection != NULL && scene->master_collection->id.recalc != 0) { + DEG_id_tag_update_ex( + bmain, &scene->master_collection->id, scene->master_collection->id.recalc); + } + } } FOREACH_MAIN_ID_END; diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 708f04bf044..7b034af1438 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -632,7 +632,7 @@ static void uv_weld_align(bContext *C, eUVWeldAlign tool) } } else { - /* error - cant find an endpoint */ + /* error - can't find an endpoint */ } } @@ -1469,7 +1469,12 @@ static int uv_hide_exec(bContext *C, wmOperator *op) if (ts->uv_flag & UV_SYNC_SELECTION) { if (EDBM_mesh_hide(em, swap)) { - EDBM_update_generic(ob->data, true, false); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } continue; } @@ -1607,7 +1612,12 @@ static int uv_reveal_exec(bContext *C, wmOperator *op) /* call the mesh function if we are in mesh sync sel */ if (ts->uv_flag & UV_SYNC_SELECTION) { if (EDBM_mesh_reveal(em, select)) { - EDBM_update_generic(ob->data, true, false); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); } continue; } diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c index 016a054cf21..2613c5b23a0 100644 --- a/source/blender/editors/uvedit/uvedit_path.c +++ b/source/blender/editors/uvedit/uvedit_path.c @@ -552,7 +552,7 @@ static bool uv_shortest_path_pick_ex(const SpaceImage *sima, if (uv_selectmode & UV_SELECT_EDGE) { /* Special case as we don't use true edge selection, * flush the selection from the vertices. */ - BM_mesh_select_mode_flush_ex(em->bm, SCE_SELECT_VERTEX); + BM_mesh_select_mode_flush_ex(em->bm, SCE_SELECT_VERTEX, BM_SELECT_LEN_FLUSH_RECALC_ALL); } } ED_uvedit_select_sync_flush(scene->toolsettings, em, select); diff --git a/source/blender/editors/uvedit/uvedit_rip.c b/source/blender/editors/uvedit/uvedit_rip.c index e1b9a287457..f8b53879ac4 100644 --- a/source/blender/editors/uvedit/uvedit_rip.c +++ b/source/blender/editors/uvedit/uvedit_rip.c @@ -572,7 +572,7 @@ static UVRipPairs *uv_rip_pairs_from_loop(BMLoop *l_init, rip->loops = BLI_gset_ptr_new(__func__); /* We can rely on this stack being small, as we're walking down two sides of an edge loop, - * so the stack wont be much larger than the total number of fans at any one vertex. */ + * so the stack won't be much larger than the total number of fans at any one vertex. */ BLI_SMALLSTACK_DECLARE(stack, BMLoop *); /* Needed for cases when we walk onto loops which already have a side assigned, diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 39950f9d0c7..0a7cd579a0a 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1296,7 +1296,7 @@ static int uv_select_edgering( l_step = uvedit_loop_find_other_radial_loop_with_visible_face( scene, l_step_opposite, cd_loop_uv_offset); if (l_step == NULL) { - /* Ensure we touch the opposite edge if we cant walk over it. */ + /* Ensure we touch the opposite edge if we can't walk over it. */ l_step = l_step_opposite; } } diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index d530d10b3c8..5fdc7c3f337 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -66,7 +66,7 @@ class GVArray { bool is_empty() const { - return size_; + return size_ == 0; } /* Copies the value at the given index into the provided storage. The `r_value` pointer is diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h b/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h index 685f0cb36cb..7d75ed5804e 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h @@ -29,3 +29,7 @@ void OBJECT_OT_lineart_clear(struct wmOperatorType *ot); void OBJECT_OT_lineart_clear_all(struct wmOperatorType *ot); void WM_operatortypes_lineart(void); + +struct LineartCache; + +void MOD_lineart_clear_cache(struct LineartCache **lc); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index cc79810d2a2..f0aae7e4498 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -38,6 +38,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "MOD_gpencil_lineart.h" #include "lineart/MOD_lineart.h" #include "BKE_collection.h" @@ -88,7 +89,7 @@ static void generate_strokes_actual( } MOD_lineart_gpencil_generate( - lmd->render_buffer, + lmd->cache, depsgraph, ob, gpl, @@ -156,11 +157,31 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec return; } - MOD_lineart_compute_feature_lines(depsgraph, lmd); + LineartCache *local_lc = gpd->runtime.lineart_cache; + if (!gpd->runtime.lineart_cache) { + MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_destroy_render_data(lmd); + } + else { + if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc); + MOD_lineart_destroy_render_data(lmd); + } + MOD_lineart_chain_clear_picked_flag(local_lc); + lmd->cache = local_lc; + } generate_strokes_actual(md, depsgraph, ob, gpl, gpf); - MOD_lineart_destroy_render_data(lmd); + if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + /* Clear local cache. */ + if (local_lc != gpd->runtime.lineart_cache) { + MOD_lineart_clear_cache(&local_lc); + } + /* Restore the original cache pointer so the modifiers below still have access to the "global" + * cache. */ + lmd->cache = gpd->runtime.lineart_cache; + } WM_main_add_notifier(NA_EDITED | NC_GPENCIL, NULL); } @@ -182,11 +203,12 @@ static void bakeModifier(Main *UNUSED(bmain), return; } - MOD_lineart_compute_feature_lines(depsgraph, lmd); + if (!gpd->runtime.lineart_cache) { + MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_destroy_render_data(lmd); + } generate_strokes_actual(md, depsgraph, ob, gpl, gpf); - - MOD_lineart_destroy_render_data(lmd); } static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) @@ -198,6 +220,9 @@ static void add_this_collection(Collection *c, const ModifierUpdateDepsgraphContext *ctx, const int mode) { + if (!c) { + return; + } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (c, ob, mode) { if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) { @@ -255,10 +280,16 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) const int source_type = RNA_enum_get(ptr, "source_type"); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); + if (!BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data)) { + uiItemR(layout, ptr, "use_cache", 0, NULL, ICON_NONE); + } + uiItemR(layout, ptr, "source_type", 0, NULL, ICON_NONE); if (source_type == LRT_SOURCE_OBJECT) { @@ -277,12 +308,17 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_material", 0, IFACE_("Material Borders"), ICON_NONE); uiItemR(col, ptr, "use_edge_mark", 0, IFACE_("Edge Marks"), ICON_NONE); uiItemR(col, ptr, "use_intersection", 0, IFACE_("Intersections"), ICON_NONE); - uiItemR(col, ptr, "use_crease", 0, IFACE_("Crease"), ICON_NONE); - uiLayout *sub = uiLayoutRow(col, true); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_crease")); - uiLayoutSetPropSep(sub, true); - uiItemR(sub, ptr, "crease_threshold", UI_ITEM_R_SLIDER, " ", ICON_NONE); + uiLayout *sub = uiLayoutRowWithHeading(col, false, IFACE_("Crease")); + uiItemR(sub, ptr, "use_crease", 0, "", ICON_NONE); + uiLayout *entry = uiLayoutRow(sub, false); + uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_crease") || is_first); + if (use_cache && !is_first) { + uiItemL(entry, IFACE_("Angle Cached"), ICON_INFO); + } + else { + uiItemR(entry, ptr, "crease_threshold", UI_ITEM_R_SLIDER, " ", ICON_NONE); + } uiItemPointerR(layout, ptr, "target_layer", &obj_data_ptr, "layers", NULL, ICON_GREASEPENCIL); @@ -305,14 +341,35 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) NULL, material_valid ? ICON_SHADING_TEXTURE : ICON_ERROR); - uiItemR(layout, ptr, "use_remove_doubles", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); - uiItemR(layout, ptr, "use_object_instances", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); - gpencil_modifier_panel_end(layout, ptr); } +static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetEnabled(layout, !is_baked); + + if (use_cache && !is_first) { + uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + return; + } + + uiLayout *col = uiLayoutColumn(layout, true); + + uiItemR(col, ptr, "use_remove_doubles", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); + uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); +} + static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; @@ -392,15 +449,23 @@ static void transparency_panel_draw(const bContext *UNUSED(C), Panel *panel) static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) { - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); uiLayout *layout = panel->layout; const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); + if (use_cache && !is_first) { + uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + return; + } + uiLayout *col = uiLayoutColumnWithHeading(layout, true, IFACE_("Chain")); uiItemR(col, ptr, "use_fuzzy_intersections", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_fuzzy_all", 0, NULL, ICON_NONE); @@ -418,13 +483,21 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *layout = panel->layout; const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); + if (use_cache && !is_first) { + uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + return; + } + uiLayout *col = uiLayoutColumn(layout, true); uiLayout *row = uiLayoutRow(col, true); + uiItemR(row, ptr, "source_vertex_group", 0, IFACE_("Filter Source"), ICON_GROUP_VERTEX); uiItemR(row, ptr, "invert_source_vertex_group", UI_ITEM_R_TOGGLE, "", ICON_ARROW_LEFTRIGHT); @@ -471,6 +544,8 @@ static void panelRegister(ARegionType *region_type) region_type, eGpencilModifierType_Lineart, panel_draw); gpencil_modifier_subpanel_register( + region_type, "geometry", "Geometry Processing", NULL, options_panel_draw, panel_type); + gpencil_modifier_subpanel_register( region_type, "style", "Style", NULL, style_panel_draw, panel_type); PanelType *occlusion_panel = gpencil_modifier_subpanel_register( region_type, "occlusion", "Occlusion", NULL, occlusion_panel_draw, panel_type); diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 861085d3e16..b6175762bbe 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -250,6 +250,10 @@ typedef struct LineartRenderBuffer { ListBase wasted_cuts; SpinLock lock_cuts; + /* This is just a pointer to LineartCache::chain_data_pool, which acts as a cache for line + * chains. */ + LineartStaticMemPool *chain_data_pool; + /* Render status */ double view_vector[3]; @@ -309,6 +313,18 @@ typedef struct LineartRenderBuffer { } LineartRenderBuffer; +typedef struct LineartCache { + /** Separate memory pool for chain data, this goes to the cache, so when we free the main pool, + * chains will still be available. */ + LineartStaticMemPool chain_data_pool; + + /** A copy of rb->chains so we have that data available after rb has been destroyed. */ + ListBase chains; + + /** Cache only contains edge types specified in this variable. */ + char rb_edge_types; +} LineartCache; + #define DBL_TRIANGLE_LIM 1e-8 #define DBL_EDGE_LIM 1e-9 @@ -563,10 +579,11 @@ void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float thresh void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); int MOD_lineart_chain_count(const LineartEdgeChain *ec); -void MOD_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb); +void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, - struct LineartGpencilModifierData *lmd); + struct LineartGpencilModifierData *lmd, + LineartCache **cached_result); struct Scene; @@ -579,7 +596,7 @@ LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, doub struct bGPDlayer; struct bGPDframe; -void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb, +void MOD_lineart_gpencil_generate(LineartCache *cache, struct Depsgraph *depsgraph, struct Object *ob, struct bGPDlayer *gpl, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 23928b4ccda..f90fbaf4ccf 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -74,7 +74,7 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, static LineartEdgeChain *lineart_chain_create(LineartRenderBuffer *rb) { LineartEdgeChain *ec; - ec = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChain)); + ec = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChain)); BLI_addtail(&rb->chains, ec); @@ -119,7 +119,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, return old_rlci; } - eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem)); + eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); copy_v2_v2(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); @@ -149,7 +149,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb return ec->chain.first; } - eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem)); + eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); copy_v2_v2(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); @@ -198,7 +198,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) * so we assign it based on the first segment we found. */ ec->object_ref = e->object_ref; - LineartEdge *new_e = e; + LineartEdge *new_e; LineartVert *new_vt; float N[3] = {0}; @@ -889,12 +889,12 @@ int MOD_lineart_chain_count(const LineartEdgeChain *ec) return count; } -void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb) +void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) { - if (rb == NULL) { + if (lc == NULL) { return; } - LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &lc->chains) { ec->picked = 0; } } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 06d63c3ddab..3796e374db7 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -21,6 +21,7 @@ * \ingroup editors */ +#include "MOD_gpencil_lineart.h" #include "MOD_lineart.h" #include "BLI_linklist.h" @@ -42,6 +43,7 @@ #include "BKE_gpencil_modifier.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_pointcache.h" #include "BKE_scene.h" #include "DEG_depsgraph_query.h" #include "DNA_camera_types.h" @@ -107,6 +109,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e); +static LineartCache *lineart_init_cache(void); + static void lineart_discard_segment(LineartRenderBuffer *rb, LineartEdgeSegment *es) { BLI_spin_lock(&rb->lock_cuts); @@ -482,7 +486,7 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) rb->material.last = rb->material.first; rb->edge_mark.last = rb->edge_mark.first; - TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); for (i = 0; i < thread_count; i++) { rti[i].thread_id = i; @@ -1987,7 +1991,7 @@ static void lineart_main_load_geometries( } DEG_OBJECT_ITER_END; - TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); for (int i = 0; i < thread_count; i++) { olti[i].rb = rb; @@ -2771,13 +2775,13 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb) void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) { - LineartRenderBuffer *rb = lmd->render_buffer; + LineartRenderBuffer *rb = lmd->render_buffer_ptr; lineart_destroy_render_data(rb); if (rb) { MEM_freeN(rb); - lmd->render_buffer = NULL; + lmd->render_buffer_ptr = NULL; } if (G.debug_value == 4000) { @@ -2785,14 +2789,33 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) } } +static LineartCache *lineart_init_cache() +{ + LineartCache *lc = MEM_callocN(sizeof(LineartCache), "Lineart Cache"); + return lc; +} + +void MOD_lineart_clear_cache(struct LineartCache **lc) +{ + if (!(*lc)) { + return; + } + lineart_mem_destroy(&((*lc)->chain_data_pool)); + MEM_freeN(*lc); + (*lc) = NULL; +} + static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, - LineartGpencilModifierData *lmd) + LineartGpencilModifierData *lmd, + LineartCache *lc) { LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer"); - lmd->render_buffer = rb; + lmd->cache = lc; + lmd->render_buffer_ptr = rb; + lc->rb_edge_types = lmd->edge_types_override; - if (!scene || !scene->camera) { + if (!scene || !scene->camera || !lc) { return NULL; } Camera *c = scene->camera->data; @@ -2835,11 +2858,15 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, /* See lineart_edge_from_triangle() for how this option may impact performance. */ rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; - rb->use_contour = (lmd->edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; - rb->use_crease = (lmd->edge_types & LRT_EDGE_FLAG_CREASE) != 0; - rb->use_material = (lmd->edge_types & LRT_EDGE_FLAG_MATERIAL) != 0; - rb->use_edge_marks = (lmd->edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0; - rb->use_intersections = (lmd->edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0; + int16_t edge_types = lmd->edge_types_override; + + rb->use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; + rb->use_crease = (edge_types & LRT_EDGE_FLAG_CREASE) != 0; + rb->use_material = (edge_types & LRT_EDGE_FLAG_MATERIAL) != 0; + rb->use_edge_marks = (edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0; + rb->use_intersections = (edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0; + + rb->chain_data_pool = &lc->chain_data_pool; BLI_spin_init(&rb->lock_task); BLI_spin_init(&rb->lock_cuts); @@ -3818,7 +3845,9 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this * * \return True when a change is made. */ -bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModifierData *lmd) +bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, + LineartGpencilModifierData *lmd, + LineartCache **cached_result) { LineartRenderBuffer *rb; Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -3836,7 +3865,10 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif return false; } - rb = lineart_create_render_buffer(scene, lmd); + LineartCache *lc = lineart_init_cache(); + *cached_result = lc; + + rb = lineart_create_render_buffer(scene, lmd, lc); /* Triangle thread testing data size varies depending on the thread count. * See definition of LineartTriangleThread for details. */ @@ -3844,7 +3876,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif /* This is used to limit calculation to a certain level to save time, lines who have higher * occlusion levels will get ignored. */ - rb->max_occlusion_level = MAX2(lmd->level_start, lmd->level_end); + rb->max_occlusion_level = (lmd->flags & LRT_GPENCIL_USE_CACHE) ? + lmd->level_end_override : + (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start); /* FIXME(Yiming): See definition of int #LineartRenderBuffer::_source_type for detailed. */ rb->_source_type = lmd->source_type; @@ -3907,13 +3941,8 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif /* Then we connect chains based on the _proximity_ of their end points in image space, here's * the place threshold value gets involved. */ - - /* do_geometry_space = true. */ MOD_lineart_chain_connect(rb); - /* After chaining, we need to clear flags so we don't confuse GPencil generation calls. */ - MOD_lineart_chain_clear_picked_flag(rb); - float *t_image = &lmd->chaining_image_threshold; /* This configuration ensures there won't be accidental lost of short unchained segments. */ MOD_lineart_chain_discard_short(rb, MIN2(*t_image, 0.001f) - FLT_EPSILON); @@ -3921,6 +3950,12 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif if (rb->angle_splitting_threshold > FLT_EPSILON) { MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); } + + /* Finally transfer the result list into cache. */ + memcpy(&lc->chains, &rb->chains, sizeof(ListBase)); + + /* At last, we need to clear flags so we don't confuse GPencil generation calls. */ + MOD_lineart_chain_clear_picked_flag(lc); } if (G.debug_value == 4000) { @@ -3933,7 +3968,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif return true; } -static int lineart_rb_edge_types(LineartRenderBuffer *rb) +static int UNUSED_FUNCTION(lineart_rb_edge_types)(LineartRenderBuffer *rb) { int types = 0; types |= rb->use_contour ? LRT_EDGE_FLAG_CONTOUR : 0; @@ -3944,7 +3979,7 @@ static int lineart_rb_edge_types(LineartRenderBuffer *rb) return types; } -static void lineart_gpencil_generate(LineartRenderBuffer *rb, +static void lineart_gpencil_generate(LineartCache *cache, Depsgraph *depsgraph, Object *gpencil_object, float (*gp_obmat_inverse)[4], @@ -3964,9 +3999,9 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, const char *vgname, int modifier_flags) { - if (rb == NULL) { + if (cache == NULL) { if (G.debug_value == 4000) { - printf("NULL Lineart rb!\n"); + printf("NULL Lineart cache!\n"); } return; } @@ -3990,11 +4025,11 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, float mat[4][4]; unit_m4(mat); - int enabled_types = lineart_rb_edge_types(rb); + int enabled_types = cache->rb_edge_types; bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP; bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP; - LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) { if (ec->picked) { continue; @@ -4108,7 +4143,7 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, /** * Wrapper for external calls. */ -void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb, +void MOD_lineart_gpencil_generate(LineartCache *cache, Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, @@ -4156,7 +4191,7 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb, } float gp_obmat_inverse[4][4]; invert_m4_m4(gp_obmat_inverse, ob->obmat); - lineart_gpencil_generate(rb, + lineart_gpencil_generate(cache, depsgraph, ob, gp_obmat_inverse, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c index c023c63ebc9..7ebb869e955 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c @@ -88,7 +88,12 @@ static void clear_strokes(Object *ob, GpencilModifierData *md, int frame) BKE_gpencil_layer_frame_delete(gpl, gpf); } -static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int frame) +static bool bake_strokes(Object *ob, + Depsgraph *dg, + LineartCache **lc, + GpencilModifierData *md, + int frame, + bool is_first) { /* Modifier data sanity check. */ if (lineart_mod_is_disabled(md)) { @@ -111,11 +116,22 @@ static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int /* No greasepencil frame created or found. */ return false; } - - MOD_lineart_compute_feature_lines(dg, lmd); + LineartCache *local_lc = *lc; + if (!(*lc)) { + MOD_lineart_compute_feature_lines(dg, lmd, lc); + MOD_lineart_destroy_render_data(lmd); + } + else { + if (is_first || (!(lmd->flags & LRT_GPENCIL_USE_CACHE))) { + MOD_lineart_compute_feature_lines(dg, lmd, &local_lc); + MOD_lineart_destroy_render_data(lmd); + } + MOD_lineart_chain_clear_picked_flag(local_lc); + lmd->cache = local_lc; + } MOD_lineart_gpencil_generate( - lmd->render_buffer, + lmd->cache, dg, ob, gpl, @@ -135,7 +151,15 @@ static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int lmd->vgname, lmd->flags); - MOD_lineart_destroy_render_data(lmd); + if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + /* Clear local cache. */ + if (!is_first) { + MOD_lineart_clear_cache(&local_lc); + } + /* Restore the original cache pointer so the modifiers below still have access to the + * "global" cache. */ + lmd->cache = gpd->runtime.lineart_cache; + } return true; } @@ -174,14 +198,21 @@ static bool lineart_gpencil_bake_single_target(LineartBakeJob *bj, Object *ob, i } } + GpencilLineartLimitInfo info = BKE_gpencil_get_lineart_modifier_limits(ob); + + LineartCache *lc = NULL; + bool is_first = true; LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { if (md->type != eGpencilModifierType_Lineart) { continue; } - if (bake_strokes(ob, bj->dg, md, frame)) { + BKE_gpencil_set_lineart_modifier_limits(md, &info, is_first); + if (bake_strokes(ob, bj->dg, &lc, md, frame, is_first)) { touched = true; + is_first = false; } } + MOD_lineart_clear_cache(&lc); return touched; } diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h index 03d60c60b4b..197077cf76c 100644 --- a/source/blender/gpu/GPU_index_buffer.h +++ b/source/blender/gpu/GPU_index_buffer.h @@ -44,7 +44,6 @@ typedef struct GPUIndexBufBuilder { uint index_max; GPUPrimType prim_type; uint32_t *data; - const struct GPUIndexBufBuilder *parent; } GPUIndexBufBuilder; /* supports all primitive types. */ @@ -55,17 +54,11 @@ void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint ve GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len); /* - * Thread safe sub builders. + * Thread safe. * - * Note that `GPU_indexbuf_subbuilder_init` and `GPU_indexbuf_subbuilder_finish` are not thread - * safe and should be called when no threads are active. The pattern is to create a subbuilder for - * each thread/task. Each thread/task would update their sub builder. When all thread are completed - * the sub-builders can then be merged back to the parent builder. + * Function inspired by the reduction directives of multithread work APIs.. */ -void GPU_indexbuf_subbuilder_init(const GPUIndexBufBuilder *parent_builder, - GPUIndexBufBuilder *sub_builder); -void GPU_indexbuf_subbuilder_finish(GPUIndexBufBuilder *builder, - const GPUIndexBufBuilder *parent_builder); +void GPU_indexbuf_join(GPUIndexBufBuilder *builder, const GPUIndexBufBuilder *builder_from); void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v); void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *); diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc index 3cdcaac5544..be7470e79c1 100644 --- a/source/blender/gpu/intern/gpu_index_buffer.cc +++ b/source/blender/gpu/intern/gpu_index_buffer.cc @@ -57,7 +57,6 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder, builder->index_max = 0; builder->prim_type = prim_type; builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); - builder->parent = nullptr; } void GPU_indexbuf_init(GPUIndexBufBuilder *builder, @@ -80,21 +79,12 @@ GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len) return elem_; } -void GPU_indexbuf_subbuilder_init(const GPUIndexBufBuilder *parent_builder, - GPUIndexBufBuilder *sub_builder) +void GPU_indexbuf_join(GPUIndexBufBuilder *builder_to, const GPUIndexBufBuilder *builder_from) { - BLI_assert(parent_builder->parent == nullptr); - memcpy(sub_builder, parent_builder, sizeof(GPUIndexBufBuilder)); - sub_builder->parent = parent_builder; -} - -void GPU_indexbuf_subbuilder_finish(GPUIndexBufBuilder *parent_builder, - const GPUIndexBufBuilder *sub_builder) -{ - BLI_assert(parent_builder == sub_builder->parent); - parent_builder->index_len = max_uu(parent_builder->index_len, sub_builder->index_len); - parent_builder->index_min = min_uu(parent_builder->index_min, sub_builder->index_min); - parent_builder->index_max = max_uu(parent_builder->index_max, sub_builder->index_max); + BLI_assert(builder_to->data == builder_from->data); + builder_to->index_len = max_uu(builder_to->index_len, builder_from->index_len); + builder_to->index_min = min_uu(builder_to->index_min, builder_from->index_min); + builder_to->index_max = max_uu(builder_to->index_max, builder_from->index_max); } void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v) diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 265dec7c56a..e2eb8953292 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -159,7 +159,7 @@ void Shader::print_log(Span<const char *> sources, char *log, const char *stage, } /* Print line from the source file that is producing the error. */ if ((error_line != -1) && (error_line != last_error_line || error_char != last_error_char)) { - const char *src_line_end = src_line; + const char *src_line_end; found_line_id = false; /* error_line is 1 based in this case. */ int src_line_index = 1; diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl index 21c7f79a57c..6dd0201535d 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl @@ -36,6 +36,9 @@ vec3 compute_masks(vec2 uv) corner_rad = right_half ? outRoundCorners.y : outRoundCorners.x; } + /* Fade emboss at the border. */ + float emboss_size = upper_half ? 0.0 : min(1.0, uv_sdf.x / (corner_rad * ratio)); + /* Signed distance field from the corner (in pixel). * inner_sdf is sharp and outer_sdf is rounded. */ uv_sdf -= corner_rad; @@ -43,9 +46,6 @@ vec3 compute_masks(vec2 uv) float outer_sdf = -length(min(uv_sdf, 0.0)); float sdf = inner_sdf + outer_sdf + corner_rad; - /* Fade emboss at the border. */ - float emboss_size = clamp((upper_half) ? 0.0 : (uv.x / corner_rad), 0.0, 1.0); - /* Clamp line width to be at least 1px wide. This can happen if the projection matrix * has been scaled (i.e: Node editor)... */ float line_width = (lineWidth > 0.0) ? max(fwidth(uv.y), lineWidth) : 0.0; diff --git a/source/blender/gpu/tests/gpu_index_buffer_test.cc b/source/blender/gpu/tests/gpu_index_buffer_test.cc index ebc110056e3..78e351af7f1 100644 --- a/source/blender/gpu/tests/gpu_index_buffer_test.cc +++ b/source/blender/gpu/tests/gpu_index_buffer_test.cc @@ -21,7 +21,7 @@ TEST_F(GPUTest, gpu_index_buffer_subbuilders) GPUIndexBufBuilder subbuilders[num_subbuilders]; for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) { - GPU_indexbuf_subbuilder_init(&builder, &subbuilders[subbuilder_index]); + memcpy(&subbuilders[subbuilder_index], &builder, sizeof(builder)); } for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) { @@ -35,7 +35,7 @@ TEST_F(GPUTest, gpu_index_buffer_subbuilders) for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) { EXPECT_EQ(builder.index_len, subbuilder_index * verts_per_subbuilders); - GPU_indexbuf_subbuilder_finish(&builder, &subbuilders[subbuilder_index]); + GPU_indexbuf_join(&builder, &subbuilders[subbuilder_index]); EXPECT_EQ(builder.index_len, (subbuilder_index + 1) * verts_per_subbuilders); } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 9c84127105a..69a80d6e0d3 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -70,6 +70,7 @@ extern "C" { */ struct ImBuf; struct rcti; +struct rctf; /** * @@ -323,6 +324,11 @@ typedef enum IMB_Proxy_Size { IMB_PROXY_MAX_SLOT = 4, } IMB_Proxy_Size; +typedef enum eIMBInterpolationFilterMode { + IMB_FILTER_NEAREST, + IMB_FILTER_BILINEAR, +} eIMBInterpolationFilterMode; + /* Defaults to BL_proxy within the directory of the animation. */ void IMB_anim_set_index_dir(struct anim *anim, const char *dir); void IMB_anim_get_fname(struct anim *anim, char *file, int size); @@ -727,11 +733,17 @@ void IMB_processor_apply_threaded( void(init_handle)(void *handle, int start_line, int tot_line, void *customdata), void *(do_thread)(void *)); -typedef void (*ScanlineThreadFunc)(void *custom_data, int start_scanline, int num_scanlines); +typedef void (*ScanlineThreadFunc)(void *custom_data, int scanline); void IMB_processor_apply_threaded_scanlines(int total_scanlines, ScanlineThreadFunc do_thread, void *custom_data); +void IMB_transform(struct ImBuf *src, + struct ImBuf *dst, + float transform_matrix[3][3], + struct rctf *src_crop, + const eIMBInterpolationFilterMode filter); + /* ffmpeg */ void IMB_ffmpeg_init(void); const char *IMB_ffmpeg_last_error(void); diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h index 363ad45e0e6..37309ccc13a 100644 --- a/source/blender/imbuf/intern/IMB_indexer.h +++ b/source/blender/imbuf/intern/IMB_indexer.h @@ -49,6 +49,7 @@ typedef struct anim_index_entry { int frameno; uint64_t seek_pos; + uint64_t seek_pos_pts; uint64_t seek_pos_dts; uint64_t pts; } anim_index_entry; @@ -77,14 +78,19 @@ typedef struct anim_index_builder { } anim_index_builder; anim_index_builder *IMB_index_builder_create(const char *name); -void IMB_index_builder_add_entry( - anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts); +void IMB_index_builder_add_entry(anim_index_builder *fp, + int frameno, + uint64_t seek_pos, + uint64_t seek_pos_pts, + uint64_t seek_pos_dts, + uint64_t pts); void IMB_index_builder_proc_frame(anim_index_builder *fp, unsigned char *buffer, int data_size, int frameno, uint64_t seek_pos, + uint64_t seek_pos_pts, uint64_t seek_pos_dts, uint64_t pts); @@ -92,6 +98,7 @@ void IMB_index_builder_finish(anim_index_builder *fp, int rollback); struct anim_index *IMB_indexer_open(const char *name); uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index); +uint64_t IMB_indexer_get_seek_pos_pts(struct anim_index *idx, int frame_index); uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index); int IMB_indexer_get_frame_index(struct anim_index *idx, int frameno); diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index b65c3e364db..450c76630a8 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -924,7 +924,7 @@ static int ffmpeg_decode_video_frame(struct anim *anim) anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + anim->cur_pts = av_get_pts_from_frame(anim->pFrame); if (anim->pFrame->key_frame) { anim->cur_key_frame_pts = anim->cur_pts; @@ -949,7 +949,7 @@ static int ffmpeg_decode_video_frame(struct anim *anim) anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + anim->cur_pts = av_get_pts_from_frame(anim->pFrame); if (anim->pFrame->key_frame) { anim->cur_key_frame_pts = anim->cur_pts; @@ -1048,25 +1048,31 @@ static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position) return pos; } +/* This gives us an estimate of which pts our requested frame will have. + * Note that this might be off a bit in certain video files, but it should still be close enough. + */ static int64_t ffmpeg_get_pts_to_search(struct anim *anim, struct anim_index *tc_index, int position) { int64_t pts_to_search; - int64_t st_time = anim->pFormatCtx->start_time; - AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - double pts_time_base = av_q2d(v_st->time_base); if (tc_index) { int new_frame_index = IMB_indexer_get_frame_index(tc_index, position); pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index); } else { - pts_to_search = (long long)floor(((double)position) / pts_time_base / frame_rate + 0.5); + int64_t st_time = anim->pFormatCtx->start_time; + AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; + AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL); + AVRational time_base = v_st->time_base; + + int64_t steps_per_frame = (frame_rate.den * time_base.den) / (frame_rate.num * time_base.num); + pts_to_search = position * steps_per_frame; - if (st_time != AV_NOPTS_VALUE) { - pts_to_search += st_time / pts_time_base / AV_TIME_BASE; + if (st_time != AV_NOPTS_VALUE && st_time != 0) { + int64_t start_frame = (double)st_time / AV_TIME_BASE * av_q2d(frame_rate); + pts_to_search += start_frame * steps_per_frame; } } return pts_to_search; @@ -1100,6 +1106,7 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea (int64_t)pts_to_search); int64_t start_gop_frame = anim->cur_key_frame_pts; + bool scan_fuzzy = false; while (anim->cur_pts < pts_to_search) { av_log(anim->pFormatCtx, @@ -1114,6 +1121,14 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea if (start_gop_frame != anim->cur_key_frame_pts) { break; } + + if (anim->cur_pts < pts_to_search && + anim->cur_pts + anim->pFrame->pkt_duration > pts_to_search) { + /* Our estimate of the pts was a bit off, but we have the frame we want. */ + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN fuzzy frame match\n"); + scan_fuzzy = true; + break; + } } if (start_gop_frame != anim->cur_key_frame_pts) { @@ -1126,7 +1141,8 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea (int64_t)anim->cur_pts, (int64_t)pts_to_search); } - if (anim->cur_pts == pts_to_search) { + + if (scan_fuzzy || anim->cur_pts == pts_to_search) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n"); } else { @@ -1139,17 +1155,19 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea * necessary I-frame is not honored. It is not even guaranteed that I-frame, that must be * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and * https://developer.blender.org/T86944. */ -static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t *requested_pos) +static int ffmpeg_generic_seek_workaround(struct anim *anim, + int64_t *requested_pos, + int64_t pts_to_search) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); int64_t current_pos = *requested_pos; + int64_t offset = 0; + + int64_t cur_pts, prev_pts = -1; - /* This time offset maximum limit is arbitrary. If some files fails to decode it may be - * increased. Seek performance will be negatively affected. Small initial offset is - * necessary because encoder can re-arrange frames as it needs but within it's delay, which - * is usually small. */ - for (int offset = 5; offset < 25; offset++) { + /* Step backward frame by frame until we find the key frame we are looking for. */ + while (current_pos != 0) { current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate); current_pos = max_ii(current_pos, 0); @@ -1161,17 +1179,34 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t *requested_ /* Read first video stream packet. */ AVPacket *read_packet = av_packet_alloc(); while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) { - if (anim->cur_packet->stream_index == anim->videoStream) { + if (read_packet->stream_index == anim->videoStream) { break; } + av_packet_unref(read_packet); } - /* If this packet contains I-frame, exit loop. This should be the frame that we need. */ + /* If this packet contains an I-frame, this could be the frame that we need. */ bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY; + /* We need to check the packet timestamp as the key frame could be for a GOP forward in the the + * video stream. So if it has a larger timestamp than the frame we want, ignore it. + */ + cur_pts = timestamp_from_pts_or_dts(read_packet->pts, read_packet->dts); av_packet_free(&read_packet); + if (is_key_frame) { - break; + if (cur_pts <= pts_to_search) { + /* We found the I-frame we were looking for! */ + break; + } + if (cur_pts == prev_pts) { + /* We got the same key frame packet twice. + * This probably means that we have hit the beginning of the stream. */ + break; + } } + + prev_pts = cur_pts; + offset++; } *requested_pos = current_pos; @@ -1181,10 +1216,11 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t *requested_ } /* Seek to last necessary key frame. */ -static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim_index *tc_index) +static int ffmpeg_seek_to_key_frame(struct anim *anim, + int position, + struct anim_index *tc_index, + int64_t pts_to_search) { - AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - int64_t pos; int ret; @@ -1197,25 +1233,28 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim /* No need to seek, return early. */ return 0; } + uint64_t pts; uint64_t dts; pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index); + pts = IMB_indexer_get_seek_pos_pts(tc_index, new_frame_index); dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index); - anim->cur_key_frame_pts = pos; + anim->cur_key_frame_pts = timestamp_from_pts_or_dts(pts, dts); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pts = %" PRIu64 "\n", pts); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts); if (ffmpeg_seek_by_byte(anim->pFormatCtx)) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n"); ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE); - av_update_cur_dts(anim->pFormatCtx, v_st, dts); } else { - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using DTS pos\n"); - ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, dts, AVSEEK_FLAG_BACKWARD); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS pos\n"); + ret = av_seek_frame( + anim->pFormatCtx, anim->videoStream, anim->cur_key_frame_pts, AVSEEK_FLAG_BACKWARD); } } else { @@ -1230,7 +1269,7 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); } else { - ret = ffmpeg_generic_seek_workaround(anim, &pos); + ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos); } @@ -1243,31 +1282,40 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim } av_packet_unref(current_gop_start_packet); } - bool same_gop = current_gop_start_packet->pts == anim->cur_key_frame_pts; + int64_t gop_pts = timestamp_from_pts_or_dts(current_gop_start_packet->pts, + current_gop_start_packet->dts); + + av_packet_free(¤t_gop_start_packet); + bool same_gop = gop_pts == anim->cur_key_frame_pts; if (same_gop && position > anim->cur_position) { /* Change back to our old frame position so we can simply continue decoding from there. */ + int64_t cur_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts); + + if (cur_pts == gop_pts) { + /* We are already at the correct position. */ + return 0; + } AVPacket *temp = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, temp) >= 0) { - if (temp->stream_index == anim->videoStream && temp->pts == anim->cur_packet->pts) { + int64_t temp_pts = timestamp_from_pts_or_dts(temp->pts, temp->dts); + if (temp->stream_index == anim->videoStream && temp_pts == cur_pts) { break; } av_packet_unref(temp); } - av_packet_free(¤t_gop_start_packet); av_packet_free(&temp); return 0; } - anim->cur_key_frame_pts = current_gop_start_packet->pts; - av_packet_free(¤t_gop_start_packet); + anim->cur_key_frame_pts = gop_pts; /* Seek back so we are at the correct position after we decoded a frame. */ av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); } } if (ret < 0) { - int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); av_log(anim->pFormatCtx, AV_LOG_ERROR, "FETCH: " @@ -1278,6 +1326,8 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim pts_to_search, ret); } + /* Flush the internal buffers of ffmpeg. This needs to be done after seeking to avoid decoding + * errors. */ avcodec_flush_buffers(anim->pCodecCtx); anim->cur_pts = -1; @@ -1328,7 +1378,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n"); ffmpeg_decode_video_frame(anim); } - else if (ffmpeg_seek_to_key_frame(anim, position, tc_index) >= 0) { + else if (ffmpeg_seek_to_key_frame(anim, position, tc_index, pts_to_search) >= 0) { ffmpeg_decode_video_frame_scan(anim, pts_to_search); } diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 68d0b516828..71e513fb405 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -3539,12 +3539,11 @@ typedef struct PartialThreadData { int xmin, ymin, xmax; } PartialThreadData; -static void partial_buffer_update_rect_thread_do(void *data_v, - int start_scanline, - int num_scanlines) +static void partial_buffer_update_rect_thread_do(void *data_v, int scanline) { PartialThreadData *data = (PartialThreadData *)data_v; - int ymin = data->ymin + start_scanline; + int ymin = data->ymin + scanline; + const int num_scanlines = 1; partial_buffer_update_rect(data->ibuf, data->display_buffer, data->linear_buffer, diff --git a/source/blender/imbuf/intern/dds/ColorBlock.cpp b/source/blender/imbuf/intern/dds/ColorBlock.cpp index a05c7e2a70d..1a67975b5f0 100644 --- a/source/blender/imbuf/intern/dds/ColorBlock.cpp +++ b/source/blender/imbuf/intern/dds/ColorBlock.cpp @@ -113,9 +113,8 @@ void ColorBlock::init(uint w, uint h, const float *data, uint x, uint y) const uint idx = ((y + by) * w + x + bx); Color32 &c = color(e, i); - c.r = uint8(255 * CLAMP(data[idx + 0 * srcPlane], - 0.0f, - 1.0f)); /* @@ Is this the right way to quantize floats to bytes? */ + /* @@ Is this the right way to quantize floats to bytes? */ + c.r = uint8(255 * CLAMP(data[idx + 0 * srcPlane], 0.0f, 1.0f)); c.g = uint8(255 * CLAMP(data[idx + 1 * srcPlane], 0.0f, 1.0f)); c.b = uint8(255 * CLAMP(data[idx + 2 * srcPlane], 0.0f, 1.0f)); c.a = uint8(255 * CLAMP(data[idx + 3 * srcPlane], 0.0f, 1.0f)); diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 5f580449e12..47712456014 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -536,13 +536,12 @@ typedef struct FloatToFloatThreadData { int stride_from; } FloatToFloatThreadData; -static void imb_buffer_float_from_float_thread_do(void *data_v, - int start_scanline, - int num_scanlines) +static void imb_buffer_float_from_float_thread_do(void *data_v, int scanline) { + const int num_scanlines = 1; FloatToFloatThreadData *data = (FloatToFloatThreadData *)data_v; - size_t offset_from = ((size_t)start_scanline) * data->stride_from * data->channels_from; - size_t offset_to = ((size_t)start_scanline) * data->stride_to * data->channels_from; + size_t offset_from = ((size_t)scanline) * data->stride_from * data->channels_from; + size_t offset_to = ((size_t)scanline) * data->stride_to * data->channels_from; IMB_buffer_float_from_float(data->rect_to + offset_to, data->rect_from + offset_from, data->channels_from, diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 1fe3a7717fb..e2d469ab5a3 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -127,6 +127,22 @@ void bicubic_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in /** \name Bi-Linear Interpolation * \{ */ +BLI_INLINE void bilinear_interpolation_color_fl( + struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v) +{ + BLI_assert(outF); + BLI_assert(in->rect_float); + BLI_bilinear_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v); +} + +BLI_INLINE void bilinear_interpolation_color_char( + struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v) +{ + BLI_assert(outI); + BLI_assert(in->rect); + BLI_bilinear_interpolation_char((unsigned char *)in->rect, outI, in->x, in->y, 4, u, v); +} + void bilinear_interpolation_color( struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) { @@ -238,60 +254,58 @@ void bilinear_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, i /** \name Nearest Interpolation * \{ */ -/* function assumes out to be zero'ed, only does RGBA */ -void nearest_interpolation_color( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) +/* functions assumes out to be zero'ed, only does RGBA */ +BLI_INLINE void nearest_interpolation_color_char( + struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v) { - const float *dataF; - unsigned char *dataI; - int y1, x1; - + BLI_assert(outI); + BLI_assert(in->rect); /* ImBuf in must have a valid rect or rect_float, assume this is already checked */ + int x1 = (int)(u); + int y1 = (int)(v); - x1 = (int)(u); - y1 = (int)(v); + /* sample area entirely outside image? */ + if (x1 < 0 || x1 >= in->x || y1 < 0 || y1 >= in->y) { + outI[0] = outI[1] = outI[2] = outI[3] = 0; + return; + } + + const size_t offset = (in->x * y1 + x1) * 4; + const unsigned char *dataI = (unsigned char *)in->rect + offset; + outI[0] = dataI[0]; + outI[1] = dataI[1]; + outI[2] = dataI[2]; + outI[3] = dataI[3]; +} + +BLI_INLINE void nearest_interpolation_color_fl( + struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v) +{ + BLI_assert(outF); + BLI_assert(in->rect_float); + /* ImBuf in must have a valid rect or rect_float, assume this is already checked */ + int x1 = (int)(u); + int y1 = (int)(v); /* sample area entirely outside image? */ - if (x1 < 0 || x1 > in->x - 1 || y1 < 0 || y1 > in->y - 1) { - if (outI) { - outI[0] = outI[1] = outI[2] = outI[3] = 0; - } - if (outF) { - outF[0] = outF[1] = outF[2] = outF[3] = 0.0f; - } + if (x1 < 0 || x1 >= in->x || y1 < 0 || y1 >= in->y) { + zero_v4(outF); return; } - /* sample including outside of edges of image */ - if (x1 < 0 || y1 < 0) { - if (outI) { - outI[0] = 0; - outI[1] = 0; - outI[2] = 0; - outI[3] = 0; - } - if (outF) { - outF[0] = 0.0f; - outF[1] = 0.0f; - outF[2] = 0.0f; - outF[3] = 0.0f; - } + const size_t offset = (in->x * y1 + x1) * 4; + const float *dataF = in->rect_float + offset; + copy_v4_v4(outF, dataF); +} + +void nearest_interpolation_color( + struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) +{ + if (outF) { + nearest_interpolation_color_fl(in, outI, outF, u, v); } else { - dataI = (unsigned char *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x1; - if (outI) { - outI[0] = dataI[0]; - outI[1] = dataI[1]; - outI[2] = dataI[2]; - outI[3] = dataI[3]; - } - dataF = in->rect_float + ((size_t)in->x) * y1 * 4 + 4 * x1; - if (outF) { - outF[0] = dataF[0]; - outF[1] = dataF[1]; - outF[2] = dataF[2]; - outF[3] = dataF[3]; - } + nearest_interpolation_color_char(in, outI, outF, u, v); } } @@ -350,6 +364,141 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in } /* -------------------------------------------------------------------- */ +/** \name Image transform + * \{ */ +typedef struct TransformUserData { + ImBuf *src; + ImBuf *dst; + float start_uv[2]; + float add_x[2]; + float add_y[2]; + rctf src_crop; +} TransformUserData; + +static void imb_transform_calc_start_uv(const float transform_matrix[3][3], float r_start_uv[2]) +{ + float orig[2]; + orig[0] = 0.0f; + orig[1] = 0.0f; + mul_v2_m3v2(r_start_uv, transform_matrix, orig); +} + +static void imb_transform_calc_add_x(const float transform_matrix[3][3], + const float start_uv[2], + const int width, + float r_add_x[2]) +{ + float uv_max_x[2]; + uv_max_x[0] = width; + uv_max_x[1] = 0.0f; + mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x); + sub_v2_v2(r_add_x, start_uv); + mul_v2_fl(r_add_x, 1.0f / width); +} + +static void imb_transform_calc_add_y(const float transform_matrix[3][3], + const float start_uv[2], + const int height, + float r_add_y[2]) +{ + float uv_max_y[2]; + uv_max_y[0] = 0.0f; + uv_max_y[1] = height; + mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y); + sub_v2_v2(r_add_y, start_uv); + mul_v2_fl(r_add_y, 1.0f / height); +} + +typedef void (*InterpolationColorFunction)( + struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); +BLI_INLINE void imb_transform_scanlines(const TransformUserData *user_data, + int scanline, + InterpolationColorFunction interpolation) +{ + const int width = user_data->dst->x; + + float uv[2]; + madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline); + + unsigned char *outI = NULL; + float *outF = NULL; + pixel_from_buffer(user_data->dst, &outI, &outF, 0, scanline); + + for (int xi = 0; xi < width; xi++) { + if (uv[0] >= user_data->src_crop.xmin && uv[0] < user_data->src_crop.xmax && + uv[1] >= user_data->src_crop.ymin && uv[1] < user_data->src_crop.ymax) { + interpolation(user_data->src, outI, outF, uv[0], uv[1]); + } + add_v2_v2(uv, user_data->add_x); + if (outI) { + outI += 4; + } + if (outF) { + outF += 4; + } + } +} + +static void imb_transform_nearest_scanlines(void *custom_data, int scanline) +{ + const TransformUserData *user_data = custom_data; + InterpolationColorFunction interpolation = NULL; + if (user_data->dst->rect_float) { + interpolation = nearest_interpolation_color_fl; + } + else { + interpolation = nearest_interpolation_color_char; + } + imb_transform_scanlines(user_data, scanline, interpolation); +} + +static void imb_transform_bilinear_scanlines(void *custom_data, int scanline) +{ + const TransformUserData *user_data = custom_data; + InterpolationColorFunction interpolation = NULL; + if (user_data->dst->rect_float) { + interpolation = bilinear_interpolation_color_fl; + } + else if (user_data->dst->rect) { + interpolation = bilinear_interpolation_color_char; + } + imb_transform_scanlines(user_data, scanline, interpolation); +} + +static ScanlineThreadFunc imb_transform_scanline_func(const eIMBInterpolationFilterMode filter) +{ + ScanlineThreadFunc scanline_func = NULL; + switch (filter) { + case IMB_FILTER_NEAREST: + scanline_func = imb_transform_nearest_scanlines; + break; + case IMB_FILTER_BILINEAR: + scanline_func = imb_transform_bilinear_scanlines; + break; + } + return scanline_func; +} + +void IMB_transform(struct ImBuf *src, + struct ImBuf *dst, + float transform_matrix[3][3], + struct rctf *src_crop, + const eIMBInterpolationFilterMode filter) +{ + TransformUserData user_data; + user_data.src = src; + user_data.dst = dst; + user_data.src_crop = *src_crop; + imb_transform_calc_start_uv(transform_matrix, user_data.start_uv); + imb_transform_calc_add_x(transform_matrix, user_data.start_uv, src->x, user_data.add_x); + imb_transform_calc_add_y(transform_matrix, user_data.start_uv, src->y, user_data.add_y); + ScanlineThreadFunc scanline_func = imb_transform_scanline_func(filter); + IMB_processor_apply_threaded_scanlines(dst->y, scanline_func, &user_data); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Threaded Image Processing * \{ */ @@ -374,7 +523,7 @@ void IMB_processor_apply_threaded( int total_tasks = (buffer_lines + lines_per_task - 1) / lines_per_task; int i, start_line; - task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); + task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW); handles = MEM_callocN(handle_size * total_tasks, "processor apply threaded handles"); @@ -409,41 +558,28 @@ void IMB_processor_apply_threaded( typedef struct ScanlineGlobalData { void *custom_data; ScanlineThreadFunc do_thread; - int scanlines_per_task; - int total_scanlines; } ScanlineGlobalData; -static void processor_apply_scanline_func(TaskPool *__restrict pool, void *taskdata) +static void processor_apply_parallel(void *__restrict userdata, + const int scanline, + const TaskParallelTLS *__restrict UNUSED(tls)) { - ScanlineGlobalData *data = BLI_task_pool_user_data(pool); - int start_scanline = POINTER_AS_INT(taskdata); - int num_scanlines = min_ii(data->scanlines_per_task, data->total_scanlines - start_scanline); - data->do_thread(data->custom_data, start_scanline, num_scanlines); + ScanlineGlobalData *data = userdata; + data->do_thread(data->custom_data, scanline); } void IMB_processor_apply_threaded_scanlines(int total_scanlines, ScanlineThreadFunc do_thread, void *custom_data) { - const int scanlines_per_task = 64; - ScanlineGlobalData data; - data.custom_data = custom_data; - data.do_thread = do_thread; - 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; - TaskPool *task_pool = BLI_task_pool_create(&data, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); - 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); - start_line += scanlines_per_task; - } - - /* work and wait until tasks are done */ - BLI_task_pool_work_and_wait(task_pool); - - /* Free memory. */ - BLI_task_pool_free(task_pool); + TaskParallelSettings settings; + ScanlineGlobalData data = { + .do_thread = do_thread, + .custom_data = custom_data, + }; + + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, total_scanlines, &data, processor_apply_parallel, &settings); } /** \} */ diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 4d116eb73b8..a530acb15b0 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -50,7 +50,7 @@ # include <libavutil/imgutils.h> #endif -static const char magic[] = "BlenMIdx"; +static const char binary_header_str[] = "BlenMIdx"; static const char temp_ext[] = "_part"; static const int proxy_sizes[] = {IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75, IMB_PROXY_100}; @@ -65,7 +65,7 @@ static int tc_types[] = { }; #endif -#define INDEX_FILE_VERSION 1 +#define INDEX_FILE_VERSION 2 /* ---------------------------------------------------------------------- * - time code index functions @@ -96,16 +96,25 @@ anim_index_builder *IMB_index_builder_create(const char *name) return NULL; } - fprintf(rv->fp, "%s%c%.3d", magic, (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v', INDEX_FILE_VERSION); + fprintf(rv->fp, + "%s%c%.3d", + binary_header_str, + (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v', + INDEX_FILE_VERSION); return rv; } -void IMB_index_builder_add_entry( - anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts) +void IMB_index_builder_add_entry(anim_index_builder *fp, + int frameno, + uint64_t seek_pos, + uint64_t seek_pos_pts, + uint64_t seek_pos_dts, + uint64_t pts) { fwrite(&frameno, sizeof(int), 1, fp->fp); fwrite(&seek_pos, sizeof(uint64_t), 1, fp->fp); + fwrite(&seek_pos_pts, sizeof(uint64_t), 1, fp->fp); fwrite(&seek_pos_dts, sizeof(uint64_t), 1, fp->fp); fwrite(&pts, sizeof(uint64_t), 1, fp->fp); } @@ -115,6 +124,7 @@ void IMB_index_builder_proc_frame(anim_index_builder *fp, int data_size, int frameno, uint64_t seek_pos, + uint64_t seek_pos_pts, uint64_t seek_pos_dts, uint64_t pts) { @@ -122,13 +132,14 @@ void IMB_index_builder_proc_frame(anim_index_builder *fp, anim_index_entry e; e.frameno = frameno; e.seek_pos = seek_pos; + e.seek_pos_pts = seek_pos_pts; e.seek_pos_dts = seek_pos_dts; e.pts = pts; fp->proc_frame(fp, buffer, data_size, &e); } else { - IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_dts, pts); + IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_pts, seek_pos_dts, pts); } } @@ -159,22 +170,26 @@ struct anim_index *IMB_indexer_open(const char *name) int i; if (!fp) { + fprintf(stderr, "Couldn't open indexer file: %s\n", name); return NULL; } if (fread(header, 12, 1, fp) != 1) { + fprintf(stderr, "Couldn't read indexer file: %s\n", name); fclose(fp); return NULL; } header[12] = 0; - if (memcmp(header, magic, 8) != 0) { + if (memcmp(header, binary_header_str, 8) != 0) { + fprintf(stderr, "Error reading %s: Binary file type string missmatch\n", name); fclose(fp); return NULL; } if (atoi(header + 9) != INDEX_FILE_VERSION) { + fprintf(stderr, "Error reading %s: File version missmatch\n", name); fclose(fp); return NULL; } @@ -187,6 +202,7 @@ struct anim_index *IMB_indexer_open(const char *name) idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */ sizeof(uint64_t) + /* seek_pos */ + sizeof(uint64_t) + /* seek_pos_pts */ sizeof(uint64_t) + /* seek_pos_dts */ sizeof(uint64_t) /* pts */ ); @@ -200,12 +216,13 @@ struct anim_index *IMB_indexer_open(const char *name) for (i = 0; i < idx->num_entries; i++) { items_read += fread(&idx->entries[i].frameno, sizeof(int), 1, fp); items_read += fread(&idx->entries[i].seek_pos, sizeof(uint64_t), 1, fp); + items_read += fread(&idx->entries[i].seek_pos_pts, sizeof(uint64_t), 1, fp); items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(uint64_t), 1, fp); items_read += fread(&idx->entries[i].pts, sizeof(uint64_t), 1, fp); } - if (UNLIKELY(items_read != idx->num_entries * 4)) { - perror("error reading animation index file"); + if (UNLIKELY(items_read != idx->num_entries * 5)) { + fprintf(stderr, "Error: Element data size missmatch in: %s\n", name); MEM_freeN(idx->entries); MEM_freeN(idx); fclose(fp); @@ -216,6 +233,7 @@ struct anim_index *IMB_indexer_open(const char *name) for (i = 0; i < idx->num_entries; i++) { BLI_endian_switch_int32(&idx->entries[i].frameno); BLI_endian_switch_uint64(&idx->entries[i].seek_pos); + BLI_endian_switch_uint64(&idx->entries[i].seek_pos_pts); BLI_endian_switch_uint64(&idx->entries[i].seek_pos_dts); BLI_endian_switch_uint64(&idx->entries[i].pts); } @@ -237,6 +255,17 @@ uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index) return idx->entries[frame_index].seek_pos; } +uint64_t IMB_indexer_get_seek_pos_pts(struct anim_index *idx, int frame_index) +{ + if (frame_index < 0) { + frame_index = 0; + } + if (frame_index >= idx->num_entries) { + frame_index = idx->num_entries - 1; + } + return idx->entries[frame_index].seek_pos_pts; +} + uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index) { if (frame_index < 0) { @@ -318,8 +347,7 @@ int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size) { switch (pr_size) { case IMB_PROXY_NONE: - /* if we got here, something is broken anyways, so sane defaults... */ - return 0; + return -1; case IMB_PROXY_25: return 0; case IMB_PROXY_50: @@ -329,16 +357,16 @@ int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size) case IMB_PROXY_100: return 3; default: - return 0; + BLI_assert(!"Unhandled proxy size enum!"); + return -1; } } int IMB_timecode_to_array_index(IMB_Timecode_Type tc) { switch (tc) { - case IMB_TC_NONE: /* if we got here, something is broken anyways, - * so sane defaults... */ - return 0; + case IMB_TC_NONE: + return -1; case IMB_TC_RECORD_RUN: return 0; case IMB_TC_FREE_RUN: @@ -348,7 +376,8 @@ int IMB_timecode_to_array_index(IMB_Timecode_Type tc) case IMB_TC_RECORD_RUN_NO_GAPS: return 3; default: - return 0; + BLI_assert(!"Unhandled timecode type enum!"); + return -1; } } @@ -384,6 +413,8 @@ static bool get_proxy_filename(struct anim *anim, char index_dir[FILE_MAXDIR]; int i = IMB_proxy_size_to_array_index(preview_size); + BLI_assert(i >= 0); + char proxy_name[256]; char stream_suffix[20]; const char *name = (temp) ? "proxy_%d%s_part.avi" : "proxy_%d%s.avi"; @@ -415,6 +446,9 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname { char index_dir[FILE_MAXDIR]; int i = IMB_timecode_to_array_index(tc); + + BLI_assert(i >= 0); + const char *index_names[] = { "record_run%s%s.blen_tc", "free_run%s%s.blen_tc", @@ -771,9 +805,10 @@ typedef struct FFmpegIndexBuilderContext { IMB_Proxy_Size proxy_sizes_in_use; uint64_t seek_pos; - uint64_t last_seek_pos; - uint64_t seek_pos_dts; uint64_t seek_pos_pts; + uint64_t seek_pos_dts; + uint64_t last_seek_pos; + uint64_t last_seek_pos_pts; uint64_t last_seek_pos_dts; uint64_t start_pts; double frame_rate; @@ -926,8 +961,9 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c { int i; uint64_t s_pos = context->seek_pos; + uint64_t s_pts = context->seek_pos_pts; uint64_t s_dts = context->seek_pos_dts; - uint64_t pts = av_get_pts_from_frame(context->iFormatCtx, in_frame); + uint64_t pts = av_get_pts_from_frame(in_frame); for (i = 0; i < context->num_proxy_sizes; i++) { add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame); @@ -941,15 +977,15 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c context->frameno = floor( (pts - context->start_pts) * context->pts_time_base * context->frame_rate + 0.5); - /* decoding starts *always* on I-Frames, - * so: P-Frames won't work, even if all the - * information is in place, when we seek - * to the I-Frame presented *after* the P-Frame, - * but located before the P-Frame within - * the stream */ + int64_t seek_pos_pts = timestamp_from_pts_or_dts(context->seek_pos_pts, context->seek_pos_dts); - if (pts < context->seek_pos_pts) { + if (pts < seek_pos_pts) { + /* Decoding starts *always* on I-Frames. In this case our position is + * before our seek I-Frame. So we need to pick the previous available + * I-Frame to be able to decode this one properly. + */ s_pos = context->last_seek_pos; + s_pts = context->last_seek_pos_pts; s_dts = context->last_seek_pos_dts; } @@ -966,6 +1002,7 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c curr_packet->size, tc_frameno, s_pos, + s_pts, s_dts, pts); } @@ -1004,10 +1041,12 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, if (next_packet->stream_index == context->videoStream) { if (next_packet->flags & AV_PKT_FLAG_KEY) { context->last_seek_pos = context->seek_pos; + context->last_seek_pos_pts = context->seek_pos_pts; context->last_seek_pos_dts = context->seek_pos_dts; + context->seek_pos = next_packet->pos; - context->seek_pos_dts = next_packet->dts; context->seek_pos_pts = next_packet->pts; + context->seek_pos_dts = next_packet->dts; } int ret = avcodec_send_packet(context->iCodecCtx, next_packet); @@ -1389,6 +1428,10 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) char fname[FILE_MAX]; int i = IMB_proxy_size_to_array_index(preview_size); + if (i < 0) { + return NULL; + } + if (anim->proxy_anim[i]) { return anim->proxy_anim[i]; } @@ -1412,6 +1455,10 @@ struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) char fname[FILE_MAX]; int i = IMB_timecode_to_array_index(tc); + if (i < 0) { + return NULL; + } + if (anim->curr_idx[i]) { return anim->curr_idx[i]; } diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c index 6ae93def50f..4b5d68b9c13 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -988,8 +988,9 @@ typedef struct RectBlendThreadData { bool accumulate; } RectBlendThreadData; -static void rectblend_thread_do(void *data_v, int start_scanline, int num_scanlines) +static void rectblend_thread_do(void *data_v, int scanline) { + const int num_scanlines = 1; RectBlendThreadData *data = (RectBlendThreadData *)data_v; IMB_rectblend(data->dbuf, data->obuf, @@ -999,11 +1000,11 @@ static void rectblend_thread_do(void *data_v, int start_scanline, int num_scanli data->texmask, data->mask_max, data->destx, - data->desty + start_scanline, + data->desty + scanline, data->origx, - data->origy + start_scanline, + data->origy + scanline, data->srcx, - data->srcy + start_scanline, + data->srcy + scanline, data->width, num_scanlines, data->mode, diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 61bc185eb8d..7b4bf704096 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -112,13 +112,13 @@ static bool get_thumb_dir(char *dir, ThumbSize size) #endif switch (size) { case THB_NORMAL: - subdir = "/" THUMBNAILS "/normal/"; + subdir = SEP_STR THUMBNAILS SEP_STR "normal" SEP_STR; break; case THB_LARGE: - subdir = "/" THUMBNAILS "/large/"; + subdir = SEP_STR THUMBNAILS SEP_STR "large" SEP_STR; break; case THB_FAIL: - subdir = "/" THUMBNAILS "/fail/blender/"; + subdir = SEP_STR THUMBNAILS SEP_STR "fail" SEP_STR "blender" SEP_STR; break; default: return 0; /* unknown size */ diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c index b7b31b3e56a..48e7f171bb4 100644 --- a/source/blender/imbuf/intern/thumbs_blend.c +++ b/source/blender/imbuf/intern/thumbs_blend.c @@ -47,7 +47,8 @@ ImBuf *IMB_thumb_load_blend(const char *blen_path, const char *blen_group, const if (blen_group && blen_id) { LinkNode *ln, *names, *lp, *previews = NULL; - struct BlendHandle *libfiledata = BLO_blendhandle_from_file(blen_path, NULL); + struct BlendHandle *libfiledata = BLO_blendhandle_from_file( + blen_path, &(BlendFileReadReport){.reports = NULL}); int idcode = BKE_idtype_idcode_from_name(blen_group); int i, nprevs, nnames; diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index ccf353595c9..e3ed897458d 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -44,6 +44,7 @@ * in the write code for the conventions. */ using Alembic::AbcGeom::kFacevaryingScope; +using Alembic::AbcGeom::kVaryingScope; using Alembic::AbcGeom::kVertexScope; using Alembic::Abc::C4fArraySample; @@ -292,6 +293,7 @@ void write_custom_data(const OCompoundProperty &prop, using Alembic::Abc::C3fArraySamplePtr; using Alembic::Abc::C4fArraySamplePtr; using Alembic::Abc::PropertyHeader; +using Alembic::Abc::UInt32ArraySamplePtr; using Alembic::AbcGeom::IC3fGeomParam; using Alembic::AbcGeom::IC4fGeomParam; @@ -300,21 +302,26 @@ using Alembic::AbcGeom::IV3fGeomParam; static void read_uvs(const CDStreamConfig &config, void *data, + const AbcUvScope uv_scope, const Alembic::AbcGeom::V2fArraySamplePtr &uvs, - const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) + const UInt32ArraySamplePtr &indices) { MPoly *mpolys = config.mpoly; + MLoop *mloops = config.mloop; MLoopUV *mloopuvs = static_cast<MLoopUV *>(data); unsigned int uv_index, loop_index, rev_loop_index; + BLI_assert(uv_scope != ABC_UV_SCOPE_NONE); + const bool do_uvs_per_loop = (uv_scope == ABC_UV_SCOPE_LOOP); + for (int i = 0; i < config.totpoly; i++) { MPoly &poly = mpolys[i]; unsigned int rev_loop_offset = poly.loopstart + poly.totloop - 1; for (int f = 0; f < poly.totloop; f++) { - loop_index = poly.loopstart + f; rev_loop_index = rev_loop_offset - f; + loop_index = do_uvs_per_loop ? poly.loopstart + f : mloops[rev_loop_index].v; uv_index = (*indices)[loop_index]; const Imath::V2f &uv = (*uvs)[uv_index]; @@ -473,20 +480,24 @@ static void read_custom_data_uvs(const ICompoundProperty &prop, IV2fGeomParam::Sample sample; uv_param.getIndexed(sample, iss); - if (uv_param.getScope() != kFacevaryingScope) { + UInt32ArraySamplePtr uvs_indices = sample.getIndices(); + + const AbcUvScope uv_scope = get_uv_scope(uv_param.getScope(), config, uvs_indices); + + if (uv_scope == ABC_UV_SCOPE_NONE) { return; } void *cd_data = config.add_customdata_cb(config.mesh, prop_header.getName().c_str(), CD_MLOOPUV); - read_uvs(config, cd_data, sample.getVals(), sample.getIndices()); + read_uvs(config, cd_data, uv_scope, sample.getVals(), uvs_indices); } void read_generated_coordinates(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss) { - if (prop.getPropertyHeader(propNameOriginalCoordinates) == nullptr) { + if (!prop.valid() || prop.getPropertyHeader(propNameOriginalCoordinates) == nullptr) { /* The ORCO property isn't there, so don't bother trying to process it. */ return; } @@ -559,4 +570,28 @@ void read_custom_data(const std::string &iobject_full_name, } } +/* UVs can be defined per-loop (one value per vertex per face), or per-vertex (one value per + * vertex). The first case is the most common, as this is the standard way of storing this data + * given that some vertices might be on UV seams and have multiple possible UV coordinates; the + * second case can happen when the mesh is split according to the UV islands, in which case storing + * a single UV value per vertex allows to deduplicate data and thus to reduce the file size since + * vertices are guaranteed to only have a single UV coordinate. */ +AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope, + const CDStreamConfig &config, + const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) +{ + if (scope == kFacevaryingScope && indices->size() == config.totloop) { + return ABC_UV_SCOPE_LOOP; + } + + /* kVaryingScope is sometimes used for vertex scopes as the values vary across the vertices. To + * be sure, one has to check the size of the data against the number of vertices, as it could + * also be a varying attribute across the faces (i.e. one value per face). */ + if ((scope == kVaryingScope || scope == kVertexScope) && indices->size() == config.totvert) { + return ABC_UV_SCOPE_VERTEX; + } + + return ABC_UV_SCOPE_NONE; +} + } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index 9ee964c0545..e9736555ead 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -122,4 +122,14 @@ void read_custom_data(const std::string &iobject_full_name, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss); +typedef enum { + ABC_UV_SCOPE_NONE, + ABC_UV_SCOPE_LOOP, + ABC_UV_SCOPE_VERTEX, +} AbcUvScope; + +AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope, + const CDStreamConfig &config, + const Alembic::AbcGeom::UInt32ArraySamplePtr &indices); + } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 11b6c1c18ca..79f34f671c7 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -121,6 +121,7 @@ struct AbcMeshData { P3fArraySamplePtr positions; P3fArraySamplePtr ceil_positions; + AbcUvScope uv_scope; V2fArraySamplePtr uvs; UInt32ArraySamplePtr uvs_indices; }; @@ -192,8 +193,9 @@ static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices; - const bool do_uvs = (mloopuvs && uvs && uvs_indices) && - (uvs_indices->size() == face_indices->size()); + const bool do_uvs = (mloopuvs && uvs && uvs_indices); + const bool do_uvs_per_loop = do_uvs && mesh_data.uv_scope == ABC_UV_SCOPE_LOOP; + BLI_assert(!do_uvs || mesh_data.uv_scope != ABC_UV_SCOPE_NONE); unsigned int loop_index = 0; unsigned int rev_loop_index = 0; unsigned int uv_index = 0; @@ -227,8 +229,7 @@ static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) if (do_uvs) { MLoopUV &loopuv = mloopuvs[rev_loop_index]; - - uv_index = (*uvs_indices)[loop_index]; + uv_index = (*uvs_indices)[do_uvs_per_loop ? loop_index : loop.v]; /* Some Alembic files are broken (or at least export UVs in a way we don't expect). */ if (uv_index >= uvs_size) { @@ -357,22 +358,29 @@ BLI_INLINE void read_uvs_params(CDStreamConfig &config, IV2fGeomParam::Sample uvsamp; uv.getIndexed(uvsamp, selector); - abc_data.uvs = uvsamp.getVals(); - abc_data.uvs_indices = uvsamp.getIndices(); + UInt32ArraySamplePtr uvs_indices = uvsamp.getIndices(); - if (abc_data.uvs_indices->size() == config.totloop) { - std::string name = Alembic::Abc::GetSourceName(uv.getMetaData()); + const AbcUvScope uv_scope = get_uv_scope(uv.getScope(), config, uvs_indices); - /* According to the convention, primary UVs should have had their name - * set using Alembic::Abc::SetSourceName, but you can't expect everyone - * to follow it! :) */ - if (name.empty()) { - name = uv.getName(); - } + if (uv_scope == ABC_UV_SCOPE_NONE) { + return; + } - void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV); - config.mloopuv = static_cast<MLoopUV *>(cd_ptr); + abc_data.uv_scope = uv_scope; + abc_data.uvs = uvsamp.getVals(); + abc_data.uvs_indices = uvs_indices; + + std::string name = Alembic::Abc::GetSourceName(uv.getMetaData()); + + /* According to the convention, primary UVs should have had their name + * set using Alembic::Abc::SetSourceName, but you can't expect everyone + * to follow it! :) */ + if (name.empty()) { + name = uv.getName(); } + + void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV); + config.mloopuv = static_cast<MLoopUV *>(cd_ptr); } static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type) @@ -462,6 +470,7 @@ CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation) config.mvert = mesh->mvert; config.mloop = mesh->mloop; config.mpoly = mesh->mpoly; + config.totvert = mesh->totvert; config.totloop = mesh->totloop; config.totpoly = mesh->totpoly; config.loopdata = &mesh->ldata; diff --git a/source/blender/io/collada/BCMath.cpp b/source/blender/io/collada/BCMath.cpp index 0521fda5fb1..51c86ee53f2 100644 --- a/source/blender/io/collada/BCMath.cpp +++ b/source/blender/io/collada/BCMath.cpp @@ -67,8 +67,9 @@ BCMatrix::BCMatrix(BC_global_forward_axis global_forward_axis, BC_global_up_axis mat3_from_axis_conversion( BC_DEFAULT_FORWARD, BC_DEFAULT_UP, global_forward_axis, global_up_axis, mrot); - transpose_m3( - mrot); /* TODO: Verify that mat3_from_axis_conversion() returns a transposed matrix */ + /* TODO: Verify that `mat3_from_axis_conversion()` returns a transposed matrix */ + transpose_m3(mrot); + copy_m4_m3(mat, mrot); set_transform(mat); } diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc index 0f90855dcb8..3b20ac9f110 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc @@ -288,8 +288,8 @@ void GpencilExporterPDF::export_stroke_to_polyline(bGPDlayer *gpl, } /** - * Set color - * @param do_fill: True if the stroke is only fill + * Set color. + * \param do_fill: True if the stroke is only fill. */ void GpencilExporterPDF::color_set(bGPDlayer *gpl, const bool do_fill) { diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc index c62764cca06..438263167ca 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc @@ -353,8 +353,8 @@ void GpencilExporterSVG::export_stroke_to_polyline(bGPDlayer *gpl, /** * Set color SVG string for stroke - * \param node_gps: Stroke node - * @param do_fill: True if the stroke is only fill + * \param node_gps: Stroke node. + * \param do_fill: True if the stroke is only fill. */ void GpencilExporterSVG::color_string_set(bGPDlayer *gpl, bGPDstroke *gps, diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 583e56de9c2..a5ed870ee78 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -328,11 +328,12 @@ typedef struct bPoseChannel { * and are applied on top of the copies in pchan->bone */ float roll1, roll2; - float curve_in_x, curve_in_y; - float curve_out_x, curve_out_y; + float curve_in_x, curve_in_z; + float curve_out_x, curve_out_z; float ease1, ease2; - float scale_in_x, scale_in_y; - float scale_out_x, scale_out_y; + float scale_in_x DNA_DEPRECATED, scale_in_z DNA_DEPRECATED; + float scale_out_x DNA_DEPRECATED, scale_out_z DNA_DEPRECATED; + float scale_in[3], scale_out[3]; /** B-Bone custom handles; set on read file or rebuild pose based on pchan->bone data. */ struct bPoseChannel *bbone_prev; diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index 85780bc33c5..3d83d0d2f6f 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -86,12 +86,13 @@ typedef struct Bone { /** Curved bones settings - these define the "rest-pose" for a curved bone. */ float roll1, roll2; - float curve_in_x, curve_in_y; - float curve_out_x, curve_out_y; + float curve_in_x, curve_in_z; + float curve_out_x, curve_out_z; /** Length of bezier handles. */ float ease1, ease2; - float scale_in_x, scale_in_y; - float scale_out_x, scale_out_y; + float scale_in_x DNA_DEPRECATED, scale_in_z DNA_DEPRECATED; + float scale_out_x DNA_DEPRECATED, scale_out_z DNA_DEPRECATED; + float scale_in[3], scale_out[3]; /** Patch for upward compatibility, UNUSED! */ float size[3]; @@ -103,6 +104,10 @@ typedef struct Bone { /** Type of next/prev bone handles. */ char bbone_prev_type; char bbone_next_type; + /** B-Bone flags. */ + int bbone_flag; + short bbone_prev_flag; + short bbone_next_flag; /** Next/prev bones to use as handle references when calculating bbones (optional). */ struct Bone *bbone_prev; struct Bone *bbone_next; @@ -259,8 +264,10 @@ typedef enum eBone_Flag { BONE_NO_LOCAL_LOCATION = (1 << 22), /** object child will use relative transform (like deform) */ BONE_RELATIVE_PARENTING = (1 << 23), +#ifdef DNA_DEPRECATED_ALLOW /** it will add the parent end roll to the inroll */ BONE_ADD_PARENT_END_ROLL = (1 << 24), +#endif /** this bone was transformed by the mirror function */ BONE_TRANSFORM_MIRROR = (1 << 25), /** this bone is associated with a locked vertex group, ONLY USE FOR DRAWING */ @@ -291,6 +298,29 @@ typedef enum eBone_BBoneHandleType { BBONE_HANDLE_TANGENT = 3, /* Custom handle in tangent mode (use direction, not location). */ } eBone_BBoneHandleType; +/* bone->bbone_flag */ +typedef enum eBone_BBoneFlag { + /** Add the parent Out roll to the In roll. */ + BBONE_ADD_PARENT_END_ROLL = (1 << 0), + /** Multiply B-Bone easing values with Scale Length. */ + BBONE_SCALE_EASING = (1 << 1), +} eBone_BBoneFlag; + +/* bone->bbone_prev/next_flag */ +typedef enum eBone_BBoneHandleFlag { + /** Use handle bone scaling for scale X. */ + BBONE_HANDLE_SCALE_X = (1 << 0), + /** Use handle bone scaling for scale Y (length). */ + BBONE_HANDLE_SCALE_Y = (1 << 1), + /** Use handle bone scaling for scale Z. */ + BBONE_HANDLE_SCALE_Z = (1 << 2), + /** Use handle bone scaling for easing. */ + BBONE_HANDLE_SCALE_EASE = (1 << 3), + /** Is handle scale required? */ + BBONE_HANDLE_SCALE_ANY = BBONE_HANDLE_SCALE_X | BBONE_HANDLE_SCALE_Y | BBONE_HANDLE_SCALE_Z | + BBONE_HANDLE_SCALE_EASE, +} eBone_BBoneHandleFlag; + #define MAXBONENAME 64 #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index e4f6a1eea41..debf2c3475e 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -43,7 +43,7 @@ typedef struct CurveMapPoint { short flag, shorty; } CurveMapPoint; -/* curvepoint->flag */ +/** #CurveMapPoint.flag */ enum { CUMA_SELECT = (1 << 0), CUMA_HANDLE_VECTOR = (1 << 1), @@ -95,19 +95,18 @@ typedef struct CurveMapping { char _pad[6]; } CurveMapping; -/* CurveMapping.flag */ +/** #CurveMapping.flag */ typedef enum eCurveMappingFlags { CUMA_DO_CLIP = (1 << 0), CUMA_PREMULLED = (1 << 1), CUMA_DRAW_CFRA = (1 << 2), CUMA_DRAW_SAMPLE = (1 << 3), - /* The curve is extended by extrapolation. When not set the curve is extended - * Horizontally */ + /** The curve is extended by extrapolation. When not set the curve is extended horizontally. */ CUMA_EXTEND_EXTRAPOLATE = (1 << 4), } eCurveMappingFlags; -/* cumapping->preset */ +/** #CurveMapping.preset */ typedef enum eCurveMappingPreset { CURVE_PRESET_LINE = 0, CURVE_PRESET_SHARP = 1, @@ -120,13 +119,13 @@ typedef enum eCurveMappingPreset { CURVE_PRESET_BELL = 8, } eCurveMappingPreset; -/* CurveMapping->tone */ +/** #CurveMapping.tone */ typedef enum eCurveMappingTone { CURVE_TONE_STANDARD = 0, CURVE_TONE_FILMLIKE = 2, } eCurveMappingTone; -/* histogram->mode */ +/** #Histogram.mode */ enum { HISTO_MODE_LUMA = 0, HISTO_MODE_RGB = 1, @@ -154,8 +153,7 @@ typedef struct Histogram { short flag; int height; - /* sample line only */ - /* image coords src -> dst */ + /** Sample line only (image coords: source -> destination). */ float co[2][2]; } Histogram; @@ -180,13 +178,15 @@ typedef struct Scopes { char _pad[4]; } Scopes; -/* scopes->wavefrm_mode */ -#define SCOPES_WAVEFRM_LUMA 0 -#define SCOPES_WAVEFRM_RGB_PARADE 1 -#define SCOPES_WAVEFRM_YCC_601 2 -#define SCOPES_WAVEFRM_YCC_709 3 -#define SCOPES_WAVEFRM_YCC_JPEG 4 -#define SCOPES_WAVEFRM_RGB 5 +/** #Scopes.wavefrm_mode */ +enum { + SCOPES_WAVEFRM_LUMA = 0, + SCOPES_WAVEFRM_RGB_PARADE = 1, + SCOPES_WAVEFRM_YCC_601 = 2, + SCOPES_WAVEFRM_YCC_709 = 3, + SCOPES_WAVEFRM_YCC_JPEG = 4, + SCOPES_WAVEFRM_RGB = 5, +}; typedef struct ColorManagedViewSettings { int flag; @@ -214,7 +214,7 @@ typedef struct ColorManagedColorspaceSettings { char name[64]; } ColorManagedColorspaceSettings; -/* ColorManagedViewSettings->flag */ +/** #ColorManagedViewSettings.flag */ enum { COLORMANAGE_VIEW_USE_CURVES = (1 << 0), }; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index e3b5ecfac04..e5282409329 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -72,7 +72,7 @@ typedef struct CustomData { CustomDataLayer *layers; /** * runtime only! - maps types to indices of first layer of that type, - * MUST be >= CD_NUMTYPES, but we cant use a define here. + * MUST be >= CD_NUMTYPES, but we can't use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ int typemap[51]; diff --git a/source/blender/makesdna/DNA_dynamicpaint_types.h b/source/blender/makesdna/DNA_dynamicpaint_types.h index e3dcd283efa..5f3c54a0482 100644 --- a/source/blender/makesdna/DNA_dynamicpaint_types.h +++ b/source/blender/makesdna/DNA_dynamicpaint_types.h @@ -156,7 +156,7 @@ typedef struct DynamicPaintSurface { /* canvas flags */ enum { - /** surface is already baking, so it wont get updated (loop) */ + /** surface is already baking, so it won't get updated (loop) */ MOD_DPAINT_BAKING = 1 << 1, }; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 410212ce100..3977ad326da 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -878,6 +878,7 @@ typedef enum eLineArtGPencilModifierFlags { LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 1), LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */, LRT_GPENCIL_IS_BAKED = (1 << 3), + LRT_GPENCIL_USE_CACHE = (1 << 4), } eLineArtGPencilModifierFlags; typedef enum eLineartGpencilTransparencyFlags { @@ -886,6 +887,8 @@ typedef enum eLineartGpencilTransparencyFlags { LRT_GPENCIL_TRANSPARENCY_MATCH = (1 << 1), } eLineartGpencilTransparencyFlags; +struct LineartCache; + typedef struct LineartGpencilModifierData { GpencilModifierData modifier; @@ -925,16 +928,24 @@ typedef struct LineartGpencilModifierData { /* CPU mode */ float chaining_image_threshold; - int _pad; - /* Ported from SceneLineArt flags. */ int calculation_flags; /* Additional Switches. */ int flags; - /* Runtime only. */ - void *render_buffer; + /* Runtime data. */ + + /* Because we can potentially only compute features lines once per modifier stack (Use Cache), we + * need to have these override values to ensure that we have the data we need is computed and + * stored in the cache. */ + char level_start_override; + char level_end_override; + short edge_types_override; + + struct LineartCache *cache; + /* Keep a pointer to the render buffer so we can call destroy from ModifierData. */ + struct LineartRenderBuffer *render_buffer_ptr; } LineartGpencilModifierData; diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index ea3c1ff7275..6b7b89e91fa 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -629,6 +629,7 @@ typedef struct bGPdata_Runtime { /** Brush pointer */ Brush *sbuffer_brush; struct GpencilBatchCache *gpencil_cache; + struct LineartCache *lineart_cache; } bGPdata_Runtime; /* grid configuration */ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 3eb5920dfe6..566173aac0f 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -276,7 +276,7 @@ enum { #define ME_USING_MIRROR_X_VERTEX_GROUPS(_me) \ (((_me)->editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) && ((_me)->symmetry & ME_SYMMETRY_X)) -/* We cant have both flags enabled at once, +/* We can't have both flags enabled at once, * flags defined in DNA_scene_types.h */ #define ME_EDIT_PAINT_SEL_MODE(_me) \ (((_me)->editflag & ME_EDIT_PAINT_FACE_SEL) ? \ @@ -296,7 +296,7 @@ enum { ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8, ME_DS_EXPAND = 1 << 9, ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10, - ME_REMESH_SMOOTH_NORMALS = 1 << 11, + ME_FLAG_UNUSED_8 = 1 << 11, /* cleared */ ME_REMESH_REPROJECT_PAINT_MASK = 1 << 12, ME_REMESH_FIX_POLES = 1 << 13, ME_REMESH_REPROJECT_VOLUME = 1 << 14, diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 05be31262a6..346e65f0fa1 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -130,7 +130,7 @@ typedef struct MLoop { /** * Optionally store the order of selected elements. - * This wont always be set since only some selection operations have an order. + * This won't always be set since only some selection operations have an order. * * Typically accessed from #Mesh.mselect */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 6c1a141448f..186d5cc9884 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1367,6 +1367,16 @@ typedef struct NodeGeometryCurveDeform { uint8_t axis; } NodeGeometryCurveDeform; +typedef struct NodeGeometryCurveSubdivide { + /* GeometryNodeAttributeInputMode (integer or attribute). */ + uint8_t cuts_type; +} NodeGeometryCurveSubdivide; + +typedef struct NodeGeometryCurveToPoints { + /* GeometryNodeCurveSampleMode. */ + uint8_t mode; +} NodeGeometryCurveToPoints; + typedef struct NodeGeometryAttributeTransfer { /* AttributeDomain. */ int8_t domain; @@ -1374,6 +1384,15 @@ typedef struct NodeGeometryAttributeTransfer { uint8_t mapping; } NodeGeometryAttributeTransfer; +typedef struct NodeGeometryRaycast { + /* GeometryNodeRaycastMapMode. */ + uint8_t mapping; + + uint8_t input_type_ray_direction; + uint8_t input_type_ray_length; + char _pad[1]; +} NodeGeometryRaycast; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1878,6 +1897,7 @@ typedef enum GeometryNodeMeshLineCountMode { typedef enum GeometryNodeCurveSampleMode { GEO_NODE_CURVE_SAMPLE_COUNT = 0, GEO_NODE_CURVE_SAMPLE_LENGTH = 1, + GEO_NODE_CURVE_SAMPLE_EVALUATED = 2, } GeometryNodeCurveSampleMode; typedef enum GeometryNodeAttributeTransferMapMode { @@ -1894,6 +1914,11 @@ typedef enum GeometryNodeCurveDeformAxis { GEO_NODE_CURVE_DEFORM_NEGZ = 5, } GeometryNodeCurveDeformAxis; +typedef enum GeometryNodeRaycastMapMode { + GEO_NODE_RAYCAST_INTERPOLATED = 0, + GEO_NODE_RAYCAST_NEAREST = 1, +} GeometryNodeRaycastMapMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 7e0bf81457d..59b153397c1 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -45,6 +45,7 @@ struct MovieClip; struct Scene; struct VFont; struct bSound; +struct SequenceLookup; /* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */ @@ -166,8 +167,9 @@ typedef struct Sequence { * frames that use the last frame after data ends. */ int startstill, endstill; - /** Machine: the strip channel, depth the depth in the sequence when dealing with metastrips. */ - int machine, depth; + /** Machine: the strip channel */ + int machine; + int _pad3; /** Starting and ending points of the strip in the sequence. */ int startdisp, enddisp; float sat; @@ -257,6 +259,10 @@ typedef struct MetaStack { int disp_range[2]; } MetaStack; +typedef struct EditingRuntime { + struct SequenceLookup *sequence_lookup; +} EditingRuntime; + typedef struct Editing { /** Pointer to the current list of seq's being edited (can be within a meta strip). */ ListBase *seqbasep; @@ -287,6 +293,8 @@ typedef struct Editing { /* Must be initialized only by seq_cache_create() */ int64_t disk_cache_timestamp; + + EditingRuntime runtime; } Editing; /* ************* Effect Variable Structs ********* */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 9f3576a2cbe..7804ece9769 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -780,8 +780,16 @@ typedef struct FileAssetSelectParams { FileSelectParams base_params; FileSelectAssetLibraryUID asset_library; + + short import_type; /* eFileAssetImportType */ + char _pad[6]; } FileAssetSelectParams; +typedef enum eFileAssetImportType { + FILE_ASSET_IMPORT_LINK = 0, + FILE_ASSET_IMPORT_APPEND = 1, +} eFileAssetImportType; + /** * A wrapper to store previous and next folder lists (#FolderList) for a specific browse mode * (#eFileBrowse_Mode). @@ -1866,6 +1874,19 @@ typedef struct SpreadsheetColumn { * #SpreadsheetColumnID in the future for different kinds of ids. */ SpreadsheetColumnID *id; + + /** + * An indicator of the type of values in the column, set at runtime. + * #eSpreadsheetColumnValueType. + */ + uint8_t data_type; + char _pad0[7]; + + /** + * The final column name generated by the data source, also just + * cached at runtime when the data source columns are generated. + */ + char *display_name; } SpreadsheetColumn; /** @@ -1906,6 +1927,9 @@ typedef struct SpaceSpreadsheet { /* List of #SpreadsheetColumn. */ ListBase columns; + /* SpreadsheetRowFilter. */ + ListBase row_filters; + /** * List of #SpreadsheetContext. * This is a path to the data that is displayed in the spreadsheet. @@ -1937,8 +1961,44 @@ typedef enum eSpaceSpreadsheet_Flag { typedef enum eSpaceSpreadsheet_FilterFlag { SPREADSHEET_FILTER_SELECTED_ONLY = (1 << 0), + SPREADSHEET_FILTER_ENABLE = (1 << 1), } eSpaceSpreadsheet_FilterFlag; +typedef struct SpreadsheetRowFilter { + struct SpreadsheetRowFilter *next, *prev; + + char column_name[64]; /* MAX_NAME. */ + + /* eSpreadsheetFilterOperation. */ + uint8_t operation; + /* eSpaceSpreadsheet_RowFilterFlag. */ + uint8_t flag; + + char _pad0[2]; + + int value_int; + char *value_string; + float value_float; + float threshold; + float value_float2[2]; + float value_float3[3]; + float value_color[4]; + + char _pad1[4]; +} SpreadsheetRowFilter; + +typedef enum eSpaceSpreadsheet_RowFilterFlag { + SPREADSHEET_ROW_FILTER_UI_EXPAND = (1 << 0), + SPREADSHEET_ROW_FILTER_BOOL_VALUE = (1 << 1), + SPREADSHEET_ROW_FILTER_ENABLED = (1 << 2), +} eSpaceSpreadsheet_RowFilterFlag; + +typedef enum eSpreadsheetFilterOperation { + SPREADSHEET_ROW_FILTER_EQUAL = 0, + SPREADSHEET_ROW_FILTER_GREATER = 1, + SPREADSHEET_ROW_FILTER_LESS = 2, +} eSpreadsheetFilterOperation; + typedef enum eSpaceSpreadsheet_ObjectEvalState { SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED = 0, SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL = 1, @@ -1950,6 +2010,16 @@ typedef enum eSpaceSpreadsheet_ContextType { SPREADSHEET_CONTEXT_NODE = 2, } eSpaceSpreadsheet_ContextType; +typedef enum eSpreadsheetColumnValueType { + SPREADSHEET_VALUE_TYPE_BOOL = 0, + SPREADSHEET_VALUE_TYPE_INT32 = 1, + SPREADSHEET_VALUE_TYPE_FLOAT = 2, + SPREADSHEET_VALUE_TYPE_FLOAT2 = 3, + SPREADSHEET_VALUE_TYPE_FLOAT3 = 4, + SPREADSHEET_VALUE_TYPE_COLOR = 5, + SPREADSHEET_VALUE_TYPE_INSTANCES = 6, +} eSpreadsheetColumnValueType; + /** * We can't just use UI_UNIT_X, because it does not take `widget.points` into account, which * modifies the width of text as well. diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 9e7e30d913e..3b35b527584 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -72,7 +72,6 @@ typedef struct RegionView3D { /** Allocated backup of its self while in localview. */ struct RegionView3D *localvd; struct RenderEngine *render_engine; - struct ViewDepths *depths; /** Animated smooth view. */ struct SmoothView3DStore *sms; @@ -102,7 +101,7 @@ typedef struct RegionView3D { /** Viewport zoom on the camera frame, see BKE_screen_view3d_zoom_to_fac. */ float camzoom; /** - * Check if persp/ortho view, since 'persp' cant be used for this since + * Check if persp/ortho view, since 'persp' can't be used for this since * it can have cameras assigned as well. (only set in #view3d_winmatrix_set) */ char is_persp; @@ -258,6 +257,8 @@ typedef struct View3D_Runtime { int flag; char _pad1[4]; + /* Only used for overlay stats while in localview. */ + struct SceneStats *local_stats; } View3D_Runtime; /** 3D ViewPort Struct. */ @@ -515,7 +516,6 @@ enum { V3D_OVERLAY_HIDE_OBJECT_ORIGINS = (1 << 10), V3D_OVERLAY_STATS = (1 << 11), V3D_OVERLAY_FADE_INACTIVE = (1 << 12), - V3D_OVERLAY_MODE_TRANSFER = (1 << 13), }; /** #View3DOverlay.edit_flag */ diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 6c88d5f7230..2a4bf53702f 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -855,7 +855,7 @@ static void cast_pointer_64_to_32(const int array_len, uint32_t *new_data) { /* WARNING: 32-bit Blender trying to load file saved by 64-bit Blender, - * pointers may lose uniqueness on truncation! (Hopefully this wont + * pointers may lose uniqueness on truncation! (Hopefully this won't * happen unless/until we ever get to multi-gigabyte .blend files...) */ for (int a = 0; a < array_len; a++) { new_data[a] = old_data[a] >> 3; @@ -1564,9 +1564,8 @@ DNA_ReconstructInfo *DNA_reconstruct_info_create(const SDNA *oldsdna, ReconstructStep *steps = create_reconstruct_steps_for_struct( oldsdna, newsdna, compare_flags, old_struct, new_struct); - int steps_len = new_struct->members_len; /* Comment the line below to skip the compression for debugging purposes. */ - steps_len = compress_reconstruct_steps(steps, new_struct->members_len); + const int steps_len = compress_reconstruct_steps(steps, new_struct->members_len); reconstruct_info->steps[new_struct_nr] = steps; reconstruct_info->step_counts[new_struct_nr] = steps_len; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 34413730eb3..84120a54da4 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -59,11 +59,13 @@ DNA_STRUCT_RENAME(SpaceOops, SpaceOutliner) DNA_STRUCT_RENAME_ELEM(BPoint, alfa, tilt) DNA_STRUCT_RENAME_ELEM(BezTriple, alfa, tilt) DNA_STRUCT_RENAME_ELEM(Bone, curveInX, curve_in_x) -DNA_STRUCT_RENAME_ELEM(Bone, curveInY, curve_in_y) +DNA_STRUCT_RENAME_ELEM(Bone, curveInY, curve_in_z) DNA_STRUCT_RENAME_ELEM(Bone, curveOutX, curve_out_x) -DNA_STRUCT_RENAME_ELEM(Bone, curveOutY, curve_out_y) +DNA_STRUCT_RENAME_ELEM(Bone, curveOutY, curve_out_z) DNA_STRUCT_RENAME_ELEM(Bone, scaleIn, scale_in_x) +DNA_STRUCT_RENAME_ELEM(Bone, scale_in_y, scale_in_z) DNA_STRUCT_RENAME_ELEM(Bone, scaleOut, scale_out_x) +DNA_STRUCT_RENAME_ELEM(Bone, scale_out_y, scale_out_z) DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_f, hardeness) DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_s, aspect_ratio) DNA_STRUCT_RENAME_ELEM(Camera, YF_dofdist, dof_distance) @@ -101,11 +103,13 @@ DNA_STRUCT_RENAME_ELEM(View3D, ob_centre_cursor, ob_center_cursor) DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_f, hardeness) DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_s, aspect_ratio) DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveInX, curve_in_x) -DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveInY, curve_in_y) +DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveInY, curve_in_z) DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveOutX, curve_out_x) -DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveOutY, curve_out_y) +DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveOutY, curve_out_z) DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleIn, scale_in_x) +DNA_STRUCT_RENAME_ELEM(bPoseChannel, scale_in_y, scale_in_z) DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleOut, scale_out_x) +DNA_STRUCT_RENAME_ELEM(bPoseChannel, scale_out_y, scale_out_z) DNA_STRUCT_RENAME_ELEM(bSameVolumeConstraint, flag, free_axis) DNA_STRUCT_RENAME_ELEM(bSound, name, filepath) DNA_STRUCT_RENAME_ELEM(bTheme, tact, space_action) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 85bcc94c335..e6b4ae97355 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -361,7 +361,7 @@ static int add_type(const char *str, int size) } if (strchr(str, '*')) { /* note: this is valid C syntax but we can't parse, complain! - * `struct SomeStruct* some_var;` <-- correct but we cant handle right now. */ + * `struct SomeStruct* some_var;` <-- correct but we can't handle right now. */ return -1; } @@ -725,7 +725,7 @@ static int convert_include(const char *filename) const int strct = add_type(md1, 0); if (strct == -1) { - fprintf(stderr, "File '%s' contains struct we cant parse \"%s\"\n", filename, md1); + fprintf(stderr, "File '%s' contains struct we can't parse \"%s\"\n", filename, md1); return 1; } diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 379e3e77b6e..c691eb3b534 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -615,10 +615,12 @@ extern StructRNA RNA_Spline; extern StructRNA RNA_SplineIKConstraint; extern StructRNA RNA_SplinePoint; extern StructRNA RNA_SpotLight; +extern StructRNA RNA_SpreadsheetColumnID; extern StructRNA RNA_SpreadsheetContext; extern StructRNA RNA_SpreadsheetContextObject; extern StructRNA RNA_SpreadsheetContextModifier; extern StructRNA RNA_SpreadsheetContextNode; +extern StructRNA RNA_SpreadsheetRowFilter; extern StructRNA RNA_Stereo3dDisplay; extern StructRNA RNA_StretchToConstraint; extern StructRNA RNA_StringAttribute; diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 8e1cfafcc42..17396a6a000 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -255,7 +255,7 @@ typedef enum PropertyFlag { * Currently only used for UI, this is similar to PROP_NEVER_NULL * except that the value may be NULL at times, used for ObData, where an Empty's will be NULL * but setting NULL on a mesh object is not possible. - * So, if its not NULL, setting NULL cant be done! + * So if it's not NULL, setting NULL can't be done! */ PROP_NEVER_UNLINK = (1 << 25), diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 7a9cfa79324..a75921859cb 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4229,7 +4229,7 @@ static void rna_generate_struct(BlenderRNA *UNUSED(brna), StructRNA *srna, FILE } fprintf(f, "\t"); rna_print_c_string(f, srna->identifier); - fprintf(f, ", NULL, NULL"); /* PyType - Cant initialize here */ + fprintf(f, ", NULL, NULL"); /* PyType - Can't initialize here */ fprintf(f, ", %d, NULL, ", srna->flag); rna_print_c_string(f, srna->name); fprintf(f, ",\n\t"); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 948fef1b51e..edcfcd130f7 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -226,7 +226,7 @@ void RNA_pointer_recast(PointerRNA *ptr, PointerRNA *r_ptr) { StructRNA *base; PointerRNA t_ptr; - *r_ptr = *ptr; /* initialize as the same in case cant recast */ + *r_ptr = *ptr; /* initialize as the same in case can't recast */ for (base = ptr->type->base; base; base = base->base) { t_ptr = rna_pointer_inherit_refine(ptr, base, ptr->data); @@ -6869,7 +6869,7 @@ char *RNA_pointer_as_string_keywords_ex(bContext *C, if (as_function && RNA_property_type(prop) == PROP_POINTER) { /* don't expand pointers for functions */ if (flag & PROP_NEVER_NULL) { - /* we cant really do the right thing here. arg=arg?, hrmf! */ + /* we can't really do the right thing here. arg=arg?, hrmf! */ buf = BLI_strdup(arg_name); } else { @@ -8184,7 +8184,7 @@ void _RNA_warning(const char *format, ...) vprintf(format, args); va_end(args); - /* gcc macro adds '\n', but cant use for other compilers */ + /* gcc macro adds '\n', but can't use for other compilers */ #ifndef __GNUC__ fputc('\n', stdout); #endif diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index c8fccfc27f8..f07aae0bb15 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -720,7 +720,7 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb prop = RNA_def_property(srna, "use_endroll_as_inroll", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text( prop, "Inherit End Roll", "Add Roll Out of the Start Handle bone to the Roll In value"); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_ADD_PARENT_END_ROLL); + RNA_def_property_boolean_sdna(prop, NULL, "bbone_flag", BBONE_ADD_PARENT_END_ROLL); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_dependency_update"); } @@ -733,11 +733,11 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb prop, "In X", "X-axis handle offset for start of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); - prop = RNA_def_property(srna, "bbone_curveiny", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "curve_in_y"); + prop = RNA_def_property(srna, "bbone_curveinz", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curve_in_z"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text( - prop, "In Y", "Y-axis handle offset for start of the B-Bone's curve, adjusts curvature"); + prop, "In Z", "Z-axis handle offset for start of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); prop = RNA_def_property(srna, "bbone_curveoutx", PROP_FLOAT, PROP_NONE); @@ -747,11 +747,11 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb prop, "Out X", "X-axis handle offset for end of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); - prop = RNA_def_property(srna, "bbone_curveouty", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "curve_out_y"); + prop = RNA_def_property(srna, "bbone_curveoutz", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curve_out_z"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text( - prop, "Out Y", "Y-axis handle offset for end of the B-Bone's curve, adjusts curvature"); + prop, "Out Z", "Z-axis handle offset for end of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); /* Ease In/Out */ @@ -769,49 +769,37 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb RNA_def_property_ui_text(prop, "Ease Out", "Length of second Bezier Handle (for B-Bones only)"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); - /* Scale In/Out */ - prop = RNA_def_property(srna, "bbone_scaleinx", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "scale_in_x"); - RNA_def_property_flag(prop, PROP_PROPORTIONAL); - RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 3); - RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_ui_text(prop, - "Scale In X", - "X-axis scale factor for start of the B-Bone, " - "adjusts thickness (for tapering effects)"); - RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); - - prop = RNA_def_property(srna, "bbone_scaleiny", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "scale_in_y"); - RNA_def_property_flag(prop, PROP_PROPORTIONAL); - RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 3); - RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_ui_text(prop, - "Scale In Y", - "Y-axis scale factor for start of the B-Bone, " - "adjusts thickness (for tapering effects)"); - RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); + if (is_posebone == false) { + prop = RNA_def_property(srna, "use_scale_easing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, "Scale Easing", "Multiply the final easing values by the Scale In/Out Y factors"); + RNA_def_property_boolean_sdna(prop, NULL, "bbone_flag", BBONE_SCALE_EASING); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); + } - prop = RNA_def_property(srna, "bbone_scaleoutx", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "scale_out_x"); + /* Scale In/Out */ + prop = RNA_def_property(srna, "bbone_scalein", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "scale_in"); + RNA_def_property_array(prop, 3); RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 3); - RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_ui_text(prop, - "Scale Out X", - "X-axis scale factor for end of the B-Bone, " - "adjusts thickness (for tapering effects)"); + RNA_def_property_float_array_default(prop, rna_default_scale_3d); + RNA_def_property_ui_text( + prop, + "Scale In", + "Scale factors for the start of the B-Bone, adjusts thickness (for tapering effects)"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); - prop = RNA_def_property(srna, "bbone_scaleouty", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "scale_out_y"); + prop = RNA_def_property(srna, "bbone_scaleout", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "scale_out"); + RNA_def_property_array(prop, 3); RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 3); - RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_ui_text(prop, - "Scale Out Y", - "Y-axis scale factor for end of the B-Bone, " - "adjusts thickness (for tapering effects)"); + RNA_def_property_float_array_default(prop, rna_default_scale_3d); + RNA_def_property_ui_text( + prop, + "Scale Out", + "Scale factors for the end of the B-Bone, adjusts thickness (for tapering effects)"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); # undef RNA_DEF_CURVEBONE_UPDATE @@ -1072,6 +1060,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_ui_range(prop, 0.0f, 1000.0f, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text(prop, "B-Bone Display Z Width", "B-Bone Z size"); + /* B-Bone Start Handle settings. */ prop = RNA_def_property(srna, "bbone_handle_type_start", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "bbone_prev_type"); RNA_def_property_enum_items(prop, prop_bbone_handle_type); @@ -1096,6 +1085,26 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_ui_text( prop, "B-Bone Start Handle", "Bone that serves as the start handle for the B-Bone curve"); + prop = RNA_def_property(srna, "bbone_handle_use_scale_start", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Start Handle Scale", + "Multiply B-Bone Scale In channels by the local scale values of the start handle. " + "This is done after the Scale Easing option and isn't affected by it"); + RNA_def_property_boolean_sdna(prop, NULL, "bbone_prev_flag", BBONE_HANDLE_SCALE_X); + RNA_def_property_array(prop, 3); + RNA_def_property_update(prop, 0, "rna_Armature_update_data"); + + prop = RNA_def_property(srna, "bbone_handle_use_ease_start", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Start Handle Ease", + "Multiply the B-Bone Ease In channel by the local Y scale value of the start handle. " + "This is done after the Scale Easing option and isn't affected by it"); + RNA_def_property_boolean_sdna(prop, NULL, "bbone_prev_flag", BBONE_HANDLE_SCALE_EASE); + RNA_def_property_update(prop, 0, "rna_Armature_update_data"); + + /* B-Bone End Handle settings. */ prop = RNA_def_property(srna, "bbone_handle_type_end", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "bbone_next_type"); RNA_def_property_enum_items(prop, prop_bbone_handle_type); @@ -1120,6 +1129,25 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_ui_text( prop, "B-Bone End Handle", "Bone that serves as the end handle for the B-Bone curve"); + prop = RNA_def_property(srna, "bbone_handle_use_scale_end", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "End Handle Scale", + "Multiply B-Bone Scale Out channels by the local scale values of the end handle. " + "This is done after the Scale Easing option and isn't affected by it"); + RNA_def_property_boolean_sdna(prop, NULL, "bbone_next_flag", BBONE_HANDLE_SCALE_X); + RNA_def_property_array(prop, 3); + RNA_def_property_update(prop, 0, "rna_Armature_update_data"); + + prop = RNA_def_property(srna, "bbone_handle_use_ease_end", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "End Handle Ease", + "Multiply the B-Bone Ease Out channel by the local Y scale value of the end handle. " + "This is done after the Scale Easing option and isn't affected by it"); + RNA_def_property_boolean_sdna(prop, NULL, "bbone_next_flag", BBONE_HANDLE_SCALE_EASE); + RNA_def_property_update(prop, 0, "rna_Armature_update_data"); + RNA_define_lib_overridable(false); } diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index dd7d50a80ba..2a4cd1d934a 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -183,7 +183,7 @@ static const EnumPropertyItem curve2d_fill_mode_items[] = { # include "ED_curve.h" /* for BKE_curve_nurbs_get */ -/* highly irritating but from RNA we cant know this */ +/* highly irritating but from RNA we can't know this */ static Nurb *curve_nurb_from_point(Curve *cu, const void *point, int *nu_index, int *pt_index) { ListBase *nurbs = BKE_curve_nurbs_get(cu); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 9b9d561603b..6b0a2b324c3 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -783,7 +783,7 @@ void RNA_struct_free_extension(StructRNA *srna, ExtensionRNA *rna_ext) rna_ext->free(rna_ext->data); /* decref's the PyObject that the srna owns */ RNA_struct_blender_type_set(srna, NULL); /* this gets accessed again - XXX fixme */ - /* NULL the srna's value so RNA_struct_free wont complain of a leak */ + /* NULL the srna's value so RNA_struct_free won't complain of a leak */ RNA_struct_py_type_set(srna, NULL); #else @@ -1053,7 +1053,7 @@ StructRNA *RNA_def_struct(BlenderRNA *brna, const char *identifier, const char * if (from) { /* find struct to derive from */ - /* Inline RNA_struct_find(...) because it wont link from here. */ + /* Inline RNA_struct_find(...) because it won't link from here. */ srnafrom = BLI_ghash_lookup(brna->structs_map, from); if (!srnafrom) { CLOG_ERROR(&LOG, "struct %s not found to define %s.", from, identifier); diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 1b89d866aba..399f45a2382 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -543,8 +543,8 @@ static void rna_FCurve_group_set(PointerRNA *ptr, return; } else if (value.data && (pid != vid)) { - /* id's differ, cant do this, should raise an error */ - printf("ERROR: ID's differ - ptr=%p vs value=%p\n", pid, vid); + /* ids differ, can't do this, should raise an error */ + printf("ERROR: IDs differ - ptr=%p vs value=%p\n", pid, vid); return; } diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 92d65961743..e2cee2836b1 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2930,6 +2930,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Is Baked", "This modifier has baked data"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_cache", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_USE_CACHE); + RNA_def_property_ui_text(prop, + "Use Cache", + "Use cached scene data from the first line art modifier in the stack. " + "Certain settings will be unavailable"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE); RNA_def_property_ui_text(prop, "Thickness", "The thickness for the generated strokes"); RNA_def_property_ui_range(prop, 1, 100, 1, 1); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index b3272f44826..c058ab6cfcc 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -1092,7 +1092,7 @@ static void rna_def_image(BlenderRNA *brna) 0, 0, "Size", - "Width and height in pixels, zero when image data cant be loaded", + "Width and height in pixels, zero when image data can't be loaded", 0, 0); RNA_def_property_subtype(prop, PROP_PIXEL); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index d5a1047d287..8e475f43e1d 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -82,7 +82,8 @@ static const EnumPropertyItem rna_enum_mesh_remesh_mode_items[] = { # include "rna_mesh_utils.h" /* -------------------------------------------------------------------- */ -/* Generic helpers */ +/** \name Generic Helpers + * \{ */ static Mesh *rna_mesh(PointerRNA *ptr) { @@ -139,8 +140,11 @@ static CustomData *rna_mesh_ldata(PointerRNA *ptr) return rna_mesh_ldata_helper(me); } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Generic CustomData Layer Functions */ +/** \name Generic CustomData Layer Functions + * \{ */ static void rna_cd_layer_name_set(CustomData *cdata, CustomDataLayer *cdl, const char *value) { @@ -205,49 +209,67 @@ static bool rna_Mesh_has_custom_normals_get(PointerRNA *ptr) return BKE_mesh_has_custom_loop_normals(me); } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Update Callbacks */ +/** \name Update Callbacks + * + * \note Skipping meshes without users is a simple way to avoid updates on newly created meshes. + * This speeds up importers that manipulate mesh data before linking it to an object & collection. + * + * \{ */ -static void rna_Mesh_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +/** + * \warning This calls `DEG_id_tag_update(id, 0)` which is something that should be phased out + * (see #deg_graph_node_tag_zero), for now it's kept since changes to updates must be carefully + * tested to make sure there aren't any regressions. + * + * This function should be replaced with more specific update flags where possible. + */ +static void rna_Mesh_update_data_legacy_deg_tag_all(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) { ID *id = ptr->owner_id; - - /* cheating way for importers to avoid slow updates */ - if (id->us > 0) { - DEG_id_tag_update(id, 0); - WM_main_add_notifier(NC_GEOM | ND_DATA, id); + if (id->us <= 0) { /* See note in section heading. */ + return; } + + DEG_id_tag_update(id, 0); + WM_main_add_notifier(NC_GEOM | ND_DATA, id); } static void rna_Mesh_update_data_edit_weight(Main *bmain, Scene *scene, PointerRNA *ptr) { BKE_mesh_batch_cache_dirty_tag(rna_mesh(ptr), BKE_MESH_BATCH_DIRTY_ALL); - rna_Mesh_update_data(bmain, scene, ptr); + rna_Mesh_update_data_legacy_deg_tag_all(bmain, scene, ptr); } static void rna_Mesh_update_data_edit_active_color(Main *bmain, Scene *scene, PointerRNA *ptr) { BKE_mesh_batch_cache_dirty_tag(rna_mesh(ptr), BKE_MESH_BATCH_DIRTY_ALL); - rna_Mesh_update_data(bmain, scene, ptr); + rna_Mesh_update_data_legacy_deg_tag_all(bmain, scene, ptr); } static void rna_Mesh_update_select(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { ID *id = ptr->owner_id; - /* cheating way for importers to avoid slow updates */ - if (id->us > 0) { - WM_main_add_notifier(NC_GEOM | ND_SELECT, id); + if (id->us <= 0) { /* See note in section heading. */ + return; } + + WM_main_add_notifier(NC_GEOM | ND_SELECT, id); } void rna_Mesh_update_draw(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { ID *id = ptr->owner_id; - /* cheating way for importers to avoid slow updates */ - if (id->us > 0) { - WM_main_add_notifier(NC_GEOM | ND_DATA, id); + if (id->us <= 0) { /* See note in section heading. */ + return; } + + WM_main_add_notifier(NC_GEOM | ND_DATA, id); } static void rna_Mesh_update_vertmask(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -274,8 +296,11 @@ static void rna_Mesh_update_facemask(Main *bmain, Scene *scene, PointerRNA *ptr) rna_Mesh_update_draw(bmain, scene, ptr); } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Property get/set Callbacks */ +/** \name Property get/set Callbacks + * \{ */ static void rna_MeshVertex_normal_get(PointerRNA *ptr, float *value) { @@ -1047,7 +1072,7 @@ static int rna_MeshPoly_vertices_get_length(PointerRNA *ptr, int length[RNA_MAX_ { MPoly *mp = (MPoly *)ptr->data; /* note, raw access uses dummy item, this _could_ crash, - * watch out for this, mface uses it but it cant work here. */ + * watch out for this, mface uses it but it can't work here. */ return (length[0] = mp->totloop); } @@ -1599,8 +1624,14 @@ static void UNUSED_FUNCTION(rna_mesh_unused)(void) /* end unused function block */ } +/** \} */ + #else +/* -------------------------------------------------------------------- */ +/** \name RNA Mesh Definition + * \{ */ + static void rna_def_mvert_group(BlenderRNA *brna) { StructRNA *srna; @@ -1619,7 +1650,7 @@ static void rna_def_mvert_group(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "def_nr"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Group Index", ""); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -1640,7 +1671,7 @@ static void rna_def_mvert(BlenderRNA *brna) prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_ui_text(prop, "Location", ""); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_DIRECTION); /* RNA_def_property_float_sdna(prop, NULL, "no"); */ @@ -1665,7 +1696,7 @@ static void rna_def_mvert(BlenderRNA *brna) prop, "rna_MeshVertex_bevel_weight_get", "rna_MeshVertex_bevel_weight_set", NULL); RNA_def_property_ui_text( prop, "Bevel Weight", "Weight used by the Bevel modifier 'Only Vertices' option"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "groups", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_funcs(prop, @@ -1718,13 +1749,13 @@ static void rna_def_medge(BlenderRNA *brna) RNA_def_property_float_funcs(prop, "rna_MEdge_crease_get", "rna_MEdge_crease_set", NULL); RNA_def_property_ui_text( prop, "Crease", "Weight used by the Subdivision Surface modifier for creasing"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "bevel_weight", PROP_FLOAT, PROP_NONE); RNA_def_property_float_funcs( prop, "rna_MEdge_bevel_weight_get", "rna_MEdge_bevel_weight_set", NULL); RNA_def_property_ui_text(prop, "Bevel Weight", "Weight used by the Bevel modifier"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SELECT); @@ -1744,7 +1775,7 @@ static void rna_def_medge(BlenderRNA *brna) prop = RNA_def_property(srna, "use_edge_sharp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SHARP); RNA_def_property_ui_text(prop, "Sharp", "Sharp edge for the Edge Split modifier"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "is_loose", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_LOOSEEDGE); @@ -1754,7 +1785,7 @@ static void rna_def_medge(BlenderRNA *brna) RNA_def_property_boolean_funcs( prop, "rna_MEdge_freestyle_edge_mark_get", "rna_MEdge_freestyle_edge_mark_set"); RNA_def_property_ui_text(prop, "Freestyle Edge Mark", "Edge mark for Freestyle line rendering"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -1937,7 +1968,7 @@ static void rna_def_mpolygon(BlenderRNA *brna) # if 0 RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MeshPoly_material_index_range"); # endif - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_FACE_SEL); @@ -1952,13 +1983,13 @@ static void rna_def_mpolygon(BlenderRNA *brna) prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SMOOTH); RNA_def_property_ui_text(prop, "Smooth", ""); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "use_freestyle_mark", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs( prop, "rna_MPoly_freestyle_face_mark_get", "rna_MPoly_freestyle_face_mark_set"); RNA_def_property_ui_text(prop, "Freestyle Face Mark", "Face mark for Freestyle line rendering"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_DIRECTION); RNA_def_property_array(prop, 3); @@ -2015,34 +2046,34 @@ static void rna_def_mloopuv(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshLoopLayer_name_set"); RNA_def_property_ui_text(prop, "Name", "Name of UV map"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs( prop, "rna_MeshUVLoopLayer_active_get", "rna_MeshUVLoopLayer_active_set"); RNA_def_property_ui_text(prop, "Active", "Set the map as active for display and editing"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "active_render", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "active_rnd", 0); RNA_def_property_boolean_funcs( prop, "rna_MeshUVLoopLayer_active_render_get", "rna_MeshUVLoopLayer_active_render_set"); RNA_def_property_ui_text(prop, "Active Render", "Set the map as active for rendering"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "active_clone", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "active_clone", 0); RNA_def_property_boolean_funcs( prop, "rna_MeshUVLoopLayer_clone_get", "rna_MeshUVLoopLayer_clone_set"); RNA_def_property_ui_text(prop, "Active Clone", "Set the map as active for cloning"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); srna = RNA_def_struct(brna, "MeshUVLoop", NULL); RNA_def_struct_sdna(srna, "MLoopUV"); RNA_def_struct_path_func(srna, "rna_MeshUVLoop_path"); prop = RNA_def_property(srna, "uv", PROP_FLOAT, PROP_XYZ); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "pin_uv", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MLOOPUV_PINNED); @@ -2069,13 +2100,13 @@ static void rna_def_mloopcol(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshLoopLayer_name_set"); RNA_def_property_ui_text(prop, "Name", "Name of Vertex color layer"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs( prop, "rna_MeshLoopColorLayer_active_get", "rna_MeshLoopColorLayer_active_set"); RNA_def_property_ui_text(prop, "Active", "Sets the layer as active for display and editing"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "active_render", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "active_rnd", 0); @@ -2083,7 +2114,7 @@ static void rna_def_mloopcol(BlenderRNA *brna) "rna_MeshLoopColorLayer_active_render_get", "rna_MeshLoopColorLayer_active_render_set"); RNA_def_property_ui_text(prop, "Active Render", "Sets the layer as active for rendering"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshLoopColor"); @@ -2109,7 +2140,7 @@ static void rna_def_mloopcol(BlenderRNA *brna) RNA_def_property_float_funcs( prop, "rna_MeshLoopColor_color_get", "rna_MeshLoopColor_color_set", NULL); RNA_def_property_ui_text(prop, "Color", ""); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); } static void rna_def_MPropCol(BlenderRNA *brna) @@ -2129,14 +2160,14 @@ static void rna_def_MPropCol(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshVertexLayer_name_set"); RNA_def_property_ui_text(prop, "Name", "Name of Sculpt Vertex color layer"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs( prop, "rna_MeshVertColorLayer_active_get", "rna_MeshVertColorLayer_active_set"); RNA_def_property_ui_text( prop, "Active", "Sets the sculpt vertex color layer as active for display and editing"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "active_render", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "active_rnd", 0); @@ -2145,7 +2176,7 @@ static void rna_def_MPropCol(BlenderRNA *brna) "rna_MeshVertColorLayer_active_render_set"); RNA_def_property_ui_text( prop, "Active Render", "Sets the sculpt vertex color layer as active for rendering"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshVertColor"); @@ -2169,7 +2200,7 @@ static void rna_def_MPropCol(BlenderRNA *brna) RNA_def_property_array(prop, 4); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Color", ""); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); } static void rna_def_mproperties(BlenderRNA *brna) { @@ -2189,7 +2220,7 @@ static void rna_def_mproperties(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); \ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshAnyLayer_name_set"); \ RNA_def_property_ui_text(prop, "Name", ""); \ - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); \ + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); \ \ prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); \ RNA_def_property_struct_type(prop, "Mesh" elemname "FloatProperty"); \ @@ -2215,7 +2246,7 @@ static void rna_def_mproperties(BlenderRNA *brna) prop = RNA_def_property(srna, "value", PROP_FLOAT, PROP_NONE); \ RNA_def_property_float_sdna(prop, NULL, "f"); \ RNA_def_property_ui_text(prop, "Value", ""); \ - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); \ + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); \ ((void)0) /* Int */ @@ -2231,7 +2262,7 @@ static void rna_def_mproperties(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); \ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshAnyLayer_name_set"); \ RNA_def_property_ui_text(prop, "Name", ""); \ - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); \ + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); \ \ prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); \ RNA_def_property_struct_type(prop, "Mesh" elemname "IntProperty"); \ @@ -2256,7 +2287,7 @@ static void rna_def_mproperties(BlenderRNA *brna) prop = RNA_def_property(srna, "value", PROP_INT, PROP_NONE); \ RNA_def_property_int_sdna(prop, NULL, "i"); \ RNA_def_property_ui_text(prop, "Value", ""); \ - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); \ + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); \ ((void)0) /* String */ @@ -2272,7 +2303,7 @@ static void rna_def_mproperties(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); \ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshAnyLayer_name_set"); \ RNA_def_property_ui_text(prop, "Name", ""); \ - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); \ + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); \ \ prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); \ RNA_def_property_struct_type(prop, "Mesh" elemname "StringProperty"); \ @@ -2302,7 +2333,7 @@ static void rna_def_mproperties(BlenderRNA *brna) "rna_MeshStringProperty_s_length", \ "rna_MeshStringProperty_s_set"); \ RNA_def_property_ui_text(prop, "Value", ""); \ - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); MESH_FLOAT_PROPERTY_LAYER("Vertex"); MESH_FLOAT_PROPERTY_LAYER("Polygon"); @@ -2330,7 +2361,7 @@ void rna_def_texmat_common(StructRNA *srna, const char *texspace_editable) RNA_def_property_ui_text(prop, "Texture Space Location", "Texture space location"); RNA_def_property_float_funcs(prop, "rna_Mesh_texspace_loc_get", NULL, NULL); RNA_def_property_editable_func(prop, texspace_editable); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "texspace_size", PROP_FLOAT, PROP_XYZ); RNA_def_property_float_sdna(prop, NULL, "size"); @@ -2338,7 +2369,7 @@ void rna_def_texmat_common(StructRNA *srna, const char *texspace_editable) RNA_def_property_ui_text(prop, "Texture Space Size", "Texture space size"); RNA_def_property_float_funcs(prop, "rna_Mesh_texspace_size_get", NULL, NULL); RNA_def_property_editable_func(prop, texspace_editable); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); /* materials */ prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE); @@ -2602,7 +2633,7 @@ static void rna_def_uv_layers(BlenderRNA *brna, PropertyRNA *cprop) prop, "rna_Mesh_uv_layer_active_get", "rna_Mesh_uv_layer_active_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK); RNA_def_property_ui_text(prop, "Active UV Loop Layer", "Active UV loop layer"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_funcs(prop, @@ -2610,7 +2641,7 @@ static void rna_def_uv_layers(BlenderRNA *brna, PropertyRNA *cprop) "rna_Mesh_uv_layer_active_index_set", "rna_Mesh_uv_layer_index_range"); RNA_def_property_ui_text(prop, "Active UV Loop Layer Index", "Active UV loop layer index"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); } /* mesh float layers */ @@ -2760,7 +2791,7 @@ static void rna_def_skin_vertices(BlenderRNA *brna, PropertyRNA *UNUSED(cprop)) RNA_def_struct_name_property(srna, prop); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshVertexLayer_name_set"); RNA_def_property_ui_text(prop, "Name", "Name of skin layer"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshSkinVertex"); @@ -2786,7 +2817,7 @@ static void rna_def_skin_vertices(BlenderRNA *brna, PropertyRNA *UNUSED(cprop)) RNA_def_property_array(prop, 2); RNA_def_property_ui_range(prop, 0.001, 100.0, 1, 3); RNA_def_property_ui_text(prop, "Radius", "Radius of the skin"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); /* Flags */ @@ -2796,13 +2827,13 @@ static void rna_def_skin_vertices(BlenderRNA *brna, PropertyRNA *UNUSED(cprop)) "Root", "Vertex is a root for rotation calculations and armature generation, " "setting this flag does not clear other roots in the same mesh island"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "use_loose", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MVERT_SKIN_LOOSE); RNA_def_property_ui_text( prop, "Loose", "If vertex has multiple adjacent edges, it is hulled to them directly"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); } static void rna_def_paint_mask(BlenderRNA *brna, PropertyRNA *UNUSED(cprop)) @@ -2837,7 +2868,7 @@ static void rna_def_paint_mask(BlenderRNA *brna, PropertyRNA *UNUSED(cprop)) prop = RNA_def_property(srna, "value", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "f"); RNA_def_property_ui_text(prop, "Value", ""); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); } static void rna_def_face_map(BlenderRNA *brna) @@ -2854,7 +2885,7 @@ static void rna_def_face_map(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshPolyLayer_name_set"); RNA_def_property_ui_text(prop, "Name", "Name of face map layer"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshFaceMap"); @@ -2878,7 +2909,7 @@ static void rna_def_face_map(BlenderRNA *brna) prop = RNA_def_property(srna, "value", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "i"); RNA_def_property_ui_text(prop, "Value", ""); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); } static void rna_def_face_maps(BlenderRNA *brna, PropertyRNA *cprop) @@ -2897,7 +2928,7 @@ static void rna_def_face_maps(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_struct_type(prop, "MeshFaceMapLayer"); RNA_def_property_pointer_funcs(prop, "rna_Mesh_face_map_active_get", NULL, NULL, NULL); RNA_def_property_ui_text(prop, "Active Face Map Layer", ""); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); FunctionRNA *func; PropertyRNA *parm; @@ -3017,7 +3048,7 @@ static void rna_def_mesh(BlenderRNA *brna) "rna_Mesh_uv_layer_stencil_index_set", "rna_Mesh_uv_layer_index_range"); RNA_def_property_ui_text(prop, "Mask UV Loop Layer Index", "Mask UV loop layer index"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); /* Vertex colors */ @@ -3229,11 +3260,6 @@ static void rna_def_mesh(BlenderRNA *brna) "generating triangles. A value greater than 0 disables Fix Poles"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - prop = RNA_def_property(srna, "use_remesh_smooth_normals", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_SMOOTH_NORMALS); - RNA_def_property_ui_text(prop, "Smooth Normals", "Smooth the normals of the remesher result"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - prop = RNA_def_property(srna, "use_remesh_fix_poles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_FIX_POLES); RNA_def_property_ui_text(prop, "Fix Poles", "Produces less poles and a better topology flow"); @@ -3307,7 +3333,7 @@ static void rna_def_mesh(BlenderRNA *brna) "Auto Smooth", "Auto smooth (based on smooth/sharp faces/edges and angle between faces), " "or use custom split normals data if available"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "smoothresh"); @@ -3316,7 +3342,7 @@ static void rna_def_mesh(BlenderRNA *brna) "Auto Smooth Angle", "Maximum angle between face normals that will be considered as smooth " "(unused if custom split normals data are available)"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); RNA_define_verify_sdna(false); prop = RNA_def_property(srna, "has_custom_normals", PROP_BOOLEAN, PROP_NONE); @@ -3347,7 +3373,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop, "Auto Texture Space", "Adjust active object's texture space automatically when transforming object"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); # if 0 prop = RNA_def_property(srna, "texspace_location", PROP_FLOAT, PROP_TRANSLATION); @@ -3437,3 +3463,5 @@ void RNA_def_mesh(BlenderRNA *brna) } #endif + +/** \} */ diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index f8fa2aab5e7..a32ed49ae1e 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -354,7 +354,7 @@ static void rna_def_movieclip(BlenderRNA *brna) 0, 0, "Size", - "Width and height in pixels, zero when image data cant be loaded", + "Width and height in pixels, zero when image data can't be loaded", 0, 0); RNA_def_property_int_funcs(prop, "rna_MovieClip_size_get", NULL, NULL); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 1a0a6a9514d..e27c3e6dada 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -464,6 +464,10 @@ static EnumPropertyItem rna_node_geometry_mesh_circle_fill_type_items[] = { { \ GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \ } +#define ITEM_INTEGER \ + { \ + GEO_NODE_ATTRIBUTE_INPUT_INTEGER, "INTEGER", 0, "Integer", "" \ + } #define ITEM_BOOLEAN \ { \ GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \ @@ -475,6 +479,7 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] ITEM_FLOAT, ITEM_VECTOR, ITEM_COLOR, + ITEM_INTEGER, ITEM_BOOLEAN, {0, NULL, 0, NULL, NULL}, }; @@ -497,6 +502,11 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float ITEM_FLOAT, {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_int[] = { + ITEM_ATTRIBUTE, + ITEM_INTEGER, + {0, NULL, 0, NULL, NULL}, +}; static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = { ITEM_ATTRIBUTE, ITEM_FLOAT, @@ -1283,6 +1293,13 @@ static bNodeLink *rna_NodeTree_link_new(bNodeTree *ntree, if (nodeCountSocketLinks(ntree, tosock) + 1 > nodeSocketLinkLimit(tosock)) { nodeRemSocketLinks(ntree, tosock); } + if (tosock->flag & SOCK_MULTI_INPUT) { + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { + if (link->fromsock == fromsock && link->tosock == tosock) { + nodeRemLink(ntree, link); + } + } + } } ret = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); @@ -9864,6 +9881,50 @@ static void def_geo_curve_deform(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_subdivide(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSubdivide", "storage"); + + prop = RNA_def_property(srna, "cuts_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_int); + RNA_def_property_ui_text(prop, "Cuts Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_curve_to_points(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_SAMPLE_EVALUATED, + "EVALUATED", + 0, + "Evaluated", + "Create points from the curve's evaluated points, based on the resolution attribute for " + "NURBS and Bezier splines"}, + {GEO_NODE_CURVE_SAMPLE_COUNT, + "COUNT", + 0, + "Count", + "Sample each spline by evenly distributing the specified number of points"}, + {GEO_NODE_CURVE_SAMPLE_LENGTH, + "LENGTH", + 0, + "Length", + "Sample each spline by splitting it into segments with the specified length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveToPoints", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to generate points from the input curve"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_attribute_transfer(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { @@ -9911,6 +9972,42 @@ static void def_geo_input_material(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_raycast(StructRNA *srna) +{ + static EnumPropertyItem mapping_items[] = { + {GEO_NODE_RAYCAST_INTERPOLATED, + "INTERPOLATED", + 0, + "Interpolated", + "Interpolate the attribute from the corners of the hit face"}, + {GEO_NODE_RAYCAST_NEAREST, + "NEAREST", + 0, + "Nearest", + "Use the attribute value of the closest mesh element"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryRaycast", "storage"); + + prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mapping_items); + RNA_def_property_ui_text(prop, "Mapping", "Mapping from the target geometry to hit points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "input_type_ray_direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); + RNA_def_property_ui_text(prop, "Input Type Ray Direction", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_ray_length", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text(prop, "Input Type Ray Length", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index b339682222c..3d3faf0c56f 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -476,8 +476,7 @@ static void rna_Object_active_shape_update(Main *bmain, Scene *UNUSED(scene), Po DEG_id_tag_update(&me->id, 0); - EDBM_mesh_normals_update(em); - BKE_editmesh_looptri_calc(em); + BKE_editmesh_looptri_and_normals_calc(em); break; } case OB_CURVE: @@ -2000,8 +1999,8 @@ static void rna_VertexGroup_vertex_add(ID *id, } while (index_len--) { - ED_vgroup_vert_add( - ob, def, *index++, weight, assignmode); /* XXX, not efficient calling within loop*/ + /* XXX: not efficient calling within loop. */ + ED_vgroup_vert_add(ob, def, *index++, weight, assignmode); } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index e463323c6dc..d08504dd6fe 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -575,8 +575,10 @@ static void rna_Object_ray_cast(Object *ob, /* Test BoundBox first (efficiency) */ BoundBox *bb = BKE_object_boundbox_get(ob); float distmin; - normalize_v3( - direction); /* Needed for valid distance check from isect_ray_aabb_v3_simple() call. */ + + /* Needed for valid distance check from #isect_ray_aabb_v3_simple() call. */ + normalize_v3(direction); + if (!bb || (isect_ray_aabb_v3_simple(origin, direction, bb->vec[0], bb->vec[6], &distmin, NULL) && distmin <= distance)) { diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index b8bb4f58dcd..f736885df77 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -620,8 +620,8 @@ static void rna_PoseChannel_constraints_remove( ED_object_constraint_update(bmain, ob); - BKE_constraints_active_set(&pchan->constraints, - NULL); /* XXX, is this really needed? - Campbell */ + /* XXX(Campbell): is this really needed? */ + BKE_constraints_active_set(&pchan->constraints, NULL); WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, id); diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index dd91a5509f5..6715941ae2a 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -856,6 +856,15 @@ static void rna_def_render_engine(BlenderRNA *brna) "Use Custom Freestyle", "Handles freestyle rendering on its own, instead of delegating it to EEVEE"); + prop = RNA_def_property(srna, "bl_use_image_save", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "type->flag", RE_USE_NO_IMAGE_SAVE); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, + "Use Image Save", + "Save images/movie to disk while rendering an animation. " + "Disabling image saving is only supported when bl_use_postprocess is also disabled"); + prop = RNA_def_property(srna, "bl_use_gpu_context", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_GPU_CONTEXT); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index be7c938a525..7f23d9bc754 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1912,10 +1912,9 @@ static void rna_Scene_transform_orientation_slots_begin(CollectionPropertyIterat iter, orient_slot, sizeof(*orient_slot), ARRAY_SIZE(scene->orientation_slots), 0, NULL); } -static int rna_Scene_transform_orientation_slots_length(PointerRNA *ptr) +static int rna_Scene_transform_orientation_slots_length(PointerRNA *UNUSED(ptr)) { - Scene *scene = (Scene *)ptr->owner_id; - return ARRAY_SIZE(scene->orientation_slots); + return ARRAY_SIZE(((Scene *)NULL)->orientation_slots); } static bool rna_Scene_use_audio_get(PointerRNA *ptr) @@ -6441,12 +6440,6 @@ static void rna_def_scene_render_data(BlenderRNA *brna) /* sequencer draw options */ -# if 0 /* see R_SEQ_GL_REND comment */ - prop = RNA_def_property(srna, "use_sequencer_gl_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "seq_flag", R_SEQ_GL_REND); - RNA_def_property_ui_text(prop, "Sequencer OpenGL", ""); -# endif - prop = RNA_def_property(srna, "sequencer_gl_preview", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "seq_prev_type"); RNA_def_property_enum_items(prop, rna_enum_shading_type_items); @@ -6454,15 +6447,6 @@ static void rna_def_scene_render_data(BlenderRNA *brna) prop, "Sequencer Preview Shading", "Display method used in the sequencer view"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SceneSequencer_update"); -# if 0 /* UNUSED, see R_SEQ_GL_REND comment */ - prop = RNA_def_property(srna, "sequencer_gl_render", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "seq_rend_type"); - RNA_def_property_enum_items(prop, rna_enum_shading_type_items); - /* XXX Label and tooltips are obviously wrong! */ - RNA_def_property_ui_text( - prop, "Sequencer Preview Shading", "Display method used in the sequencer view"); -# endif - prop = RNA_def_property(srna, "use_sequencer_override_scene_strip", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "seq_flag", R_SEQ_OVERRIDE_SCENE_SETTINGS); RNA_def_property_ui_text(prop, diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index c49b41867a8..caecd9a07d8 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -89,7 +89,7 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf /* don't do notifier when we're rendering, avoid some viewport crashes * redrawing while the data is being modified for render */ if (!G.is_rendering) { - /* cant use NC_SCENE|ND_FRAME because this causes wm_event_do_notifiers to call + /* can't use NC_SCENE|ND_FRAME because this causes wm_event_do_notifiers to call * BKE_scene_graph_update_for_newframe which will lose any un-keyed changes T24690. */ /* WM_main_add_notifier(NC_SCENE|ND_FRAME, scene); */ diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index fba1d3cda9a..25f29a3efe0 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -169,7 +169,7 @@ static void rna_Area_type_update(bContext *C, PointerRNA *ptr) /* It is possible that new layers becomes visible. */ if (area->spacetype == SPACE_VIEW3D) { - DEG_on_visible_update(CTX_data_main(C), false); + DEG_tag_on_visible_update(CTX_data_main(C), false); } CTX_wm_window_set(C, prevwin); @@ -288,7 +288,7 @@ static PointerRNA rna_Region_data_get(PointerRNA *ptr) if (region->regiondata != NULL) { if (region->regiontype == RGN_TYPE_WINDOW) { - /* We could make this static, it wont change at run-time. */ + /* We could make this static, it won't change at run-time. */ SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); if (region->type == BKE_regiontype_from_id(st, region->regiontype)) { PointerRNA newptr; diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 9851d65ece3..29100cd7658 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -94,6 +94,8 @@ const EnumPropertyItem rna_enum_sequence_modifier_type_items[] = { # include "IMB_imbuf.h" +# include "SEQ_edit.h" + typedef struct SequenceSearchData { Sequence *seq; void *data; @@ -243,6 +245,21 @@ static void rna_SequenceEditor_sequences_all_next(CollectionPropertyIterator *it iter->valid = (internal->link != NULL); } +static int rna_SequenceEditor_sequences_all_lookup_string(PointerRNA *ptr, + const char *key, + PointerRNA *r_ptr) +{ + ID *id = ptr->owner_id; + Scene *scene = (Scene *)id; + + Sequence *seq = SEQ_sequence_lookup_by_name(scene, key); + if (seq) { + RNA_pointer_create(ptr->owner_id, &RNA_Sequence, seq, r_ptr); + return true; + } + return false; +} + /* internal use */ static int rna_SequenceEditor_elements_length(PointerRNA *ptr) { @@ -627,11 +644,10 @@ static void rna_Sequence_name_set(PointerRNA *ptr, const char *value) BLI_strncpy(oldname, seq->name + 2, sizeof(seq->name) - 2); /* copy the new name into the name slot */ - BLI_strncpy_utf8(seq->name + 2, value, sizeof(seq->name) - 2); + SEQ_edit_sequence_name_set(scene, seq, value); /* make sure the name is unique */ - SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq); - + SEQ_sequence_base_unique_name_recursive(scene, &scene->ed->seqbase, seq); /* fix all the animation data which may link to this */ /* Don't rename everywhere because these are per scene. */ @@ -1997,7 +2013,7 @@ static void rna_def_editor(BlenderRNA *brna) NULL, NULL, NULL, - NULL, + "rna_SequenceEditor_sequences_all_lookup_string", NULL); prop = RNA_def_property(srna, "meta_stack", PROP_COLLECTION, PROP_NONE); @@ -2922,7 +2938,7 @@ static void rna_def_text(StructRNA *srna) prop = RNA_def_property(srna, "use_box", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TEXT_BOX); - RNA_def_property_ui_text(prop, "Shadow", "Display colored box behind text"); + RNA_def_property_ui_text(prop, "Box", "Display colored box behind text"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update"); prop = RNA_def_property(srna, "use_bold", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index bb356e4b532..f3871e625de 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -177,7 +177,11 @@ const EnumPropertyItem rna_enum_space_graph_mode_items[] = { const EnumPropertyItem rna_enum_space_sequencer_view_type_items[] = { {SEQ_VIEW_SEQUENCE, "SEQUENCER", ICON_SEQ_SEQUENCER, "Sequencer", ""}, {SEQ_VIEW_PREVIEW, "PREVIEW", ICON_SEQ_PREVIEW, "Preview", ""}, - {SEQ_VIEW_SEQUENCE_PREVIEW, "SEQUENCER_PREVIEW", ICON_SEQ_SPLITVIEW, "Sequencer/Preview", ""}, + {SEQ_VIEW_SEQUENCE_PREVIEW, + "SEQUENCER_PREVIEW", + ICON_SEQ_SPLITVIEW, + "Sequencer & Preview", + ""}, {0, NULL, 0, NULL, NULL}, }; @@ -891,8 +895,8 @@ static void rna_SpaceView3D_use_local_camera_set(PointerRNA *ptr, bool value) if (!value) { Scene *scene = ED_screen_scene_find(screen, G_MAIN->wm.first); /* NULL if the screen isn't in an active window (happens when setting from Python). - * This could be moved to the update function, in that case the scene wont relate to the screen - * so keep it working this way. */ + * This could be moved to the update function, in that case the scene won't relate to the + * screen so keep it working this way. */ if (scene != NULL) { v3d->camera = scene->camera; } @@ -4129,14 +4133,6 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop, "Fade Inactive Objects", "Fade inactive geometry using the viewport background color"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - prop = RNA_def_property(srna, "show_mode_transfer", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_MODE_TRANSFER); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, - "Flash on Mode Transfer", - "Flash the target object when tranfering the active mode to it"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - prop = RNA_def_property(srna, "fade_inactive_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.fade_alpha"); RNA_def_property_ui_text(prop, "Opacity", "Strength of the fade effect"); @@ -4970,7 +4966,7 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop = RNA_def_property(srna, "view_location", PROP_FLOAT, PROP_TRANSLATION); # if 0 - RNA_def_property_float_sdna(prop, NULL, "ofs"); /* cant use because its negated */ + RNA_def_property_float_sdna(prop, NULL, "ofs"); /* can't use because it's negated */ # else RNA_def_property_array(prop, 3); RNA_def_property_float_funcs( @@ -4981,7 +4977,7 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_update(prop, NC_WINDOW, NULL); prop = RNA_def_property( - srna, "view_rotation", PROP_FLOAT, PROP_QUATERNION); /* cant use because its inverted */ + srna, "view_rotation", PROP_FLOAT, PROP_QUATERNION); /* can't use because it's inverted */ # if 0 RNA_def_property_float_sdna(prop, NULL, "viewquat"); # else @@ -6569,6 +6565,16 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem asset_import_type_items[] = { + {FILE_ASSET_IMPORT_LINK, "LINK", 0, "Link", "Import the assets as linked data-block"}, + {FILE_ASSET_IMPORT_APPEND, + "APPEND", + 0, + "Append", + "Import the assets as copied data-block, with no link to the original asset data-block"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "FileAssetSelectParams", "FileSelectParams"); RNA_def_struct_ui_text( srna, "Asset Select Parameters", "Settings for the file selection in Asset Browser mode"); @@ -6590,6 +6596,13 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) NULL); RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL); + + prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, asset_import_type_items); + RNA_def_property_ui_text(prop, "Import Type", "Determine how the asset will be imported"); + /* Asset drag info saved by buttons stores the import type, so the space must redraw when import + * type changes. */ + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL); } static void rna_def_filemenu_entry(BlenderRNA *brna) @@ -7402,6 +7415,131 @@ static void rna_def_space_clip(BlenderRNA *brna) RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL); } +static void rna_def_spreadsheet_column_id(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SpreadsheetColumnID", NULL); + RNA_def_struct_sdna(srna, "SpreadsheetColumnID"); + RNA_def_struct_ui_text( + srna, "Spreadsheet Column ID", "Data used to identify a spreadsheet column"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Column Name", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); +} + +static void rna_def_spreadsheet_column(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem data_type_items[] = { + {SPREADSHEET_VALUE_TYPE_INT32, "INT32", ICON_NONE, "Integer", ""}, + {SPREADSHEET_VALUE_TYPE_FLOAT, "FLOAT", ICON_NONE, "Float", ""}, + {SPREADSHEET_VALUE_TYPE_BOOL, "BOOLEAN", ICON_NONE, "Boolean", ""}, + {SPREADSHEET_VALUE_TYPE_INSTANCES, "INSTANCES", ICON_NONE, "Instances", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + srna = RNA_def_struct(brna, "SpreadsheetColumn", NULL); + RNA_def_struct_sdna(srna, "SpreadsheetColumn"); + RNA_def_struct_ui_text( + srna, "Spreadsheet Column", "Persistent data associated with a spreadsheet column"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "data_type"); + RNA_def_property_enum_items(prop, data_type_items); + RNA_def_property_ui_text( + prop, "Data Type", "The data type of the corresponding column visible in the spreadsheet"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + rna_def_spreadsheet_column_id(brna); + + prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "SpreadsheetColumnID"); + RNA_def_property_ui_text( + prop, "ID", "Data used to identify the corresponding data from the data source"); +} + +static void rna_def_spreadsheet_row_filter(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem rule_operation_items[] = { + {SPREADSHEET_ROW_FILTER_EQUAL, "EQUAL", ICON_NONE, "Equal To", ""}, + {SPREADSHEET_ROW_FILTER_GREATER, "GREATER", ICON_NONE, "Greater Than", ""}, + {SPREADSHEET_ROW_FILTER_LESS, "LESS", ICON_NONE, "Less Than", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + srna = RNA_def_struct(brna, "SpreadsheetRowFilter", NULL); + RNA_def_struct_sdna(srna, "SpreadsheetRowFilter"); + RNA_def_struct_ui_text(srna, "Spreadsheet Row Filter", ""); + + prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_ROW_FILTER_ENABLED); + RNA_def_property_ui_text(prop, "Enabled", ""); + RNA_def_property_ui_icon(prop, ICON_CHECKBOX_DEHLT, 1); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_ROW_FILTER_UI_EXPAND); + RNA_def_property_ui_text(prop, "Show Expanded", ""); + RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "column_name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Column Name", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rule_operation_items); + RNA_def_property_ui_text(prop, "Operation", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "value_float", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Float Value", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "value_float2", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "2D Vector Value", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "value_float3", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Vector Value", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "value_color", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Color Value", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "value_string", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Text Value", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Threshold", "How close float values need to be to be equal"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "value_int", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "value_int"); + RNA_def_property_ui_text(prop, "Integer Value", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + prop = RNA_def_property(srna, "value_boolean", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_ROW_FILTER_BOOL_VALUE); + RNA_def_property_ui_text(prop, "Boolean Value", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); +} + static const EnumPropertyItem spreadsheet_context_type_items[] = { {SPREADSHEET_CONTEXT_OBJECT, "OBJECT", ICON_NONE, "Object", ""}, {SPREADSHEET_CONTEXT_MODIFIER, "MODIFIER", ICON_NONE, "Modifier", ""}, @@ -7536,13 +7674,18 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna) srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space"); RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data"); - rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_FOOTER)); + rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_FOOTER)); prop = RNA_def_property(srna, "is_pinned", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_PINNED); RNA_def_property_ui_text(prop, "Is Pinned", "Context path is pinned"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + prop = RNA_def_property(srna, "use_filter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", SPREADSHEET_FILTER_ENABLE); + RNA_def_property_ui_text(prop, "Use Filter", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + prop = RNA_def_property(srna, "display_context_path_collapsed", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_CONTEXT_PATH_COLLAPSED); RNA_def_property_ui_text(prop, "Display Context Path Collapsed", ""); @@ -7557,6 +7700,7 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", SPREADSHEET_FILTER_SELECTED_ONLY); RNA_def_property_ui_text( prop, "Show Only Selected", "Only include rows that correspond to selected elements"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, 0); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); prop = RNA_def_property(srna, "geometry_component_type", PROP_ENUM, PROP_NONE); @@ -7578,6 +7722,22 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Object Evaluation State", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + rna_def_spreadsheet_column(brna); + + prop = RNA_def_property(srna, "columns", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "columns", NULL); + RNA_def_property_struct_type(prop, "SpreadsheetColumn"); + RNA_def_property_ui_text(prop, "Columns", "Persistent data associated with spreadsheet columns"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + rna_def_spreadsheet_row_filter(brna); + + prop = RNA_def_property(srna, "row_filters", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "row_filters", NULL); + RNA_def_property_struct_type(prop, "SpreadsheetRowFilter"); + RNA_def_property_ui_text(prop, "Row Filters", "Filters to remove rows from the displayed data"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + func = RNA_def_function( srna, "set_geometry_node_context", "rna_spreadsheet_set_geometry_node_context"); RNA_def_function_ui_description( diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index f128719db19..dc973a9c75c 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -1018,7 +1018,8 @@ void RNA_api_ui_layout(StructRNA *srna) func = RNA_def_function(srna, "operator_menu_enum", "rna_uiItemMenuEnumO"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); - api_ui_item_op(func); /* cant use api_ui_item_op_common because property must come right after */ + /* Can't use #api_ui_item_op_common because property must come right after. */ + api_ui_item_op(func); parm = RNA_def_string(func, "property", NULL, 0, "", "Identifier of property in operator"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); api_ui_item_common(func); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 6005ec96255..4d45d1d6d63 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1072,10 +1072,9 @@ static void rna_UserDef_studiolight_solid_lights_begin(CollectionPropertyIterato rna_iterator_array_begin(iter, sl->light, sizeof(*sl->light), ARRAY_SIZE(sl->light), 0, NULL); } -static int rna_UserDef_studiolight_solid_lights_length(PointerRNA *ptr) +static int rna_UserDef_studiolight_solid_lights_length(PointerRNA *UNUSED(ptr)) { - StudioLight *sl = (StudioLight *)ptr->data; - return ARRAY_SIZE(sl->light); + return ARRAY_SIZE(((StudioLight *)NULL)->light); } /* StudioLight.light_ambient */ diff --git a/source/blender/makesrna/rna_cleanup/rna_cleaner.py b/source/blender/makesrna/rna_cleanup/rna_cleaner.py index eaeca562e47..61622f281a6 100755 --- a/source/blender/makesrna/rna_cleanup/rna_cleaner.py +++ b/source/blender/makesrna/rna_cleanup/rna_cleaner.py @@ -288,7 +288,7 @@ def write_files(basename, props_list, props_length_max): f_py.write("rna_api = [\n%s]\n" % py) f_rna.write("rna_api = [\n%s]\n" % rna) - # write useful py script, wont hurt + # write useful py script, won't hurt f_py.write("\n'''\n") f_py.write("for p_note, p_changed, p_class, p_from, p_to, p_check, p_type, p_desc in rna_api:\n") f_py.write(" print(p_to)\n") diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index e1197439c7c..28fe4376d2c 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -891,7 +891,7 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh) for (i = 0; i < curdupface; i++) { mf = &split_m->mface[i]; - test_index_face(mf, &split_m->fdata, i, ((mf->flag & ME_FACE_SEL) ? 4 : 3)); + BKE_mesh_mface_index_validate(mf, &split_m->fdata, i, ((mf->flag & ME_FACE_SEL) ? 4 : 3)); } BLI_edgehash_free(edgehash, NULL); @@ -1106,7 +1106,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, mtf->uv[0][1] = mtf->uv[1][1] = mtf->uv[2][1] = mtf->uv[3][1] = 0.5f; } - test_index_face(mf, &explode->fdata, u, (orig_v4 ? 4 : 3)); + BKE_mesh_mface_index_validate(mf, &explode->fdata, u, (orig_v4 ? 4 : 3)); u++; } diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 5f82cd9ba27..e8677c7ce1a 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -264,13 +264,10 @@ struct NodeWithState { class GeometryNodesEvaluator; /** - * Utility class that locks the state of a node. Having this is a separate class is useful because - * it allows methods to communicate that they expect the node to be locked. + * Utility class that wraps a node whose state is locked. Having this is a separate class is useful + * because it allows methods to communicate that they expect the node to be locked. */ class LockedNode : NonCopyable, NonMovable { - private: - GeometryNodesEvaluator &evaluator_; - public: /** * This is the node that is currently locked. @@ -290,13 +287,9 @@ class LockedNode : NonCopyable, NonMovable { Vector<DOutputSocket> delayed_unused_outputs; Vector<DNode> delayed_scheduled_nodes; - LockedNode(GeometryNodesEvaluator &evaluator, const DNode node, NodeState &node_state) - : evaluator_(evaluator), node(node), node_state(node_state) + LockedNode(const DNode node, NodeState &node_state) : node(node), node_state(node_state) { - node_state.mutex.lock(); } - - ~LockedNode(); }; static const CPPType *get_socket_cpp_type(const DSocket socket) @@ -352,7 +345,7 @@ class GeometryNodesEvaluator { * on cache line boundaries. Note, just because a value is allocated in one specific thread, * does not mean that it will only be used by that thread. */ - EnumerableThreadSpecific<LinearAllocator<>> local_allocators_; + threading::EnumerableThreadSpecific<LinearAllocator<>> local_allocators_; /** * Every node that is reachable from the output gets its own state. Once all states have been @@ -380,7 +373,7 @@ class GeometryNodesEvaluator { void execute() { - task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH, TASK_ISOLATION_OFF); + task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH); this->create_states_for_reachable_nodes(); this->forward_group_inputs(); @@ -426,12 +419,13 @@ class GeometryNodesEvaluator { /* Initialize the more complex parts of the node states in parallel. At this point no new * node states are added anymore, so it is safe to lookup states from `node_states_` from * multiple threads. */ - parallel_for(IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) { - LinearAllocator<> &allocator = this->local_allocators_.local(); - for (const NodeWithState &item : node_states_.as_span().slice(range)) { - this->initialize_node_state(item.node, *item.state, allocator); - } - }); + threading::parallel_for( + IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) { + LinearAllocator<> &allocator = this->local_allocators_.local(); + for (const NodeWithState &item : node_states_.as_span().slice(range)) { + this->initialize_node_state(item.node, *item.state, allocator); + } + }); } void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator) @@ -507,11 +501,12 @@ class GeometryNodesEvaluator { void destruct_node_states() { - parallel_for(IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) { - for (const NodeWithState &item : node_states_.as_span().slice(range)) { - this->destruct_node_state(item.node, *item.state); - } - }); + threading::parallel_for( + IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) { + for (const NodeWithState &item : node_states_.as_span().slice(range)) { + this->destruct_node_state(item.node, *item.state); + } + }); } void destruct_node_state(const DNode node, NodeState &node_state) @@ -568,9 +563,10 @@ class GeometryNodesEvaluator { for (const DInputSocket &socket : params_.output_sockets) { const DNode node = socket.node(); NodeState &node_state = this->get_node_state(node); - LockedNode locked_node{*this, node, node_state}; - /* Setting an input as required will schedule any linked node. */ - this->set_input_required(locked_node, socket); + this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + /* Setting an input as required will schedule any linked node. */ + this->set_input_required(locked_node, socket); + }); } } @@ -632,30 +628,33 @@ class GeometryNodesEvaluator { bool node_task_preprocessing(const DNode node, NodeState &node_state) { - LockedNode locked_node{*this, node, node_state}; - BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled); - node_state.schedule_state = NodeScheduleState::Running; + bool do_execute_node = false; + this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled); + node_state.schedule_state = NodeScheduleState::Running; - /* Early return if the node has finished already. */ - if (locked_node.node_state.node_has_finished) { - return false; - } - /* Prepare outputs and check if actually any new outputs have to be computed. */ - if (!this->prepare_node_outputs_for_execution(locked_node)) { - return false; - } - /* Initialize nodes that don't support laziness. This is done after at least one output is - * required and before we check that all required inputs are provided. This reduces the - * number of "round-trips" through the task pool by one for most nodes. */ - if (!node_state.non_lazy_node_is_initialized && !node_supports_laziness(node)) { - this->initialize_non_lazy_node(locked_node); - node_state.non_lazy_node_is_initialized = true; - } - /* Prepare inputs and check if all required inputs are provided. */ - if (!this->prepare_node_inputs_for_execution(locked_node)) { - return false; - } - return true; + /* Early return if the node has finished already. */ + if (locked_node.node_state.node_has_finished) { + return; + } + /* Prepare outputs and check if actually any new outputs have to be computed. */ + if (!this->prepare_node_outputs_for_execution(locked_node)) { + return; + } + /* Initialize nodes that don't support laziness. This is done after at least one output is + * required and before we check that all required inputs are provided. This reduces the + * number of "round-trips" through the task pool by one for most nodes. */ + if (!node_state.non_lazy_node_is_initialized && !node_supports_laziness(node)) { + this->initialize_non_lazy_node(locked_node); + node_state.non_lazy_node_is_initialized = true; + } + /* Prepare inputs and check if all required inputs are provided. */ + if (!this->prepare_node_inputs_for_execution(locked_node)) { + return; + } + do_execute_node = true; + }); + return do_execute_node; } /* A node is finished when it has computed all outputs that may be used. */ @@ -896,18 +895,18 @@ class GeometryNodesEvaluator { void node_task_postprocessing(const DNode node, NodeState &node_state) { - LockedNode locked_node{*this, node, node_state}; - - const bool node_has_finished = this->finish_node_if_possible(locked_node); - const bool reschedule_requested = node_state.schedule_state == - NodeScheduleState::RunningAndRescheduled; - node_state.schedule_state = NodeScheduleState::NotScheduled; - if (reschedule_requested && !node_has_finished) { - /* Either the node rescheduled itself or another node tried to schedule it while it ran. */ - this->schedule_node(locked_node); - } + this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + const bool node_has_finished = this->finish_node_if_possible(locked_node); + const bool reschedule_requested = node_state.schedule_state == + NodeScheduleState::RunningAndRescheduled; + node_state.schedule_state = NodeScheduleState::NotScheduled; + if (reschedule_requested && !node_has_finished) { + /* Either the node rescheduled itself or another node tried to schedule it while it ran. */ + this->schedule_node(locked_node); + } - this->assert_expected_outputs_have_been_computed(locked_node); + this->assert_expected_outputs_have_been_computed(locked_node); + }); } void assert_expected_outputs_have_been_computed(LockedNode &locked_node) @@ -1042,13 +1041,14 @@ class GeometryNodesEvaluator { this->load_unlinked_input_value(locked_node, input_socket, input_state, origin_socket); locked_node.node_state.missing_required_inputs -= 1; this->schedule_node(locked_node); - return; } - /* The value has not been computed yet, so when it will be forwarded by another node, this - * node will be triggered. */ - will_be_triggered_by_other_node = true; + else { + /* The value has not been computed yet, so when it will be forwarded by another node, this + * node will be triggered. */ + will_be_triggered_by_other_node = true; - locked_node.delayed_required_outputs.append(DOutputSocket(origin_socket)); + locked_node.delayed_required_outputs.append(DOutputSocket(origin_socket)); + } } /* If this node will be triggered by another node, we don't have to schedule it now. */ if (!will_be_triggered_by_other_node) { @@ -1095,15 +1095,16 @@ class GeometryNodesEvaluator { NodeState &node_state = this->get_node_state(node); OutputState &output_state = node_state.outputs[socket->index()]; - LockedNode locked_node{*this, node, node_state}; - if (output_state.output_usage == ValueUsage::Required) { - /* Output is marked as required already. So the node is scheduled already. */ - return; - } - /* The origin node needs to be scheduled so that it provides the requested input - * eventually. */ - output_state.output_usage = ValueUsage::Required; - this->schedule_node(locked_node); + this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + if (output_state.output_usage == ValueUsage::Required) { + /* Output is marked as required already. So the node is scheduled already. */ + return; + } + /* The origin node needs to be scheduled so that it provides the requested input + * eventually. */ + output_state.output_usage = ValueUsage::Required; + this->schedule_node(locked_node); + }); } void send_output_unused_notification(const DOutputSocket socket) @@ -1112,14 +1113,15 @@ class GeometryNodesEvaluator { NodeState &node_state = this->get_node_state(node); OutputState &output_state = node_state.outputs[socket->index()]; - LockedNode locked_node{*this, node, node_state}; - output_state.potential_users -= 1; - if (output_state.potential_users == 0) { - /* The output socket has no users anymore. */ - output_state.output_usage = ValueUsage::Unused; - /* Schedule the origin node in case it wants to set its inputs as unused as well. */ - this->schedule_node(locked_node); - } + this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + output_state.potential_users -= 1; + if (output_state.potential_users == 0) { + /* The output socket has no users anymore. */ + output_state.output_usage = ValueUsage::Unused; + /* Schedule the origin node in case it wants to set its inputs as unused as well. */ + this->schedule_node(locked_node); + } + }); } void add_node_to_task_pool(const DNode node) @@ -1247,28 +1249,27 @@ class GeometryNodesEvaluator { NodeState &node_state = this->get_node_state(node); InputState &input_state = node_state.inputs[socket->index()]; - /* Lock the node because we want to change its state. */ - LockedNode locked_node{*this, node, node_state}; - - if (socket->is_multi_input_socket()) { - /* Add a new value to the multi-input. */ - MultiInputValue &multi_value = *input_state.value.multi; - multi_value.items.append({origin, value.get()}); - } - else { - /* Assign the value to the input. */ - SingleInputValue &single_value = *input_state.value.single; - BLI_assert(single_value.value == nullptr); - single_value.value = value.get(); - } + this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + if (socket->is_multi_input_socket()) { + /* Add a new value to the multi-input. */ + MultiInputValue &multi_value = *input_state.value.multi; + multi_value.items.append({origin, value.get()}); + } + else { + /* Assign the value to the input. */ + SingleInputValue &single_value = *input_state.value.single; + BLI_assert(single_value.value == nullptr); + single_value.value = value.get(); + } - if (input_state.usage == ValueUsage::Required) { - node_state.missing_required_inputs--; - if (node_state.missing_required_inputs == 0) { - /* Schedule node if all the required inputs have been provided. */ - this->schedule_node(locked_node); + if (input_state.usage == ValueUsage::Required) { + node_state.missing_required_inputs--; + if (node_state.missing_required_inputs == 0) { + /* Schedule node if all the required inputs have been provided. */ + this->schedule_node(locked_node); + } } - } + }); } void load_unlinked_input_value(LockedNode &locked_node, @@ -1363,44 +1364,33 @@ class GeometryNodesEvaluator { { this->log_socket_value(socket, Span<GPointer>(&value, 1)); } -}; -LockedNode::~LockedNode() -{ - /* First unlock the current node. */ - node_state.mutex.unlock(); - /* Then send notifications to the other nodes. */ - for (const DOutputSocket &socket : delayed_required_outputs) { - evaluator_.send_output_required_notification(socket); - } - for (const DOutputSocket &socket : delayed_unused_outputs) { - evaluator_.send_output_unused_notification(socket); - } - for (const DNode &node : delayed_scheduled_nodes) { - evaluator_.add_node_to_task_pool(node); - } -} + /* In most cases when `NodeState` is accessed, the node has to be locked first to avoid race + * conditions. */ + template<typename Function> + void with_locked_node(const DNode node, NodeState &node_state, const Function &function) + { + LockedNode locked_node{node, node_state}; -/* TODO: Use a map data structure or so to make this faster. */ -static DInputSocket get_input_by_identifier(const DNode node, const StringRef identifier) -{ - for (const InputSocketRef *socket : node->inputs()) { - if (socket->identifier() == identifier) { - return {node.context(), socket}; - } - } - return {}; -} + node_state.mutex.lock(); + /* Isolate this thread because we don't want it to start executing another node. This other + * node might want to lock the same mutex leading to a deadlock. */ + threading::isolate_task([&] { function(locked_node); }); + node_state.mutex.unlock(); -static DOutputSocket get_output_by_identifier(const DNode node, const StringRef identifier) -{ - for (const OutputSocketRef *socket : node->outputs()) { - if (socket->identifier() == identifier) { - return {node.context(), socket}; + /* Then send notifications to the other nodes after the node state is unlocked. This avoids + * locking two nodes at the same time on this thread and helps to prevent deadlocks. */ + for (const DOutputSocket &socket : locked_node.delayed_required_outputs) { + this->send_output_required_notification(socket); + } + for (const DOutputSocket &socket : locked_node.delayed_unused_outputs) { + this->send_output_unused_notification(socket); + } + for (const DNode &node : locked_node.delayed_scheduled_nodes) { + this->add_node_to_task_pool(node); } } - return {}; -} +}; NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator, DNode dnode, @@ -1415,7 +1405,7 @@ NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator, bool NodeParamsProvider::can_get_input(StringRef identifier) const { - const DInputSocket socket = get_input_by_identifier(this->dnode, identifier); + const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); InputState &input_state = node_state_.inputs[socket->index()]; @@ -1433,7 +1423,7 @@ bool NodeParamsProvider::can_get_input(StringRef identifier) const bool NodeParamsProvider::can_set_output(StringRef identifier) const { - const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier); + const DOutputSocket socket = this->dnode.output_by_identifier(identifier); BLI_assert(socket); OutputState &output_state = node_state_.outputs[socket->index()]; @@ -1442,7 +1432,7 @@ bool NodeParamsProvider::can_set_output(StringRef identifier) const GMutablePointer NodeParamsProvider::extract_input(StringRef identifier) { - const DInputSocket socket = get_input_by_identifier(this->dnode, identifier); + const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); BLI_assert(!socket->is_multi_input_socket()); BLI_assert(this->can_get_input(identifier)); @@ -1456,7 +1446,7 @@ GMutablePointer NodeParamsProvider::extract_input(StringRef identifier) Vector<GMutablePointer> NodeParamsProvider::extract_multi_input(StringRef identifier) { - const DInputSocket socket = get_input_by_identifier(this->dnode, identifier); + const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); BLI_assert(socket->is_multi_input_socket()); BLI_assert(this->can_get_input(identifier)); @@ -1487,7 +1477,7 @@ Vector<GMutablePointer> NodeParamsProvider::extract_multi_input(StringRef identi GPointer NodeParamsProvider::get_input(StringRef identifier) const { - const DInputSocket socket = get_input_by_identifier(this->dnode, identifier); + const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); BLI_assert(!socket->is_multi_input_socket()); BLI_assert(this->can_get_input(identifier)); @@ -1505,7 +1495,7 @@ GMutablePointer NodeParamsProvider::alloc_output_value(const CPPType &type) void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value) { - const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier); + const DOutputSocket socket = this->dnode.output_by_identifier(identifier); BLI_assert(socket); evaluator_.log_socket_value(socket, value); @@ -1519,30 +1509,32 @@ void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value) bool NodeParamsProvider::lazy_require_input(StringRef identifier) { BLI_assert(node_supports_laziness(this->dnode)); - const DInputSocket socket = get_input_by_identifier(this->dnode, identifier); + const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); InputState &input_state = node_state_.inputs[socket->index()]; if (input_state.was_ready_for_execution) { return false; } - LockedNode locked_node{evaluator_, this->dnode, node_state_}; - evaluator_.set_input_required(locked_node, socket); + evaluator_.with_locked_node(this->dnode, node_state_, [&](LockedNode &locked_node) { + evaluator_.set_input_required(locked_node, socket); + }); return true; } void NodeParamsProvider::set_input_unused(StringRef identifier) { - const DInputSocket socket = get_input_by_identifier(this->dnode, identifier); + const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); - LockedNode locked_node{evaluator_, this->dnode, node_state_}; - evaluator_.set_input_unused(locked_node, socket); + evaluator_.with_locked_node(this->dnode, node_state_, [&](LockedNode &locked_node) { + evaluator_.set_input_unused(locked_node, socket); + }); } bool NodeParamsProvider::output_is_required(StringRef identifier) const { - const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier); + const DOutputSocket socket = this->dnode.output_by_identifier(identifier); BLI_assert(socket); OutputState &output_state = node_state_.outputs[socket->index()]; @@ -1555,7 +1547,7 @@ bool NodeParamsProvider::output_is_required(StringRef identifier) const bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const { BLI_assert(node_supports_laziness(this->dnode)); - const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier); + const DOutputSocket socket = this->dnode.output_by_identifier(identifier); BLI_assert(socket); OutputState &output_state = node_state_.outputs[socket->index()]; diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index f7ac59f9e4b..3de6bb62c8a 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -284,7 +284,7 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co gogd.sy /= gogd.ry; result = BKE_mesh_new_nomain(num_verts, 0, 0, num_polys * 4, num_polys); - BKE_mesh_copy_settings(result, mesh_orig); + BKE_mesh_copy_parameters_for_eval(result, mesh_orig); gogd.mverts = result->mvert; gogd.mpolys = result->mpoly; diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index 88851f91337..4677a9bc253 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -218,7 +218,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) } } - BKE_mesh_copy_settings(result, mesh); + BKE_mesh_copy_parameters_for_eval(result, mesh); BKE_mesh_calc_edges(result, true, false); result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; return result; diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index db01dec4d19..39ebc415021 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -263,28 +263,24 @@ static void simple_helper(void *__restrict userdata, copy_v3_v3_map(dcut_remap, dcut, axis_map); switch (curr_deform_data->mode) { case MOD_SIMPLEDEFORM_MODE_TWIST: - simpleDeform_twist(curr_deform_data->smd_factor, - curr_deform_data->deform_axis, - dcut_remap, - co_remap); /* apply deform */ + /* Apply deform. */ + simpleDeform_twist( + curr_deform_data->smd_factor, curr_deform_data->deform_axis, dcut_remap, co_remap); break; case MOD_SIMPLEDEFORM_MODE_BEND: - simpleDeform_bend(curr_deform_data->smd_factor, - curr_deform_data->deform_axis, - dcut_remap, - co_remap); /* apply deform */ + /* Apply deform. */ + simpleDeform_bend( + curr_deform_data->smd_factor, curr_deform_data->deform_axis, dcut_remap, co_remap); break; case MOD_SIMPLEDEFORM_MODE_TAPER: - simpleDeform_taper(curr_deform_data->smd_factor, - curr_deform_data->deform_axis, - dcut_remap, - co_remap); /* apply deform */ + /* Apply deform. */ + simpleDeform_taper( + curr_deform_data->smd_factor, curr_deform_data->deform_axis, dcut_remap, co_remap); break; case MOD_SIMPLEDEFORM_MODE_STRETCH: - simpleDeform_stretch(curr_deform_data->smd_factor, - curr_deform_data->deform_axis, - dcut_remap, - co_remap); /* apply deform */ + /* Apply deform. */ + simpleDeform_stretch( + curr_deform_data->smd_factor, curr_deform_data->deform_axis, dcut_remap, co_remap); break; default: return; /* No simple-deform mode? */ diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 0be5c164089..09fa7e9c8ac 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -346,7 +346,7 @@ static void modifier_panel_header(const bContext *C, Panel *panel) else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { /* Some modifiers can work with pre-tessellated curves only. */ if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) { - /* Add button (appearing to be ON) and add tip why this cant be changed. */ + /* Add button (appearing to be ON) and add tip why this can't be changed. */ sub = uiLayoutRow(row, true); uiBlock *block = uiLayoutGetBlock(sub); static int apply_on_spline_always_on_hack = eModifierMode_ApplyOnSpline; diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index c0bf07b8eec..72358844838 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -132,7 +132,7 @@ static void panelRegister(ARegionType *region_type) static Mesh *create_empty_mesh(const Mesh *input_mesh) { Mesh *new_mesh = BKE_mesh_new_nomain(0, 0, 0, 0, 0); - BKE_mesh_copy_settings(new_mesh, input_mesh); + BKE_mesh_copy_parameters_for_eval(new_mesh, input_mesh); return new_mesh; } @@ -193,7 +193,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return create_empty_mesh(input_mesh); } - BKE_mesh_copy_settings(mesh, input_mesh); + BKE_mesh_copy_parameters_for_eval(mesh, input_mesh); if (vmmd->flag & VOLUME_TO_MESH_USE_SMOOTH_SHADE) { BKE_mesh_smooth_flag_set(mesh, true); } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index a31306d6b08..4ae4dbc2c7f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -166,7 +166,10 @@ set(SRC geometry/nodes/node_geo_curve_deform.cc geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_to_mesh.cc + geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_resample.cc + geometry/nodes/node_geo_curve_reverse.cc + geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc @@ -191,7 +194,9 @@ set(SRC geometry/nodes/node_geo_point_separate.cc geometry/nodes/node_geo_point_translate.cc geometry/nodes/node_geo_points_to_volume.cc + geometry/nodes/node_geo_raycast.cc geometry/nodes/node_geo_select_by_material.cc + geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_subdivide.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc @@ -243,7 +248,7 @@ set(SRC shader/nodes/node_shader_map_range.cc shader/nodes/node_shader_mapping.c shader/nodes/node_shader_math.cc - shader/nodes/node_shader_mixRgb.c + shader/nodes/node_shader_mixRgb.cc shader/nodes/node_shader_mix_shader.c shader/nodes/node_shader_normal.c shader/nodes/node_shader_normal_map.c diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index 7ff05449c0b..de9e4c8c812 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -95,6 +95,9 @@ class DNode { DInputSocket input(int index) const; DOutputSocket output(int index) const; + + DInputSocket input_by_identifier(StringRef identifier) const; + DOutputSocket output_by_identifier(StringRef identifier) const; }; /* A (nullable) reference to a socket and the context it is in. It is unique within an entire @@ -173,6 +176,7 @@ class DerivedNodeTree { Span<const NodeTreeRef *> used_node_tree_refs() const; bool has_link_cycles() const; + bool has_undefined_nodes_or_sockets() const; void foreach_node(FunctionRef<void(DNode)> callback) const; std::string to_dot() const; @@ -287,6 +291,16 @@ inline DOutputSocket DNode::output(int index) const return {context_, &node_ref_->output(index)}; } +inline DInputSocket DNode::input_by_identifier(StringRef identifier) const +{ + return {context_, &node_ref_->input_by_identifier(identifier)}; +} + +inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const +{ + return {context_, &node_ref_->output_by_identifier(identifier)}; +} + /* -------------------------------------------------------------------- * DSocket inline methods. */ diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d40553801e3..66f7028af88 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -54,7 +54,10 @@ void register_node_type_geo_convex_hull(void); void register_node_type_geo_curve_deform(void); void register_node_type_geo_curve_length(void); void register_node_type_geo_curve_to_mesh(void); +void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_resample(void); +void register_node_type_geo_curve_reverse(void); +void register_node_type_geo_curve_subdivide(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_input_material(void); @@ -79,8 +82,10 @@ void register_node_type_geo_point_scale(void); void register_node_type_geo_point_separate(void); void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_volume(void); +void register_node_type_geo_raycast(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_material(void); +void register_node_type_geo_separate_components(void); void register_node_type_geo_subdivide(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 5795617deb0..b028fc28bbc 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -70,6 +70,8 @@ class NodeTreeRef; class LinkRef; class InternalLinkRef; +using SocketIndexByIdentifierMap = Map<std::string, int>; + class SocketRef : NonCopyable, NonMovable { protected: NodeRef *node_; @@ -125,6 +127,7 @@ class SocketRef : NonCopyable, NonMovable { bNodeTree *btree() const; bool is_available() const; + bool is_undefined() const; void *default_value() const; template<typename T> T *default_value() const; @@ -168,6 +171,8 @@ class NodeRef : NonCopyable, NonMovable { Vector<InputSocketRef *> inputs_; Vector<OutputSocketRef *> outputs_; Vector<InternalLinkRef *> internal_links_; + SocketIndexByIdentifierMap *input_index_by_identifier_; + SocketIndexByIdentifierMap *output_index_by_identifier_; friend NodeTreeRef; @@ -181,6 +186,9 @@ class NodeRef : NonCopyable, NonMovable { const InputSocketRef &input(int index) const; const OutputSocketRef &output(int index) const; + const InputSocketRef &input_by_identifier(StringRef identifier) const; + const OutputSocketRef &output_by_identifier(StringRef identifier) const; + bNode *bnode() const; bNodeTree *btree() const; @@ -197,6 +205,7 @@ class NodeRef : NonCopyable, NonMovable { bool is_group_output_node() const; bool is_muted() const; bool is_frame() const; + bool is_undefined() const; void *storage() const; template<typename T> T *storage() const; @@ -244,6 +253,7 @@ class NodeTreeRef : NonCopyable, NonMovable { Vector<OutputSocketRef *> output_sockets_; Vector<LinkRef *> links_; MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_; + Vector<std::unique_ptr<SocketIndexByIdentifierMap>> owned_identifier_maps_; public: NodeTreeRef(bNodeTree *btree); @@ -260,6 +270,7 @@ class NodeTreeRef : NonCopyable, NonMovable { Span<const LinkRef *> links() const; bool has_link_cycles() const; + bool has_undefined_nodes_or_sockets() const; bNodeTree *btree() const; StringRefNull name() const; @@ -276,6 +287,7 @@ class NodeTreeRef : NonCopyable, NonMovable { bNodeSocket *bsocket); void create_linked_socket_caches(); + void create_socket_identifier_maps(); }; using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>; @@ -417,6 +429,11 @@ inline bool SocketRef::is_available() const return (bsocket_->flag & SOCK_UNAVAIL) == 0; } +inline bool SocketRef::is_undefined() const +{ + return bsocket_->typeinfo == &NodeSocketTypeUndefined; +} + inline void *SocketRef::default_value() const { return bsocket_->default_value; @@ -494,6 +511,18 @@ inline const OutputSocketRef &NodeRef::output(int index) const return *outputs_[index]; } +inline const InputSocketRef &NodeRef::input_by_identifier(StringRef identifier) const +{ + const int index = input_index_by_identifier_->lookup_as(identifier); + return this->input(index); +} + +inline const OutputSocketRef &NodeRef::output_by_identifier(StringRef identifier) const +{ + const int index = output_index_by_identifier_->lookup_as(identifier); + return this->output(index); +} + inline bNode *NodeRef::bnode() const { return bnode_; @@ -554,6 +583,11 @@ inline bool NodeRef::is_frame() const return bnode_->type == NODE_FRAME; } +inline bool NodeRef::is_undefined() const +{ + return bnode_->typeinfo == &NodeTypeUndefined; +} + inline bool NodeRef::is_muted() const { return (bnode_->flag & NODE_MUTED) != 0; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 77340d67fb6..4e39568f688 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -293,7 +293,10 @@ DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Conve DefNode(GeometryNode, GEO_NODE_CURVE_DEFORM, def_geo_curve_deform, "CURVE_DEFORM", CurveDeform, "Curve Deform", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") +DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") @@ -318,7 +321,9 @@ DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "") DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") +DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index 93cada2982b..46e9d36c09c 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -47,6 +47,7 @@ void update_attribute_input_socket_availabilities(bNode &node, name_is_available && ((socket->type == SOCK_STRING && mode_ == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) || (socket->type == SOCK_FLOAT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) || + (socket->type == SOCK_INT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_INTEGER) || (socket->type == SOCK_VECTOR && mode_ == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) || (socket->type == SOCK_RGBA && mode_ == GEO_NODE_ATTRIBUTE_INPUT_COLOR)); nodeSetSocketAvailability(socket, socket_is_available); diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc index d1b71d6f2ba..9b6824fdb5c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc @@ -78,7 +78,7 @@ static void align_rotations_auto_pivot(const VArray<float3> &vectors, const float3 local_main_axis, const MutableSpan<float3> rotations) { - parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { + threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { for (const int i : range) { const float3 vector = vectors[i]; if (is_zero_v3(vector)) { @@ -129,7 +129,7 @@ static void align_rotations_fixed_pivot(const VArray<float3> &vectors, return; } - parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { + threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { for (const int i : range) { const float3 vector = vectors[i]; if (is_zero_v3(vector)) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index 5293dd8c876..c5740395dfb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -95,7 +95,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon MutableSpan<ColorGeometry4f> results = attribute_result.as_span(); ColorBand *color_ramp = &node_storage->color_ramp; - parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc index 599c9e58e52..06a4327a6c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc @@ -144,7 +144,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( input_name, result_domain, float(0.0f)); MutableSpan<float> results = attribute_result.as_span<float>(); - parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]); } @@ -156,7 +156,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>( input_name, result_domain, float3(0.0f)); MutableSpan<float3> results = attribute_result.as_span<float3>(); - parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]); } @@ -169,7 +169,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon component.attribute_get_for_read<ColorGeometry4f>( input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); - parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc index 40fe675bd6c..00f38fb0c6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc @@ -209,7 +209,7 @@ static void map_range_float(const VArray<float> &attribute_input, switch (interpolation_type) { case NODE_MAP_RANGE_LINEAR: { - parallel_for(span.index_range(), 2048, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 2048, [&](IndexRange range) { for (const int i : range) { results[i] = map_linear(span[i], min_from, max_from, min_to, max_to); } @@ -218,7 +218,7 @@ static void map_range_float(const VArray<float> &attribute_input, } case NODE_MAP_RANGE_STEPPED: { const float steps = params.get_input<float>("Steps"); - parallel_for(span.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps); } @@ -226,7 +226,7 @@ static void map_range_float(const VArray<float> &attribute_input, break; } case NODE_MAP_RANGE_SMOOTHSTEP: { - parallel_for(span.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to); } @@ -234,7 +234,7 @@ static void map_range_float(const VArray<float> &attribute_input, break; } case NODE_MAP_RANGE_SMOOTHERSTEP: { - parallel_for(span.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to); } @@ -249,7 +249,7 @@ static void map_range_float(const VArray<float> &attribute_input, const float clamp_min = min_to < max_to ? min_to : max_to; const float clamp_max = min_to < max_to ? max_to : min_to; - parallel_for(results.index_range(), 2048, [&](IndexRange range) { + threading::parallel_for(results.index_range(), 2048, [&](IndexRange range) { for (const int i : range) { results[i] = std::clamp(results[i], clamp_min, clamp_max); } @@ -273,7 +273,7 @@ static void map_range_float3(const VArray<float3> &attribute_input, switch (interpolation_type) { case NODE_MAP_RANGE_LINEAR: { - parallel_for(span.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); @@ -284,7 +284,7 @@ static void map_range_float3(const VArray<float3> &attribute_input, } case NODE_MAP_RANGE_STEPPED: { const float3 steps = params.get_input<float3>("Steps_001"); - parallel_for(span.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { results[i].x = map_stepped( span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x); @@ -297,7 +297,7 @@ static void map_range_float3(const VArray<float3> &attribute_input, break; } case NODE_MAP_RANGE_SMOOTHSTEP: { - parallel_for(span.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); @@ -307,7 +307,7 @@ static void map_range_float3(const VArray<float3> &attribute_input, break; } case NODE_MAP_RANGE_SMOOTHERSTEP: { - parallel_for(span.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc index ce0ca31cc2b..9309863f4e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -159,7 +159,7 @@ static void do_math_operation(const VArray<float> &span_a, { bool success = try_dispatch_float_math_fl_fl_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) { for (const int i : range) { span_result[i] = math_function(span_a[i], span_b[i], span_c[i]); } @@ -176,7 +176,7 @@ static void do_math_operation(const VArray<float> &span_a, { bool success = try_dispatch_float_math_fl_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { + threading::parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { for (const int i : range) { span_result[i] = math_function(span_a[i], span_b[i]); } @@ -192,7 +192,7 @@ static void do_math_operation(const VArray<float> &span_input, { bool success = try_dispatch_float_math_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { + threading::parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { for (const int i : range) { span_result[i] = math_function(span_input[i]); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index a6bd6c0ee32..931b7758a57 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -88,7 +88,7 @@ static void do_mix_operation_float(const int blend_mode, VMutableArray<float> &results) { const int size = results.size(); - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float factor = factors[i]; float3 a{inputs_a[i]}; @@ -107,7 +107,7 @@ static void do_mix_operation_float3(const int blend_mode, VMutableArray<float3> &results) { const int size = results.size(); - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float factor = factors[i]; float3 a = inputs_a[i]; @@ -125,7 +125,7 @@ static void do_mix_operation_color4f(const int blend_mode, VMutableArray<ColorGeometry4f> &results) { const int size = results.size(); - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float factor = factors[i]; ColorGeometry4f a = inputs_a[i]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc index 9c22b7fa87f..b7863d38fc2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc @@ -71,7 +71,7 @@ static void proximity_calc(MutableSpan<float> distance_span, const bool store_locations) { IndexRange range = positions.index_range(); - parallel_for(range, 512, [&](IndexRange range) { + threading::parallel_for(range, 512, [&](IndexRange range) { BVHTreeNearest nearest_from_mesh; BVHTreeNearest nearest_from_pointcloud; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index 286411b7d28..eeb77abd624 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -126,7 +126,7 @@ static void randomize_attribute(MutableSpan<T> span, /* The operations could be templated too, but it doesn't make the code much shorter. */ switch (operation) { case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE: - parallel_for(span.index_range(), 512, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { for (const int i : range) { const T random_value = random_value_in_range<T>(ids[i], seed, min, max); span[i] = random_value; @@ -134,7 +134,7 @@ static void randomize_attribute(MutableSpan<T> span, }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD: - parallel_for(span.index_range(), 512, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { for (const int i : range) { const T random_value = random_value_in_range<T>(ids[i], seed, min, max); span[i] = span[i] + random_value; @@ -142,7 +142,7 @@ static void randomize_attribute(MutableSpan<T> span, }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT: - parallel_for(span.index_range(), 512, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { for (const int i : range) { const T random_value = random_value_in_range<T>(ids[i], seed, min, max); span[i] = span[i] - random_value; @@ -150,7 +150,7 @@ static void randomize_attribute(MutableSpan<T> span, }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY: - parallel_for(span.index_range(), 512, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { for (const int i : range) { const T random_value = random_value_in_range<T>(ids[i], seed, min, max); span[i] = span[i] * random_value; @@ -170,7 +170,7 @@ static void randomize_attribute_bool(MutableSpan<bool> span, { BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE); UNUSED_VARS_NDEBUG(operation); - parallel_for(span.index_range(), 512, [&](IndexRange range) { + threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { for (const int i : range) { const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f; span[i] = random_value; @@ -190,7 +190,7 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo BLI_assert(hashes.size() == hash_attribute->size()); const CPPType &cpp_type = hash_attribute->type(); GVArray_GSpan items{*hash_attribute}; - parallel_for(hashes.index_range(), 512, [&](IndexRange range) { + threading::parallel_for(hashes.index_range(), 512, [&](IndexRange range) { for (const int i : range) { hashes[i] = cpp_type.hash(items[i]); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index d6b1ad3e9e0..e0a3f5ad334 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -90,7 +90,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec mapping_name, result_domain, {0, 0, 0}); MutableSpan<ColorGeometry4f> colors = attribute_out.as_span(); - parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { + threading::parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { for (const int i : range) { TexResult texture_result = {0}; const float3 position = mapping_attribute[i]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc index 4b677dc5c82..d1114713672 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -102,14 +102,6 @@ static void get_result_domain_and_data_type(const GeometrySet &src_geometry, } } -static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) -{ - /* This only updates a cache and can be considered to be logically const. */ - const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); - const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); - return {looptris, looptris_len}; -} - static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, const VArray<float3> &positions, const MutableSpan<int> r_indices, @@ -212,7 +204,7 @@ static void get_closest_mesh_polygons(const Mesh &mesh, Array<int> looptri_indices(positions.size()); get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions); - Span<MLoopTri> looptris = get_mesh_looptris(mesh); + Span<MLoopTri> looptris = bke::mesh_surface_sample::get_mesh_looptris(mesh); for (const int i : positions.index_range()) { const MLoopTri &looptri = looptris[looptri_indices[i]]; r_poly_indices[i] = looptri.poly; @@ -262,32 +254,6 @@ static void get_closest_mesh_corners(const Mesh &mesh, } } -static void get_barycentric_coords(const Mesh &mesh, - const Span<int> looptri_indices, - const Span<float3> positions, - const MutableSpan<float3> r_bary_coords) -{ - BLI_assert(r_bary_coords.size() == positions.size()); - BLI_assert(r_bary_coords.size() == looptri_indices.size()); - - Span<MLoopTri> looptris = get_mesh_looptris(mesh); - - for (const int i : r_bary_coords.index_range()) { - const int looptri_index = looptri_indices[i]; - const MLoopTri &looptri = looptris[looptri_index]; - - const int v0_index = mesh.mloop[looptri.tri[0]].v; - const int v1_index = mesh.mloop[looptri.tri[1]].v; - const int v2_index = mesh.mloop[looptri.tri[2]].v; - - interp_weights_tri_v3(r_bary_coords[i], - mesh.mvert[v0_index].co, - mesh.mvert[v1_index].co, - mesh.mvert[v2_index].co, - positions[i]); - } -} - static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry, GeometryComponent &dst_component, const VArray<float3> &dst_positions, @@ -308,8 +274,11 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_ if (mesh->totpoly == 0) { return; } + ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type); - if (!src_attribute) { + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + dst_name, dst_domain, data_type); + if (!src_attribute || !dst_attribute) { return; } @@ -318,45 +287,10 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_ Array<float3> positions(tot_samples); get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions); - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - dst_name, dst_domain, data_type); - if (!dst_attribute) { - return; - } - GMutableSpan dst_span = dst_attribute.as_span(); - Array<float3> bary_coords; + bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices); + interp.sample_attribute( + src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED); - /* Compute barycentric coordinates only when they are needed. */ - if (src_attribute.domain != ATTR_DOMAIN_FACE) { - bary_coords.reinitialize(tot_samples); - get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords); - } - /* Interpolate the source attribute on the surface. */ - switch (src_attribute.domain) { - case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute( - *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span); - break; - } - case ATTR_DOMAIN_FACE: { - bke::mesh_surface_sample::sample_face_attribute( - *mesh, looptri_indices, *src_attribute.varray, dst_span); - break; - } - case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute( - *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span); - break; - } - case ATTR_DOMAIN_EDGE: { - /* Not yet supported. */ - break; - } - default: { - BLI_assert_unreachable(); - break; - } - } dst_attribute.save(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index b04e04d1cb7..e2cf6e8b480 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -186,7 +186,7 @@ static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a, bool success = try_dispatch_float_math_fl3_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float3 a = span_a[i]; const float3 b = span_b[i]; @@ -218,7 +218,7 @@ static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a, bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float3 a = span_a[i]; const float3 b = span_b[i]; @@ -251,7 +251,7 @@ static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a, bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float3 a = span_a[i]; const float3 b = span_b[i]; @@ -282,7 +282,7 @@ static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a, bool success = try_dispatch_float_math_fl3_fl3_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float3 a = span_a[i]; const float3 b = span_b[i]; @@ -312,7 +312,7 @@ static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a, bool success = try_dispatch_float_math_fl3_fl_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float3 a = span_a[i]; const float b = span_b[i]; @@ -340,7 +340,7 @@ static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a, bool success = try_dispatch_float_math_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float3 in = span_a[i]; const float3 out = math_function(in); @@ -367,7 +367,7 @@ static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a, bool success = try_dispatch_float_math_fl3_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - parallel_for(IndexRange(size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float3 in = span_a[i]; const float out = math_function(in); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc index 4d568ab5c3a..da753dfc11b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc @@ -154,7 +154,7 @@ static void do_vector_rotate_around_axis(const VArray<float3> &vector, VArray_Span<float3> span_axis{axis}; VArray_Span<float> span_angle{angle}; - parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { for (const int i : range) { float angle = (invert) ? -span_angle[i] : span_angle[i]; results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle); @@ -173,7 +173,7 @@ static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector, VArray_Span<float3> span_center{center}; VArray_Span<float> span_angle{angle}; - parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { for (const int i : range) { float angle = (invert) ? -span_angle[i] : span_angle[i]; results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle); @@ -191,7 +191,7 @@ static void do_vector_rotate_euler(const VArray<float3> &vector, VArray_Span<float3> span_center{center}; VArray_Span<float3> span_rotation{rotation}; - parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { for (const int i : range) { results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc index 9b485284b35..dde7191fb69 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc @@ -220,7 +220,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, const Bounds bounds = position_bounds(positions); const Bounds parameter_bounds = input.use_bounds ? bounds : dummy_parameter_bounds(deform_axis); - parallel_for(positions.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(positions.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { const float parameter = process_parameter( positions[i], axis_index, is_negative, input, parameter_bounds); @@ -267,7 +267,7 @@ static void geo_node_curve_deform_exec(GeoNodeExecParams params) spline.evaluated_positions(), spline.evaluated_tangents(), spline.evaluated_normals(), - spline.interpolate_to_evaluated_points(spline.radii()), + spline.interpolate_to_evaluated(spline.radii()), total_length, params.extract_input<bool>("Stretch"), params.extract_input<bool>("Use Bounds")}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index e879ec624c0..fc65d1754e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -94,16 +94,16 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count); - input_spline.sample_based_on_index_factors<float3>( + input_spline.sample_with_index_factors<float3>( input_spline.evaluated_positions(), uniform_samples, output_spline->positions()); - input_spline.sample_based_on_index_factors<float>( - input_spline.interpolate_to_evaluated_points(input_spline.radii()), + input_spline.sample_with_index_factors<float>( + input_spline.interpolate_to_evaluated(input_spline.radii()), uniform_samples, output_spline->radii()); - input_spline.sample_based_on_index_factors<float>( - input_spline.interpolate_to_evaluated_points(input_spline.tilts()), + input_spline.sample_with_index_factors<float>( + input_spline.interpolate_to_evaluated(input_spline.tilts()), uniform_samples, output_spline->tilts()); @@ -123,8 +123,8 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) return false; } - input_spline.sample_based_on_index_factors( - *input_spline.interpolate_to_evaluated_points(*input_attribute), + input_spline.sample_with_index_factors( + *input_spline.interpolate_to_evaluated(*input_attribute), uniform_samples, *output_attribute); @@ -138,19 +138,28 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, const SampleModeParam &mode_param) { - std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + Span<SplinePtr> input_splines = input_curve.splines(); - for (const SplinePtr &spline : input_curve.splines()) { - if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) { - BLI_assert(mode_param.count); - output_curve->add_spline(resample_spline(*spline, *mode_param.count)); - } - else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - BLI_assert(mode_param.length); - const float length = spline->length(); - const int count = std::max(int(length / *mode_param.length), 1); - output_curve->add_spline(resample_spline(*spline, count)); - } + std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + output_curve->resize(input_splines.size()); + MutableSpan<SplinePtr> output_splines = output_curve->splines(); + + if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + BLI_assert(mode_param.count); + output_splines[i] = resample_spline(*input_splines[i], *mode_param.count); + } + }); + } + else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const float length = input_splines[i]->length(); + const int count = std::max(int(length / *mode_param.length), 1); + output_splines[i] = resample_spline(*input_splines[i], count); + } + }); } output_curve->attributes = input_curve.attributes; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc new file mode 100644 index 00000000000..e92d22a6064 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -0,0 +1,132 @@ +/* + * 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 "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_reverse_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_reverse_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +namespace blender::nodes { + +/** + * Reverse the data in a MutableSpan object. + */ +template<typename T> static void reverse_data(MutableSpan<T> r_data) +{ + const int size = r_data.size(); + for (const int i : IndexRange(size / 2)) { + std::swap(r_data[size - 1 - i], r_data[i]); + } +} + +/** + * Reverse and Swap the data between 2 MutableSpans. + */ +template<typename T> static void reverse_data(MutableSpan<T> left, MutableSpan<T> right) +{ + BLI_assert(left.size() == right.size()); + const int size = left.size(); + + for (const int i : IndexRange(size / 2 + size % 2)) { + std::swap(left[i], right[size - 1 - i]); + std::swap(right[i], left[size - 1 - i]); + } +} + +static void geo_node_curve_reverse_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", geometry_set); + return; + } + + /* Retrieve data for write access so we can avoid new allocations for the reversed data. */ + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan<SplinePtr> splines = curve.splines(); + + const std::string selection_name = params.extract_input<std::string>("Selection"); + GVArray_Typed<bool> selection = curve_component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_CURVE, true); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + if (!selection[i]) { + continue; + } + + reverse_data<float3>(splines[i]->positions()); + reverse_data<float>(splines[i]->radii()); + reverse_data<float>(splines[i]->tilts()); + + splines[i]->attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<blender::fn::GMutableSpan> output_attribute = + splines[i]->attributes.get_for_write(name); + if (!output_attribute) { + BLI_assert_unreachable(); + return false; + } + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + reverse_data(output_attribute->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + /* Deal with extra info on derived types. */ + if (BezierSpline *spline = dynamic_cast<BezierSpline *>(splines[i].get())) { + reverse_data<BezierSpline::HandleType>(spline->handle_types_left()); + reverse_data<BezierSpline::HandleType>(spline->handle_types_right()); + reverse_data<float3>(spline->handle_positions_left(), spline->handle_positions_right()); + } + else if (NURBSpline *spline = dynamic_cast<NURBSpline *>(splines[i].get())) { + reverse_data<float>(spline->weights()); + } + /* Nothing to do for poly splines. */ + + splines[i]->mark_cache_invalid(); + } + }); + + params.set_output("Curve", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_reverse() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_reverse_in, geo_node_curve_reverse_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc new file mode 100644 index 00000000000..3de2604cd0a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -0,0 +1,407 @@ +/* + * 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 "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::fn::GVArray_For_GSpan; +using blender::fn::GVArray_For_Span; +using blender::fn::GVArray_Typed; + +static bNodeSocketTemplate geo_node_curve_subdivide_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Cuts")}, + {SOCK_INT, N_("Cuts"), 1, 0, 0, 0, 0, 1000}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_subdivide_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE); +} + +static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN( + sizeof(NodeGeometryCurveSubdivide), __func__); + + data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER; + node->storage = data; +} + +namespace blender::nodes { + +static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage; + + update_attribute_input_socket_availabilities( + *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type); +} + +static Array<int> get_subdivided_offsets(const Spline &spline, + const VArray<int> &cuts, + const int spline_offset) +{ + Array<int> offsets(spline.segments_size() + 1); + int offset = 0; + for (const int i : IndexRange(spline.segments_size())) { + offsets[i] = offset; + offset = offset + std::max(cuts[spline_offset + i], 0) + 1; + } + offsets.last() = offset; + return offsets; +} + +template<typename T> +static void subdivide_attribute(Span<T> src, + const Span<int> offsets, + const bool is_cyclic, + MutableSpan<T> dst) +{ + const int src_size = src.size(); + threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { + for (const int i : range) { + const int cuts = offsets[i + 1] - offsets[i]; + dst[offsets[i]] = src[i]; + const float factor_delta = 1.0f / (cuts + 1.0f); + for (const int cut : IndexRange(cuts)) { + const float factor = (cut + 1) * factor_delta; + dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]); + } + } + }); + + if (is_cyclic) { + const int i = src_size - 1; + const int cuts = offsets[i + 1] - offsets[i]; + dst[offsets[i]] = src.last(); + const float factor_delta = 1.0f / (cuts + 1.0f); + for (const int cut : IndexRange(cuts)) { + const float factor = (cut + 1) * factor_delta; + dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first()); + } + } + else { + dst.last() = src.last(); + } +} + +/** + * De Casteljau Bezier subdivision. + * + * <pre> + * handle_prev handle_next + * O----------------O + * / \ + * / x---O---x \ + * / new_* \ + * / \ + * O O + * point_prev point_next + * </pre> + */ +static void calculate_new_bezier_point(const float3 &point_prev, + float3 &handle_prev, + float3 &new_left_handle, + float3 &new_position, + float3 &new_right_handle, + float3 &handle_next, + const float3 &point_next, + const float parameter) +{ + const float3 center_point = float3::interpolate(handle_prev, handle_next, parameter); + + handle_prev = float3::interpolate(point_prev, handle_prev, parameter); + handle_next = float3::interpolate(handle_next, point_next, parameter); + new_left_handle = float3::interpolate(handle_prev, center_point, parameter); + new_right_handle = float3::interpolate(center_point, handle_next, parameter); + new_position = float3::interpolate(new_left_handle, new_right_handle, parameter); +} + +/** + * In order to generate a Bezier spline with the same shape as the input spline, apply the + * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the + * previous result point's right handle and the left handle at the end of the segment. + * + * \note Non-vector segments in the result spline are given free handles. This could possibly be + * improved with another pass that sets handles to aligned where possible, but currently that does + * not provide much benefit for the increased complexity. + */ +static void subdivide_bezier_segment(const BezierSpline &src, + const int index, + const int offset, + const int result_size, + Span<float3> src_positions, + Span<float3> src_handles_left, + Span<float3> src_handles_right, + MutableSpan<float3> dst_positions, + MutableSpan<float3> dst_handles_left, + MutableSpan<float3> dst_handles_right, + MutableSpan<BezierSpline::HandleType> dst_type_left, + MutableSpan<BezierSpline::HandleType> dst_type_right) +{ + const bool is_last_cyclic_segment = index == (src.size() - 1); + const int next_index = is_last_cyclic_segment ? 0 : index + 1; + if (src.segment_is_vector(index)) { + if (is_last_cyclic_segment) { + dst_type_left.first() = BezierSpline::HandleType::Vector; + } + dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + + dst_positions[offset] = src_positions[index]; + const float factor_delta = 1.0f / result_size; + for (const int cut : IndexRange(result_size)) { + const float factor = cut * factor_delta; + dst_positions[offset + cut] = attribute_math::mix2( + factor, src_positions[index], src_positions[next_index]); + } + } + else { + if (is_last_cyclic_segment) { + dst_type_left.first() = BezierSpline::HandleType::Free; + } + dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); + dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + + const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; + dst_positions[offset] = src_positions[index]; + dst_handles_right[offset] = src_handles_right[index]; + dst_handles_left[i_segment_last] = src_handles_left[next_index]; + + for (const int cut : IndexRange(result_size - 1)) { + const float parameter = 1.0f / (result_size - cut); + calculate_new_bezier_point(dst_positions[offset + cut], + dst_handles_right[offset + cut], + dst_handles_left[offset + cut + 1], + dst_positions[offset + cut + 1], + dst_handles_right[offset + cut + 1], + dst_handles_left[i_segment_last], + src_positions[next_index], + parameter); + } + } +} + +static void subdivide_bezier_spline(const BezierSpline &src, + const Span<int> offsets, + BezierSpline &dst) +{ + Span<float3> src_positions = src.positions(); + Span<float3> src_handles_left = src.handle_positions_left(); + Span<float3> src_handles_right = src.handle_positions_right(); + MutableSpan<float3> dst_positions = dst.positions(); + MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); + MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); + MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left(); + MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right(); + + threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + subdivide_bezier_segment(src, + i, + offsets[i], + offsets[i + 1] - offsets[i], + src_positions, + src_handles_left, + src_handles_right, + dst_positions, + dst_handles_left, + dst_handles_right, + dst_type_left, + dst_type_right); + } + }); + + if (src.is_cyclic()) { + const int i_last = src.size() - 1; + subdivide_bezier_segment(src, + i_last, + offsets[i_last], + offsets.last() - offsets[i_last], + src_positions, + src_handles_left, + src_handles_right, + dst_positions, + dst_handles_left, + dst_handles_right, + dst_type_left, + dst_type_right); + } + else { + dst_positions.last() = src_positions.last(); + } +} + +static void subdivide_builtin_attributes(const Spline &src_spline, + const Span<int> offsets, + Spline &dst_spline) +{ + const bool is_cyclic = src_spline.is_cyclic(); + subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); + subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); + switch (src_spline.type()) { + case Spline::Type::Poly: { + const PolySpline &src = static_cast<const PolySpline &>(src_spline); + PolySpline &dst = static_cast<PolySpline &>(dst_spline); + subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); + break; + } + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); + BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); + subdivide_bezier_spline(src, offsets, dst); + dst.mark_cache_invalid(); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); + NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); + subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); + subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); + break; + } + } +} + +static void subdivide_dynamic_attributes(const Spline &src_spline, + const Span<int> offsets, + Spline &dst_spline) +{ + const bool is_cyclic = src_spline.is_cyclic(); + src_spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = src_spline.attributes.get_for_read(name); + BLI_assert(src); + + if (!dst_spline.attributes.create(name, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. */ + BLI_assert_unreachable(); + } + + std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(name); + BLI_assert(dst); + + attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) { + using T = decltype(dummy); + subdivide_attribute<T>(src->typed<T>(), offsets, is_cyclic, dst->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr subdivide_spline(const Spline &spline, + const VArray<int> &cuts, + const int spline_offset) +{ + /* Since we expect to access each value many times, it should be worth it to make sure the + * attribute is a real span (especially considering the note below). Using the offset at each + * point facilitates subdividing in parallel later. */ + Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); + const int result_size = offsets.last() + int(!spline.is_cyclic()); + SplinePtr new_spline = spline.copy_only_settings(); + new_spline->resize(result_size); + subdivide_builtin_attributes(spline, offsets, *new_spline); + subdivide_dynamic_attributes(spline, offsets, *new_spline); + return new_spline; +} + +/** + * \note Passing the virtual array for the entire spline is possibly quite inefficient here when + * the attribute was on the point domain and stored separately for each spline already, and it + * prevents some other optimizations like skipping splines with a single attribute value of < 1. + * However, it allows the node to access builtin attribute easily, so it the makes most sense this + * way until the attribute API is refactored. + */ +static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve, + const VArray<int> &cuts) +{ + const Array<int> control_point_offsets = input_curve.control_point_offsets(); + const Span<SplinePtr> input_splines = input_curve.splines(); + + std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + output_curve->resize(input_splines.size()); + output_curve->attributes = input_curve.attributes; + MutableSpan<SplinePtr> output_splines = output_curve->splines(); + + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]); + } + }); + + return output_curve; +} + +static void geo_node_subdivide_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Geometry", geometry_set); + return; + } + + const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); + GVArray_Typed<int> cuts = params.get_input_attribute<int>( + "Cuts", component, ATTR_DOMAIN_POINT, 0); + if (cuts->is_single() && cuts->get_internal_single() < 1) { + params.set_output("Geometry", geometry_set); + return; + } + + std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), *cuts); + + params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_subdivide() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_subdivide_in, geo_node_curve_subdivide_out); + ntype.draw_buttons = geo_node_curve_subdivide_layout; + node_type_storage(&ntype, + "NodeGeometryCurveSubdivide", + node_free_standard_storage, + node_copy_standard_storage); + node_type_init(&ntype, geo_node_curve_subdivide_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update); + ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index b6f04352929..c0d817385e2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -16,7 +16,7 @@ #include "BLI_array.hh" #include "BLI_float4x4.hh" -#include "BLI_timeit.hh" +#include "BLI_task.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -47,8 +47,8 @@ static void vert_extrude_to_mesh_data(const Spline &spline, const float3 profile_vert, MutableSpan<MVert> r_verts, MutableSpan<MEdge> r_edges, - int &vert_offset, - int &edge_offset) + int vert_offset, + int edge_offset) { Span<float3> positions = spline.evaluated_positions(); @@ -85,10 +85,10 @@ static void spline_extrude_to_mesh_data(const Spline &spline, MutableSpan<MEdge> r_edges, MutableSpan<MLoop> r_loops, MutableSpan<MPoly> r_polys, - int &vert_offset, - int &edge_offset, - int &loop_offset, - int &poly_offset) + int vert_offset, + int edge_offset, + int loop_offset, + int poly_offset) { const int spline_vert_len = spline.evaluated_points_size(); const int spline_edge_len = spline.evaluated_edges_size(); @@ -181,7 +181,7 @@ static void spline_extrude_to_mesh_data(const Spline &spline, Span<float3> normals = spline.evaluated_normals(); Span<float3> profile_positions = profile_spline.evaluated_positions(); - GVArray_Typed<float> radii = spline.interpolate_to_evaluated_points(spline.radii()); + GVArray_Typed<float> radii = spline.interpolate_to_evaluated(spline.radii()); for (const int i_ring : IndexRange(spline_vert_len)) { float4x4 point_matrix = float4x4::from_normalized_axis_data( positions[i_ring], normals[i_ring], tangents[i_ring]); @@ -207,63 +207,114 @@ static void spline_extrude_to_mesh_data(const Spline &spline, } } -static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve) +static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile) { - int profile_vert_total = 0; - int profile_edge_total = 0; - for (const SplinePtr &profile_spline : profile_curve.splines()) { - profile_vert_total += profile_spline->evaluated_points_size(); - profile_edge_total += profile_spline->evaluated_edges_size(); - } + return curve.evaluated_points_size() * profile.evaluated_points_size(); +} - int vert_total = 0; - int edge_total = 0; - int poly_total = 0; - for (const SplinePtr &spline : curve.splines()) { - const int spline_vert_len = spline->evaluated_points_size(); - const int spline_edge_len = spline->evaluated_edges_size(); - vert_total += spline_vert_len * profile_vert_total; - poly_total += spline_edge_len * profile_edge_total; - - /* Add the ring edges, with one ring for every curve vertex, and the edge loops - * that run along the length of the curve, starting on the first profile. */ - edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len; - } - const int corner_total = poly_total * 4; +static inline int spline_extrude_edge_size(const Spline &curve, const Spline &profile) +{ + /* Add the ring edges, with one ring for every curve vertex, and the edge loops + * that run along the length of the curve, starting on the first profile. */ + return curve.evaluated_points_size() * profile.evaluated_edges_size() + + curve.evaluated_edges_size() * profile.evaluated_points_size(); +} - if (vert_total == 0) { - return nullptr; - } +static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile) +{ + return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; +} - Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; - mesh->flag |= ME_AUTOSMOOTH; - mesh->smoothresh = DEG2RADF(180.0f); +static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile) +{ + return curve.evaluated_edges_size() * profile.evaluated_edges_size(); +} +struct ResultOffsets { + Array<int> vert; + Array<int> edge; + Array<int> loop; + Array<int> poly; +}; +static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<SplinePtr> curves) +{ + const int total = profiles.size() * curves.size(); + Array<int> vert(total + 1); + Array<int> edge(total + 1); + Array<int> loop(total + 1); + Array<int> poly(total + 1); + + int mesh_index = 0; int vert_offset = 0; int edge_offset = 0; int loop_offset = 0; int poly_offset = 0; - for (const SplinePtr &spline : curve.splines()) { - for (const SplinePtr &profile_spline : profile_curve.splines()) { - spline_extrude_to_mesh_data(*spline, - *profile_spline, - verts, - edges, - loops, - polys, - vert_offset, - edge_offset, - loop_offset, - poly_offset); + for (const int i_spline : curves.index_range()) { + for (const int i_profile : profiles.index_range()) { + vert[mesh_index] = vert_offset; + edge[mesh_index] = edge_offset; + loop[mesh_index] = loop_offset; + poly[mesh_index] = poly_offset; + vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]); + edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]); + loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile]); + poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile]); + mesh_index++; } } + vert.last() = vert_offset; + edge.last() = edge_offset; + loop.last() = loop_offset; + poly.last() = poly_offset; + + return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)}; +} + +/** + * \note Normal calculation is by far the slowest part of calculations relating to the result mesh. + * Although it would be a sensible decision to use the better topology information available while + * generating the mesh to also generate the normals, that work may wasted if the output mesh is + * changed anyway in a way that affects the normals. So currently this code uses the safer / + * simpler solution of not calculating normals. + */ +static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile) +{ + Span<SplinePtr> profiles = profile.splines(); + Span<SplinePtr> curves = curve.splines(); + + const ResultOffsets offsets = calculate_result_offsets(profiles, curves); + if (offsets.vert.last() == 0) { + return nullptr; + } - BKE_mesh_calc_normals(mesh); + Mesh *mesh = BKE_mesh_new_nomain( + offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last()); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + mesh->flag |= ME_AUTOSMOOTH; + mesh->smoothresh = DEG2RADF(180.0f); + mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; + + threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) { + for (const int i_spline : curves_range) { + const int spline_start_index = i_spline * profiles.size(); + threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) { + for (const int i_profile : profiles_range) { + const int i_mesh = spline_start_index + i_profile; + spline_extrude_to_mesh_data(*curves[i_spline], + *profiles[i_profile], + {mesh->mvert, mesh->totvert}, + {mesh->medge, mesh->totedge}, + {mesh->mloop, mesh->totloop}, + {mesh->mpoly, mesh->totpoly}, + offsets.vert[i_mesh], + offsets.edge[i_mesh], + offsets.loop[i_mesh], + offsets.poly[i_mesh]); + } + }); + } + }); return mesh; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc new file mode 100644 index 00000000000..2725c625913 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -0,0 +1,390 @@ +/* + * 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 "BLI_array.hh" +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_to_points_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_INT, N_("Count"), 10, 0, 0, 0, 2, 100000}, + {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_to_points_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( + sizeof(NodeGeometryCurveToPoints), __func__); + + data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + node->storage = data; +} + +static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *length_socket = count_socket->next; + + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); +} + +namespace blender::nodes { + +/** + * Evaluate splines in parallel to speed up the rest of the node's execution. + */ +static void evaluate_splines(Span<SplinePtr> splines) +{ + threading::parallel_for_each(splines, [](const SplinePtr &spline) { + /* These functions fill the corresponding caches on each spline. */ + spline->evaluated_positions(); + spline->evaluated_tangents(); + spline->evaluated_normals(); + spline->evaluated_lengths(); + }); +} + +static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, + const GeometryNodeCurveSampleMode mode, + const CurveEval &curve, + const Span<SplinePtr> splines) +{ + const int size = curve.splines().size(); + switch (mode) { + case GEO_NODE_CURVE_SAMPLE_COUNT: { + const int count = params.extract_input<int>("Count"); + if (count < 1) { + return {0}; + } + Array<int> offsets(size + 1); + for (const int i : offsets.index_range()) { + offsets[i] = count * i; + } + return offsets; + } + case GEO_NODE_CURVE_SAMPLE_LENGTH: { + /* Don't allow asymptotic count increase for low resolution values. */ + const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f); + Array<int> offsets(size + 1); + int offset = 0; + for (const int i : IndexRange(size)) { + offsets[i] = offset; + offset += splines[i]->length() / resolution; + } + offsets.last() = offset; + return offsets; + } + case GEO_NODE_CURVE_SAMPLE_EVALUATED: { + return curve.evaluated_point_offsets(); + } + } + BLI_assert_unreachable(); + return {0}; +} + +/** + * \note This doesn't store a map for spline domain attributes. + */ +struct ResultAttributes { + int result_size; + MutableSpan<float3> positions; + MutableSpan<float> radii; + MutableSpan<float> tilts; + + Map<std::string, GMutableSpan> point_attributes; + + MutableSpan<float3> tangents; + MutableSpan<float3> normals; + MutableSpan<float3> rotations; +}; + +static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, + const StringRef name, + const CustomDataType data_type) +{ + points.attribute_try_create(name, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); + WriteAttributeLookup attribute = points.attribute_try_get_for_write(name); + BLI_assert(attribute); + return attribute.varray->get_internal_span(); +} + +template<typename T> +static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points, + const StringRef name) +{ + GMutableSpan attribute = create_attribute_and_retrieve_span( + points, name, bke::cpp_type_to_custom_data_type(CPPType::get<T>())); + return attribute.typed<T>(); +} + +/** + * Create references for all result point cloud attributes to simplify accessing them later on. + */ +static ResultAttributes create_point_attributes(PointCloudComponent &points, + const CurveEval &curve) +{ + ResultAttributes attributes; + + attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT); + + attributes.positions = create_attribute_and_retrieve_span<float3>(points, "position"); + attributes.radii = create_attribute_and_retrieve_span<float>(points, "radius"); + attributes.tilts = create_attribute_and_retrieve_span<float>(points, "tilt"); + + /* Because of the invariants of the curve component, we use the attributes of the + * first spline as a representative for the attribute meta data all splines. */ + curve.splines().first()->attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + attributes.point_attributes.add_new( + name, create_attribute_and_retrieve_span(points, name, meta_data.data_type)); + return true; + }, + ATTR_DOMAIN_POINT); + + attributes.tangents = create_attribute_and_retrieve_span<float3>(points, "tangent"); + attributes.normals = create_attribute_and_retrieve_span<float3>(points, "normal"); + attributes.rotations = create_attribute_and_retrieve_span<float3>(points, "rotation"); + + return attributes; +} + +/** + * TODO: For non-poly splines, this has double copies that could be avoided as part + * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. + */ +static void copy_evaluated_point_attributes(Span<SplinePtr> splines, + Span<int> offsets, + ResultAttributes &data) +{ + threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + + data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); + spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size)); + spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size)); + + for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) { + const StringRef name = item.key; + GMutableSpan point_span = item.value; + + BLI_assert(spline.attributes.get_for_read(name)); + GSpan spline_span = *spline.attributes.get_for_read(name); + + spline.interpolate_to_evaluated(spline_span) + ->materialize(point_span.slice(offset, size).data()); + } + + data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); + data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); + } + }); +} + +static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines, + Span<int> offsets, + ResultAttributes &data) +{ + threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + if (size == 0) { + continue; + } + + const Array<float> uniform_samples = spline.sample_uniform_index_factors(size); + + spline.sample_with_index_factors<float3>( + spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); + + spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), + uniform_samples, + data.radii.slice(offset, size)); + + spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.tilts()), + uniform_samples, + data.tilts.slice(offset, size)); + + for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) { + const StringRef name = item.key; + GMutableSpan point_span = item.value; + + BLI_assert(spline.attributes.get_for_read(name)); + GSpan spline_span = *spline.attributes.get_for_read(name); + + spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span), + uniform_samples, + point_span.slice(offset, size)); + } + + spline.sample_with_index_factors<float3>( + spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); + for (float3 &tangent : data.tangents) { + tangent.normalize(); + } + + spline.sample_with_index_factors<float3>( + spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); + for (float3 &normals : data.normals) { + normals.normalize(); + } + } + }); +} + +/** + * \note Use attributes from the curve component rather than the attribute data directly on the + * attribute storage to allow reading the virtual spline attributes like "cyclic" and "resolution". + */ +static void copy_spline_domain_attributes(const CurveComponent &curve_component, + Span<int> offsets, + PointCloudComponent &points) +{ + curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { + if (meta_data.domain != ATTR_DOMAIN_CURVE) { + return true; + } + GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + name, ATTR_DOMAIN_CURVE, meta_data.data_type); + const CPPType &type = spline_attribute->type(); + + OutputAttribute result_attribute = points.attribute_try_get_for_output_only( + name, ATTR_DOMAIN_POINT, meta_data.data_type); + GMutableSpan result = result_attribute.as_span(); + + for (const int i : IndexRange(spline_attribute->size())) { + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + if (size != 0) { + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + spline_attribute->get(i, buffer); + type.fill_initialized(buffer, result[offset], size); + } + } + + result_attribute.save(); + return true; + }); +} + +static void create_default_rotation_attribute(ResultAttributes &data) +{ + threading::parallel_for(IndexRange(data.result_size), 512, [&](IndexRange range) { + for (const int i : range) { + data.rotations[i] = float4x4::from_normalized_axis_data( + {0, 0, 0}, data.normals[i], data.tangents[i]) + .to_euler(); + } + }); +} + +static void geo_node_curve_to_points_exec(GeoNodeExecParams params) +{ + NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); + const CurveEval &curve = *curve_component.get_for_read(); + const Span<SplinePtr> splines = curve.splines(); + curve.assert_valid_point_attributes(); + + evaluate_splines(splines); + + const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const int total_size = offsets.last(); + if (total_size == 0) { + params.set_output("Geometry", GeometrySet()); + return; + } + + GeometrySet result = GeometrySet::create_with_pointcloud(BKE_pointcloud_new_nomain(total_size)); + PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>(); + + ResultAttributes new_attributes = create_point_attributes(point_component, curve); + + switch (mode) { + case GEO_NODE_CURVE_SAMPLE_COUNT: + case GEO_NODE_CURVE_SAMPLE_LENGTH: + copy_uniform_sample_point_attributes(splines, offsets, new_attributes); + break; + case GEO_NODE_CURVE_SAMPLE_EVALUATED: + copy_evaluated_point_attributes(splines, offsets, new_attributes); + break; + } + + copy_spline_domain_attributes(curve_component, offsets, point_component); + create_default_rotation_attribute(new_attributes); + + /* The default radius is way too large for points, divide by 10. */ + for (float &radius : new_attributes.radii) { + radius *= 0.1f; + } + + params.set_output("Geometry", std::move(result)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_to_points() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_to_points_in, geo_node_curve_to_points_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec; + ntype.draw_buttons = geo_node_curve_to_points_layout; + node_type_storage( + &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_curve_to_points_init); + node_type_update(&ntype, geo_node_curve_to_points_update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 910adc467d6..b1da2dcd3c4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -120,7 +120,7 @@ static void copy_dynamic_attributes(const CustomDataAttributes &src, static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) { - SplinePtr new_spline = spline.copy_settings(); + SplinePtr new_spline = spline.copy_only_settings(); new_spline->resize(mask.size()); spline_copy_builtin_attributes(spline, *new_spline, mask); diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index adfd924f185..bc758b59987 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -23,8 +23,12 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "NOD_type_conversions.hh" + #include "node_geometry_util.hh" +using blender::fn::GVArray_For_GSpan; + static bNodeSocketTemplate geo_node_join_geometry_in[] = { {SOCK_GEOMETRY, N_("Geometry"), @@ -79,7 +83,7 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent const Mesh *first_input_mesh = src_components[0]->get_for_read(); Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys); - BKE_mesh_copy_settings(new_mesh, first_input_mesh); + BKE_mesh_copy_parameters_for_eval(new_mesh, first_input_mesh); for (const int i : IndexRange(materials.size())) { Material *material = materials[i]; @@ -157,35 +161,30 @@ static Array<const GeometryComponent *> to_base_components(Span<const Component return components; } -static Set<std::string> find_all_attribute_names(Span<const GeometryComponent *> components) +static Map<std::string, AttributeMetaData> get_final_attribute_info( + Span<const GeometryComponent *> components, Span<StringRef> ignored_attributes) { - Set<std::string> attribute_names; - for (const GeometryComponent *component : components) { - Set<std::string> names = component->attribute_names(); - for (const std::string &name : names) { - attribute_names.add(name); - } - } - return attribute_names; -} + Map<std::string, AttributeMetaData> info; -static void determine_final_data_type_and_domain(Span<const GeometryComponent *> components, - StringRef attribute_name, - CustomDataType *r_type, - AttributeDomain *r_domain) -{ - Vector<CustomDataType> data_types; - Vector<AttributeDomain> domains; for (const GeometryComponent *component : components) { - ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name); - if (attribute) { - data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type())); - domains.append(attribute.domain); - } + component->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { + if (ignored_attributes.contains(name)) { + return true; + } + info.add_or_modify( + name, + [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, + [&](AttributeMetaData *meta_data_final) { + meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity( + {meta_data_final->data_type, meta_data.data_type}); + meta_data_final->domain = blender::bke::attribute_domain_highest_priority( + {meta_data_final->domain, meta_data.domain}); + }); + return true; + }); } - *r_type = bke::attribute_data_type_highest_complexity(data_types); - *r_domain = bke::attribute_domain_highest_priority(domains); + return info; } static void fill_new_attribute(Span<const GeometryComponent *> src_components, @@ -219,23 +218,20 @@ static void join_attributes(Span<const GeometryComponent *> src_components, GeometryComponent &result, Span<StringRef> ignored_attributes = {}) { - Set<std::string> attribute_names = find_all_attribute_names(src_components); - for (StringRef name : ignored_attributes) { - attribute_names.remove(name); - } + const Map<std::string, AttributeMetaData> info = get_final_attribute_info(src_components, + ignored_attributes); - for (const std::string &attribute_name : attribute_names) { - CustomDataType data_type; - AttributeDomain domain; - determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain); + for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) { + const StringRef name = item.key; + const AttributeMetaData &meta_data = item.value; OutputAttribute write_attribute = result.attribute_try_get_for_output_only( - attribute_name, domain, data_type); + name, meta_data.domain, meta_data.data_type); if (!write_attribute) { continue; } GMutableSpan dst_span = write_attribute.as_span(); - fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span); + fill_new_attribute(src_components, name, meta_data.data_type, meta_data.domain, dst_span); write_attribute.save(); } } @@ -306,6 +302,127 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet UNUSED_VARS(src_components, dst_component); } +/** + * \note This takes advantage of the fact that creating attributes on joined curves never + * changes a point attribute into a spline attribute; it is always the other way around. + */ +static void ensure_control_point_attribute(const StringRef name, + const CustomDataType data_type, + Span<CurveComponent *> src_components, + CurveEval &result) +{ + MutableSpan<SplinePtr> splines = result.splines(); + const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); + + /* In order to fill point attributes with spline domain attribute values where necessary, keep + * track of the curve each spline came from while iterating over the splines in the result. */ + int src_component_index = 0; + int spline_index_in_component = 0; + const CurveEval *current_curve = src_components[src_component_index]->get_for_read(); + + for (SplinePtr &spline : splines) { + std::optional<GSpan> attribute = spline->attributes.get_for_read(name); + + if (attribute) { + if (attribute->type() != type) { + /* In this case, the attribute exists, but it has the wrong type. So create a buffer + * for the converted values, do the conversion, and then replace the attribute. */ + void *converted_buffer = MEM_mallocN_aligned( + spline->size() * type.size(), type.alignment(), __func__); + + const DataTypeConversions &conversions = blender::nodes::get_implicit_type_conversions(); + conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), type) + ->materialize(converted_buffer); + + spline->attributes.remove(name); + spline->attributes.create_by_move(name, data_type, converted_buffer); + } + } + else { + spline->attributes.create(name, data_type); + + if (current_curve->attributes.get_for_read(name)) { + /* In this case the attribute did not exist, but there is a spline domain attribute + * we can retrieve a value from, as a spline to point domain conversion. So fill the + * new attribute with the value for this spline. */ + GVArrayPtr current_curve_attribute = current_curve->attributes.get_for_read( + name, data_type, nullptr); + + BLI_assert(spline->attributes.get_for_read(name)); + std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(name); + + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + current_curve_attribute->get(spline_index_in_component, buffer); + type.fill_initialized(buffer, new_attribute->data(), new_attribute->size()); + } + } + + /* Move to the next spline and maybe the next input component. */ + spline_index_in_component++; + if (spline != splines.last() && spline_index_in_component >= current_curve->splines().size()) { + src_component_index++; + spline_index_in_component = 0; + + current_curve = src_components[src_component_index]->get_for_read(); + } + } +} + +/** + * Fill data for an attribute on the new curve based on all source curves. + */ +static void ensure_spline_attribute(const StringRef name, + const CustomDataType data_type, + Span<CurveComponent *> src_components, + CurveEval &result) +{ + const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); + + result.attributes.create(name, data_type); + GMutableSpan result_attribute = *result.attributes.get_for_write(name); + + int offset = 0; + for (const CurveComponent *component : src_components) { + const CurveEval &curve = *component->get_for_read(); + const int size = curve.splines().size(); + if (size == 0) { + continue; + } + GVArrayPtr read_attribute = curve.attributes.get_for_read(name, data_type, nullptr); + GVArray_GSpan src_span{*read_attribute}; + + const void *src_buffer = src_span.data(); + type.copy_to_initialized_n(src_buffer, result_attribute[offset], size); + + offset += size; + } +} + +/** + * Special handling for copying spline attributes. This is necessary because we move the splines + * out of the source components instead of copying them, meaning we can no longer access point + * domain attributes on the source components. + * + * \warning Splines have been moved out of the source components at this point, so it + * is important to only read curve-level data (spline domain attributes) from them. + */ +static void join_curve_attributes(const Map<std::string, AttributeMetaData> &info, + Span<CurveComponent *> src_components, + CurveEval &result) +{ + for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) { + const StringRef name = item.key; + const AttributeMetaData meta_data = item.value; + + if (meta_data.domain == ATTR_DOMAIN_CURVE) { + ensure_spline_attribute(name, meta_data.data_type, src_components, result); + } + else { + ensure_control_point_attribute(name, meta_data.data_type, src_components, result); + } + } +} + static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result) { Vector<CurveComponent *> src_components; @@ -328,6 +445,11 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge return; } + /* Retrieve attribute info before moving the splines out of the input components. */ + const Map<std::string, AttributeMetaData> info = get_final_attribute_info( + {(const GeometryComponent **)src_components.data(), src_components.size()}, + {"position", "radius", "tilt", "cyclic", "resolution"}); + CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); CurveEval *dst_curve = new CurveEval(); for (CurveComponent *component : src_components) { @@ -336,14 +458,9 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge dst_curve->add_spline(std::move(spline)); } } - - /* For now, remove all custom attributes, since they might have different types, - * or an attribute might not exist on all splines. */ dst_curve->attributes.reallocate(dst_curve->splines().size()); - CustomData_reset(&dst_curve->attributes.data); - for (SplinePtr &spline : dst_curve->splines()) { - CustomData_reset(&spline->attributes.data); - } + + join_curve_attributes(info, src_components, *dst_curve); dst_component.replace(dst_curve); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 9651301cb34..3a93bc22b7e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -43,6 +43,7 @@ Mesh *create_cube_mesh(const float size) const BMeshCreateParams bmcp = {true}; const BMAllocTemplate allocsize = {8, 12, 24, 6}; BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + BM_data_layer_add_named(bm, &bm->ldata, CD_MLOOPUV, nullptr); BMO_op_callf(bm, BMO_FLAG_DEFAULTS, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index a3a1b72006c..22195b9e8db 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -26,7 +26,7 @@ static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_in[] = { {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 0, 7}, + {SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 1, 7}, {-1, ""}, }; @@ -44,6 +44,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) const BMeshCreateParams bmcp = {true}; const BMAllocTemplate allocsize = {0, 0, 0, 0}; BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + BM_data_layer_add_named(bm, &bm->ldata, CD_MLOOPUV, nullptr); BMO_op_callf(bm, BMO_FLAG_DEFAULTS, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 0fb7910c904..637003a46c7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -62,7 +62,7 @@ static void copy_attributes_to_points(CurveEval &curve, if (source_attribute_names.contains_as("tilt")) { const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>( "tilt", ATTR_DOMAIN_POINT, 0.0f); - parallel_for(splines.index_range(), 256, [&](IndexRange range) { + threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { for (const int i : range) { copy_attribute_to_points<float>( *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); @@ -73,7 +73,7 @@ static void copy_attributes_to_points(CurveEval &curve, if (source_attribute_names.contains_as("radius")) { const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>( "radius", ATTR_DOMAIN_POINT, 1.0f); - parallel_for(splines.index_range(), 256, [&](IndexRange range) { + threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { for (const int i : range) { copy_attribute_to_points<float>( *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); @@ -97,7 +97,7 @@ static void copy_attributes_to_points(CurveEval &curve, const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); - parallel_for(splines.index_range(), 128, [&](IndexRange range) { + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { /* Create attribute on the spline points. */ splines[i]->attributes.create(name, data_type); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index e52ab1b2127..b119b7b31e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -189,7 +189,7 @@ static void add_instances_from_component(InstancesComponent &instances, * (anything except for collection mode with "Whole Collection" turned off). */ if (possible_handles.size() == 1) { const int handle = possible_handles.first(); - parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { + threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { for (const int i : range) { handles[i] = handle; transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); @@ -200,7 +200,7 @@ static void add_instances_from_component(InstancesComponent &instances, else { const int seed = params.get_input<int>("Seed"); Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT); - parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { + threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { for (const int i : range) { const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size(); const int handle = possible_handles[index]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc new file mode 100644 index 00000000000..bfd6027e0fc --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -0,0 +1,321 @@ +/* + * 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 "DNA_mesh_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_raycast_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_GEOMETRY, N_("Target Geometry")}, + {SOCK_STRING, N_("Ray Direction")}, + {SOCK_VECTOR, N_("Ray Direction"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_STRING, N_("Ray Length")}, + {SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_STRING, N_("Target Attribute")}, + {SOCK_STRING, N_("Is Hit")}, + {SOCK_STRING, N_("Hit Position")}, + {SOCK_STRING, N_("Hit Normal")}, + {SOCK_STRING, N_("Hit Distance")}, + {SOCK_STRING, N_("Hit Attribute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_raycast_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); + uiItemR(layout, ptr, "input_type_ray_direction", 0, IFACE_("Ray Direction"), ICON_NONE); + uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE); +} + +static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), + __func__); + data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node->storage = data; +} + +static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; + blender::nodes::update_attribute_input_socket_availabilities( + *node, + "Ray Direction", + (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); + blender::nodes::update_attribute_input_socket_availabilities( + *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); +} + +namespace blender::nodes { + +static void raycast_to_mesh(const Mesh *mesh, + const VArray<float3> &ray_origins, + const VArray<float3> &ray_directions, + const VArray<float> &ray_lengths, + const MutableSpan<bool> r_hit, + const MutableSpan<int> r_hit_indices, + const MutableSpan<float3> r_hit_positions, + const MutableSpan<float3> r_hit_normals, + const MutableSpan<float> r_hit_distances) +{ + BLI_assert(ray_origins.size() == ray_directions.size()); + BLI_assert(ray_origins.size() == ray_lengths.size()); + BLI_assert(ray_origins.size() == r_hit.size() || r_hit.is_empty()); + BLI_assert(ray_origins.size() == r_hit_indices.size() || r_hit_indices.is_empty()); + BLI_assert(ray_origins.size() == r_hit_positions.size() || r_hit_positions.is_empty()); + BLI_assert(ray_origins.size() == r_hit_normals.size() || r_hit_normals.is_empty()); + BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty()); + + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 4); + + if (tree_data.tree != nullptr) { + for (const int i : ray_origins.index_range()) { + const float ray_length = ray_lengths[i]; + const float3 ray_origin = ray_origins[i]; + const float3 ray_direction = ray_directions[i].normalized(); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = ray_length; + if (BLI_bvhtree_ray_cast(tree_data.tree, + ray_origin, + ray_direction, + 0.0f, + &hit, + tree_data.raycast_callback, + &tree_data) != -1) { + if (!r_hit.is_empty()) { + r_hit[i] = hit.index >= 0; + } + if (!r_hit_indices.is_empty()) { + /* Index should always be a valid looptri index, use 0 when hit failed. */ + r_hit_indices[i] = max_ii(hit.index, 0); + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = hit.co; + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = hit.no; + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = hit.dist; + } + } + else { + if (!r_hit.is_empty()) { + r_hit[i] = false; + } + if (!r_hit_indices.is_empty()) { + r_hit_indices[i] = 0; + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = ray_length; + } + } + } + + free_bvhtree_from_mesh(&tree_data); + } +} + +static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( + GeometryNodeRaycastMapMode map_mode) +{ + switch (map_mode) { + case GEO_NODE_RAYCAST_INTERPOLATED: + return bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED; + default: + case GEO_NODE_RAYCAST_NEAREST: + return bke::mesh_surface_sample::eAttributeMapMode::NEAREST; + } +} + +static void raycast_from_points(const GeoNodeExecParams ¶ms, + const GeometrySet &src_geometry, + GeometryComponent &dst_component, + const StringRef hit_name, + const StringRef hit_position_name, + const StringRef hit_normal_name, + const StringRef hit_distance_name, + const Span<std::string> hit_attribute_names, + const Span<std::string> hit_attribute_output_names) +{ + BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size()); + + const MeshComponent *src_mesh_component = src_geometry.get_component_for_read<MeshComponent>(); + if (src_mesh_component == nullptr) { + return; + } + const Mesh *src_mesh = src_mesh_component->get_for_read(); + if (src_mesh == nullptr) { + return; + } + if (src_mesh->totpoly == 0) { + return; + } + + const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage; + bke::mesh_surface_sample::eAttributeMapMode map_mode = get_map_mode( + (GeometryNodeRaycastMapMode)storage.mapping); + const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + + GVArray_Typed<float3> ray_origins = dst_component.attribute_get_for_read<float3>( + "position", result_domain, {0, 0, 0}); + GVArray_Typed<float3> ray_directions = params.get_input_attribute<float3>( + "Ray Direction", dst_component, result_domain, {0, 0, 0}); + GVArray_Typed<float> ray_lengths = params.get_input_attribute<float>( + "Ray Length", dst_component, result_domain, 0); + + OutputAttribute_Typed<bool> hit_attribute = + dst_component.attribute_try_get_for_output_only<bool>(hit_name, result_domain); + OutputAttribute_Typed<float3> hit_position_attribute = + dst_component.attribute_try_get_for_output_only<float3>(hit_position_name, result_domain); + OutputAttribute_Typed<float3> hit_normal_attribute = + dst_component.attribute_try_get_for_output_only<float3>(hit_normal_name, result_domain); + OutputAttribute_Typed<float> hit_distance_attribute = + dst_component.attribute_try_get_for_output_only<float>(hit_distance_name, result_domain); + + /* Positions and looptri indices are always needed for interpolation, + * so create temporary arrays if no output attribute is given. + */ + Array<int> hit_indices; + Array<float3> hit_positions_internal; + if (!hit_attribute_names.is_empty()) { + hit_indices.reinitialize(ray_origins->size()); + + if (!hit_position_attribute) { + hit_positions_internal.reinitialize(ray_origins->size()); + } + } + const MutableSpan<bool> is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan<bool>(); + const MutableSpan<float3> hit_positions = hit_position_attribute ? + hit_position_attribute.as_span() : + hit_positions_internal; + const MutableSpan<float3> hit_normals = hit_normal_attribute ? hit_normal_attribute.as_span() : + MutableSpan<float3>(); + const MutableSpan<float> hit_distances = hit_distance_attribute ? + hit_distance_attribute.as_span() : + MutableSpan<float>(); + + raycast_to_mesh(src_mesh, + ray_origins, + ray_directions, + ray_lengths, + is_hit, + hit_indices, + hit_positions, + hit_normals, + hit_distances); + + hit_attribute.save(); + hit_position_attribute.save(); + hit_normal_attribute.save(); + hit_distance_attribute.save(); + + /* Custom interpolated attributes */ + bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices); + for (const int i : hit_attribute_names.index_range()) { + const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data( + hit_attribute_names[i]); + if (meta_data) { + ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( + hit_attribute_names[i]); + OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only( + hit_attribute_output_names[i], result_domain, meta_data->data_type); + + interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); + + hit_attribute_output.save(); + } + } +} + +static void geo_node_raycast_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet cast_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); + + const std::string hit_name = params.extract_input<std::string>("Is Hit"); + const std::string hit_position_name = params.extract_input<std::string>("Hit Position"); + const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal"); + const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance"); + + const Array<std::string> hit_attribute_names = { + params.extract_input<std::string>("Target Attribute")}; + const Array<std::string> hit_attribute_output_names = { + params.extract_input<std::string>("Hit Attribute")}; + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + cast_geometry_set = bke::geometry_set_realize_instances(cast_geometry_set); + + static const Array<GeometryComponentType> SupportedTypes = { + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + for (GeometryComponentType geo_type : SupportedTypes) { + if (geometry_set.has(geo_type)) { + raycast_from_points(params, + cast_geometry_set, + geometry_set.get_component_for_write(geo_type), + hit_name, + hit_position_name, + hit_normal_name, + hit_distance_name, + hit_attribute_names, + hit_attribute_output_names); + } + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_raycast() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_init(&ntype, geo_node_raycast_init); + node_type_update(&ntype, geo_node_raycast_update); + node_type_storage( + &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec; + ntype.draw_buttons = geo_node_raycast_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc index 9bc963eec43..ee114741a77 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc @@ -60,7 +60,7 @@ static void select_mesh_by_material(const Mesh &mesh, material_indices.append(i); } } - parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) { + threading::parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc new file mode 100644 index 00000000000..bdc3e56783c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_join_geometry_in[]{ + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_join_geometry_out[]{ + {SOCK_GEOMETRY, N_("Mesh")}, + {SOCK_GEOMETRY, N_("Point Cloud")}, + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_GEOMETRY, N_("Volume")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void geo_node_separate_components_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + /* Note that it will be possible to skip realizing instances here when instancing + * geometry directly is supported by creating corresponding geometry instances. */ + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + GeometrySet meshes; + GeometrySet point_clouds; + GeometrySet volumes; + GeometrySet curves; + + if (geometry_set.has<MeshComponent>()) { + meshes.add(*geometry_set.get_component_for_read<MeshComponent>()); + } + if (geometry_set.has<PointCloudComponent>()) { + point_clouds.add(*geometry_set.get_component_for_read<PointCloudComponent>()); + } + if (geometry_set.has<CurveComponent>()) { + curves.add(*geometry_set.get_component_for_read<CurveComponent>()); + } + if (geometry_set.has<VolumeComponent>()) { + volumes.add(*geometry_set.get_component_for_read<VolumeComponent>()); + } + + params.set_output("Mesh", meshes); + params.set_output("Point Cloud", point_clouds); + params.set_output("Curve", curves); + params.set_output("Volume", volumes); +} + +} // namespace blender::nodes + +void register_node_type_geo_separate_components() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SEPARATE_COMPONENTS, "Separate Components", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_join_geometry_in, geo_node_join_geometry_out); + ntype.geometry_node_execute = blender::nodes::geo_node_separate_components_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index cfa790780f2..9a3eb574bcd 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -84,6 +84,16 @@ bool DerivedNodeTree::has_link_cycles() const return false; } +bool DerivedNodeTree::has_undefined_nodes_or_sockets() const +{ + for (const NodeTreeRef *tree_ref : used_node_tree_refs_) { + if (tree_ref->has_undefined_nodes_or_sockets()) { + return true; + } + } + return false; +} + /* Calls the given callback on all nodes in the (possibly nested) derived node tree. */ void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const { diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 188d198e159..17a13f2d1b0 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -97,6 +97,12 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer); return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } + if (found_socket->type == SOCK_INT) { + const int value = this->get_input<int>(found_socket->identifier); + BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); + conversions.convert_to_uninitialized(CPPType::get<int>(), *cpp_type, &value, buffer); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); + } if (found_socket->type == SOCK_VECTOR) { const float3 value = this->get_input<float3>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 8699736e543..154ee716153 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include <mutex> + #include "NOD_node_tree_ref.hh" #include "BLI_dot_export.hh" @@ -105,6 +107,7 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) } } + this->create_socket_identifier_maps(); this->create_linked_socket_caches(); for (NodeRef *node : nodes_by_id_) { @@ -316,6 +319,89 @@ void OutputSocketRef::foreach_logical_target( } } +namespace { +struct SocketByIdentifierMap { + SocketIndexByIdentifierMap *map = nullptr; + std::unique_ptr<SocketIndexByIdentifierMap> owned_map; +}; +} // namespace + +static std::unique_ptr<SocketIndexByIdentifierMap> create_identifier_map(const ListBase &sockets) +{ + std::unique_ptr<SocketIndexByIdentifierMap> map = std::make_unique<SocketIndexByIdentifierMap>(); + int index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &sockets, index) { + map->add_new(socket->identifier, index); + } + return map; +} + +/* This function is not threadsafe. */ +static SocketByIdentifierMap get_or_create_identifier_map( + const bNode &node, const ListBase &sockets, const bNodeSocketTemplate *sockets_template) +{ + SocketByIdentifierMap map; + if (sockets_template == nullptr) { + if (BLI_listbase_is_empty(&sockets)) { + static SocketIndexByIdentifierMap empty_map; + map.map = &empty_map; + } + else if (node.type == NODE_REROUTE) { + if (&node.inputs == &sockets) { + static SocketIndexByIdentifierMap reroute_input_map = [] { + SocketIndexByIdentifierMap map; + map.add_new("Input", 0); + return map; + }(); + map.map = &reroute_input_map; + } + else { + static SocketIndexByIdentifierMap reroute_output_map = [] { + SocketIndexByIdentifierMap map; + map.add_new("Output", 0); + return map; + }(); + map.map = &reroute_output_map; + } + } + else { + /* The node has a dynamic amount of sockets. Therefore we need to create a new map. */ + map.owned_map = create_identifier_map(sockets); + map.map = &*map.owned_map; + } + } + else { + /* Cache only one map for nodes that have the same sockets. */ + static Map<const bNodeSocketTemplate *, std::unique_ptr<SocketIndexByIdentifierMap>> maps; + map.map = &*maps.lookup_or_add_cb(sockets_template, + [&]() { return create_identifier_map(sockets); }); + } + return map; +} + +void NodeTreeRef::create_socket_identifier_maps() +{ + /* `get_or_create_identifier_map` is not threadsafe, therefore we have to hold a lock here. */ + static std::mutex mutex; + std::lock_guard lock{mutex}; + + for (NodeRef *node : nodes_by_id_) { + bNode &bnode = *node->bnode_; + SocketByIdentifierMap inputs_map = get_or_create_identifier_map( + bnode, bnode.inputs, bnode.typeinfo->inputs); + SocketByIdentifierMap outputs_map = get_or_create_identifier_map( + bnode, bnode.outputs, bnode.typeinfo->outputs); + node->input_index_by_identifier_ = inputs_map.map; + node->output_index_by_identifier_ = outputs_map.map; + if (inputs_map.owned_map) { + owned_identifier_maps_.append(std::move(inputs_map.owned_map)); + } + if (outputs_map.owned_map) { + owned_identifier_maps_.append(std::move(outputs_map.owned_map)); + } + } +} + static bool has_link_cycles_recursive(const NodeRef &node, MutableSpan<bool> visited, MutableSpan<bool> is_in_stack) @@ -358,6 +444,21 @@ bool NodeTreeRef::has_link_cycles() const return false; } +bool NodeTreeRef::has_undefined_nodes_or_sockets() const +{ + for (const NodeRef *node : nodes_by_id_) { + if (node->is_undefined()) { + return true; + } + } + for (const SocketRef *socket : sockets_by_id_) { + if (socket->is_undefined()) { + return true; + } + } + return true; +} + std::string NodeTreeRef::to_dot() const { dot::DirectedGraph digraph; diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index 090c6216224..6baaa17f956 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -61,35 +61,60 @@ static void node_shader_exec_mix_rgb(void *UNUSED(data), copy_v3_v3(out[0]->vec, col); } +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case MA_RAMP_BLEND: + return "mix_blend"; + case MA_RAMP_ADD: + return "mix_add"; + case MA_RAMP_MULT: + return "mix_mult"; + case MA_RAMP_SUB: + return "mix_sub"; + case MA_RAMP_SCREEN: + return "mix_screen"; + case MA_RAMP_DIV: + return "mix_div"; + case MA_RAMP_DIFF: + return "mix_diff"; + case MA_RAMP_DARK: + return "mix_dark"; + case MA_RAMP_LIGHT: + return "mix_light"; + case MA_RAMP_OVERLAY: + return "mix_overlay"; + case MA_RAMP_DODGE: + return "mix_dodge"; + case MA_RAMP_BURN: + return "mix_burn"; + case MA_RAMP_HUE: + return "mix_hue"; + case MA_RAMP_SAT: + return "mix_sat"; + case MA_RAMP_VAL: + return "mix_val"; + case MA_RAMP_COLOR: + return "mix_color"; + case MA_RAMP_SOFT: + return "mix_soft"; + case MA_RAMP_LINEAR: + return "mix_linear"; + } + + return nullptr; +} + static int gpu_shader_mix_rgb(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - static const char *names[] = { - "mix_blend", - "mix_add", - "mix_mult", - "mix_sub", - "mix_screen", - "mix_div", - "mix_diff", - "mix_dark", - "mix_light", - "mix_overlay", - "mix_dodge", - "mix_burn", - "mix_hue", - "mix_sat", - "mix_val", - "mix_color", - "mix_soft", - "mix_linear", - }; + const char *name = gpu_shader_get_name(node->custom1); - if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) { - int ret = GPU_stack_link(mat, node, names[node->custom1], in, out); + if (name != nullptr) { + int ret = GPU_stack_link(mat, node, name, in, out); if (ret && node->custom2 & SHD_MIXRGB_CLAMP) { const float min[3] = {0.0f, 0.0f, 0.0f}; const float max[3] = {1.0f, 1.0f, 1.0f}; @@ -109,7 +134,7 @@ void register_node_type_sh_mix_rgb(void) sh_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); node_type_socket_templates(&ntype, sh_node_mix_rgb_in, sh_node_mix_rgb_out); node_type_label(&ntype, node_blend_label); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_mix_rgb); + node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); node_type_gpu(&ntype, gpu_shader_mix_rgb); nodeRegisterType(&ntype); diff --git a/source/blender/python/bmesh/bmesh_py_api.c b/source/blender/python/bmesh/bmesh_py_api.c index 906f8ab702f..9cafb700430 100644 --- a/source/blender/python/bmesh/bmesh_py_api.c +++ b/source/blender/python/bmesh/bmesh_py_api.c @@ -147,10 +147,10 @@ static PyObject *bpy_bm_update_edit_mesh(PyObject *UNUSED(self), PyObject *args, } { - extern void EDBM_update_generic( + extern void EDBM_update_extern( struct Mesh * me, const bool do_tessface, const bool is_destructive); - EDBM_update_generic(me, do_loop_triangles, is_destructive); + EDBM_update_extern(me, do_loop_triangles, is_destructive); } Py_RETURN_NONE; diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 638975f8fba..323661e7ca9 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1181,7 +1181,7 @@ PyDoc_STRVAR( "\n" " Custom-data layers are only copied from ``mesh`` on initialization.\n" " Further calls will copy custom-data to matching layers, layers missing on the target " - "mesh wont be added.\n"); + "mesh won't be added.\n"); static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = {"mesh", "face_normals", "use_shape_key", "shape_key_index", NULL}; @@ -2500,7 +2500,7 @@ PyDoc_STRVAR( "\n" " Running this on sequences besides :class:`BMesh.verts`, :class:`BMesh.edges`, " ":class:`BMesh.faces`\n" - " works but wont result in each element having a valid index, instead its order in the " + " works but won't result in each element having a valid index, instead its order in the " "sequence will be set.\n"); static PyObject *bpy_bmelemseq_index_update(BPy_BMElemSeq *self) { @@ -3020,8 +3020,8 @@ static struct PyMethodDef bpy_bmfaceseq_methods[] = { static struct PyMethodDef bpy_bmloopseq_methods[] = { /* odd function, initializes index values */ - /* no: index_update() function since we cant iterate over loops */ - /* no: sort() function since we cant iterate over loops */ + /* no: index_update() function since we can't iterate over loops */ + /* no: sort() function since we can't iterate over loops */ {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 9b6ca7fcec5..cebc72d99d1 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -91,7 +91,7 @@ static PyObject *idprop_py_from_idp_double(const IDProperty *prop) static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty *parent) { BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &BPy_IDGroup_Type); - group->id = id; + group->owner_id = id; group->prop = prop; group->parent = parent; /* can be NULL */ return (PyObject *)group; @@ -105,7 +105,7 @@ static PyObject *idprop_py_from_idp_id(IDProperty *prop) static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop) { BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &BPy_IDArray_Type); - array->id = id; + array->owner_id = id; array->prop = prop; return (PyObject *)array; } @@ -152,7 +152,7 @@ static Py_hash_t BPy_IDGroup_hash(BPy_IDProperty *self) static PyObject *BPy_IDGroup_repr(BPy_IDProperty *self) { return PyUnicode_FromFormat("<bpy id prop: owner=\"%s\", name=\"%s\", address=%p>", - self->id ? self->id->name : "<NONE>", + self->owner_id ? self->owner_id->name : "<NONE>", self->prop->name, self->prop); } @@ -326,7 +326,7 @@ static PyObject *BPy_IDGroup_Map_GetItem(BPy_IDProperty *self, PyObject *item) return NULL; } - return BPy_IDGroup_WrapData(self->id, idprop, self->prop); + return BPy_IDGroup_WrapData(self->owner_id, idprop, self->prop); } /* returns NULL on success, error string on failure */ @@ -946,7 +946,7 @@ static PyObject *BPy_Group_IterValues_next(BPy_IDGroup_Iter *self) } IDProperty *cur = self->cur; self->cur = self->reversed ? self->cur->prev : self->cur->next; - return BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop); + return BPy_IDGroup_WrapData(self->group->owner_id, cur, self->group->prop); } PyErr_SetNone(PyExc_StopIteration); return NULL; @@ -964,7 +964,7 @@ static PyObject *BPy_Group_IterItems_next(BPy_IDGroup_Iter *self) PyObject *ret = PyTuple_New(2); PyTuple_SET_ITEMS(ret, PyUnicode_FromString(cur->name), - BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop)); + BPy_IDGroup_WrapData(self->group->owner_id, cur, self->group->prop)); return ret; } PyErr_SetNone(PyExc_StopIteration); @@ -1514,7 +1514,7 @@ static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args) idprop = IDP_GetPropertyFromGroup(self->prop, key); if (idprop) { - PyObject *pyobj = BPy_IDGroup_WrapData(self->id, idprop, self->prop); + PyObject *pyobj = BPy_IDGroup_WrapData(self->owner_id, idprop, self->prop); if (pyobj) { return pyobj; } diff --git a/source/blender/python/generic/idprop_py_api.h b/source/blender/python/generic/idprop_py_api.h index 1e8e26a3b6d..f53c45b07c2 100644 --- a/source/blender/python/generic/idprop_py_api.h +++ b/source/blender/python/generic/idprop_py_api.h @@ -56,14 +56,14 @@ extern PyTypeObject BPy_IDGroup_IterItems_Type; typedef struct BPy_IDProperty { PyObject_VAR_HEAD - struct ID *id; /* can be NULL */ + struct ID *owner_id; /* can be NULL */ struct IDProperty *prop; /* must be second member */ struct IDProperty *parent; } BPy_IDProperty; typedef struct BPy_IDArray { PyObject_VAR_HEAD - struct ID *id; /* can be NULL */ + struct ID *owner_id; /* can be NULL */ struct IDProperty *prop; /* must be second member */ } BPy_IDArray; diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c index 53e22314ec4..793b980295c 100644 --- a/source/blender/python/generic/imbuf_py_api.c +++ b/source/blender/python/generic/imbuf_py_api.c @@ -495,15 +495,16 @@ static PyObject *M_imbuf_load(PyObject *UNUSED(self), PyObject *args, PyObject * return Py_ImBuf_CreatePyObject(ibuf); } -PyDoc_STRVAR(M_imbuf_write_doc, - ".. function:: write(image, filepath)\n" - "\n" - " Write an image.\n" - "\n" - " :arg image: the image to write.\n" - " :type image: :class:`ImBuf`\n" - " :arg filepath: the filepath of the image.\n" - " :type filepath: string\n"); +PyDoc_STRVAR( + M_imbuf_write_doc, + ".. function:: write(image, filepath=image.filepath)\n" + "\n" + " Write an image.\n" + "\n" + " :arg image: the image to write.\n" + " :type image: :class:`ImBuf`\n" + " :arg filepath: Optional filepath of the image (fallback to the images file path).\n" + " :type filepath: string\n"); static PyObject *M_imbuf_write(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { Py_ImBuf *py_imb; diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 9824d5f17c4..127380feec1 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -523,7 +523,7 @@ PyObject *PyC_FrozenSetFromStrings(const char **strings) /** * Similar to #PyErr_Format(), * - * Implementation - we cant actually prepend the existing exception, + * Implementation - we can't actually prepend the existing exception, * because it could have _any_ arguments given to it, so instead we get its * ``__str__`` output and raise our own exception including it. */ @@ -846,7 +846,7 @@ PyObject *PyC_DefaultNameSpace(const char *filename) PyModule_AddStringConstant(mod_main, "__name__", "__main__"); if (filename) { /* __file__ mainly for nice UI'ness - * note: this wont map to a real file when executing text-blocks and buttons. */ + * note: this won't map to a real file when executing text-blocks and buttons. */ PyModule_AddObject(mod_main, "__file__", PyC_UnicodeFromByte(filename)); } PyModule_AddObject(mod_main, "__builtins__", builtins); diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index dc9b8b52821..30a61067c9e 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -403,7 +403,7 @@ void BPy_init_modules(struct bContext *C) Py_DECREF(py_modpath); } else { - printf("bpy: couldn't find 'scripts/modules', blender probably wont start.\n"); + printf("bpy: couldn't find 'scripts/modules', blender probably won't start.\n"); } /* stand alone utility modules not related to blender directly */ IDProp_Init_Types(); /* not actually a submodule, just types */ diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 4de6063098b..a89d97bb40c 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -298,15 +298,6 @@ static int bpy_app_global_flag_set__only_disable(PyObject *UNUSED(self), return bpy_app_global_flag_set(NULL, value, closure); } -PyDoc_STRVAR(bpy_app_binary_path_python_doc, - "String, the path to the python executable (read-only). " - "Deprecated! Use ``sys.executable`` instead."); -static PyObject *bpy_app_binary_path_python_get(PyObject *UNUSED(self), void *UNUSED(closure)) -{ - PyErr_Warn(PyExc_RuntimeWarning, "Use 'sys.executable' instead of 'binary_path_python'!"); - return Py_INCREF_RET(PySys_GetObject("executable")); -} - PyDoc_STRVAR(bpy_app_debug_value_doc, "Short, number which can be set to non-zero values for testing purposes"); static PyObject *bpy_app_debug_value_get(PyObject *UNUSED(self), void *UNUSED(closure)) @@ -441,12 +432,6 @@ static PyGetSetDef bpy_app_getsets[] = { bpy_app_global_flag_doc, (void *)G_FLAG_USERPREF_NO_SAVE_ON_EXIT}, - {"binary_path_python", - bpy_app_binary_path_python_get, - NULL, - bpy_app_binary_path_python_doc, - NULL}, - {"debug_value", bpy_app_debug_value_get, bpy_app_debug_value_set, diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index f95e655d0c6..8796877e9aa 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -154,8 +154,8 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate) fprintf(stderr, "ERROR: Python context internal state bug. this should not happen!\n"); } else if (py_call_level == 0) { - /* XXX - Calling classes currently wont store the context :\, - * cant set NULL because of this. but this is very flakey still. */ + /* XXX - Calling classes currently won't store the context :\, + * can't set NULL because of this. but this is very flakey still. */ #if 0 BPY_context_set(NULL); #endif @@ -825,7 +825,7 @@ static void dealloc_obj_dealloc(PyObject *self) { bpy_module_delay_init(((dealloc_obj *)self)->mod); - /* Note, for subclassed PyObjects we cant just call PyObject_DEL() directly or it will crash */ + /* Note, for subclassed PyObjects we can't just call PyObject_DEL() directly or it will crash */ dealloc_obj_Type.tp_free(self); } @@ -838,7 +838,7 @@ PyMODINIT_FUNC PyInit_bpy(void) /* Problem: * 1) this init function is expected to have a private member defined - 'md_def' * but this is only set for C defined modules (not py packages) - * so we cant return 'bpy_package_py' as is. + * so we can't return 'bpy_package_py' as is. * * 2) there is a 'bpy' C module for python to load which is basically all of blender, * and there is scripts/bpy/__init__.py, diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index 7a688b8c77d..5f97e11b412 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -258,7 +258,8 @@ static PyObject *bpy_lib_enter(BPy_Library *self) BKE_reports_init(&reports, RPT_STORE); - self->blo_handle = BLO_blendhandle_from_file(self->abspath, &reports); + self->blo_handle = BLO_blendhandle_from_file(self->abspath, + &(BlendFileReadReport){.reports = &reports}); if (self->blo_handle == NULL) { if (BPy_reports_to_error(&reports, PyExc_IOError, true) != -1) { diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 4a5e2552598..055dd624ea8 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -87,7 +87,7 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) bContext *C = BPY_context_get(); if (C == NULL) { - PyErr_SetString(PyExc_RuntimeError, "Context is None, cant poll any operators"); + PyErr_SetString(PyExc_RuntimeError, "Context is None, can't poll any operators"); return NULL; } @@ -174,7 +174,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) bContext *C = BPY_context_get(); if (C == NULL) { - PyErr_SetString(PyExc_RuntimeError, "Context is None, cant poll any operators"); + PyErr_SetString(PyExc_RuntimeError, "Context is None, can't poll any operators"); return NULL; } @@ -367,7 +367,7 @@ static PyObject *pyop_as_string(PyObject *UNUSED(self), PyObject *args) if (C == NULL) { PyErr_SetString(PyExc_RuntimeError, - "Context is None, cant get the string representation of this object."); + "Context is None, can't get the string representation of this object."); return NULL; } diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 6ad80f9160b..0d451a8dd7f 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -210,7 +210,7 @@ static const EnumPropertyItem property_subtype_array_items[] = { /** * Store #PyObject data for a dynamically defined property. * Currently this is only used to store call-back functions. - * Properties that don't use custom-callbacks wont allocate this struct. + * Properties that don't use custom callbacks won't allocate this struct. * * Memory/Reference Management * --------------------------- @@ -2264,7 +2264,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, } \ (void)0 -/* terse macros for error checks shared between all funcs cant use function +/* terse macros for error checks shared between all funcs can't use function * calls because of static strings passed to pyrna_set_to_enum_bitfield */ #define BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items) \ if (UNLIKELY(id_len >= MAX_IDPROP_NAME)) { \ diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index fb1cb823964..89fe907e57b 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1647,7 +1647,7 @@ int pyrna_pydict_to_props(PointerRNA *ptr, break; } - item = PyDict_GetItemString(kw, arg_name); /* Wont set an error. */ + item = PyDict_GetItemString(kw, arg_name); /* Won't set an error. */ if (item == NULL) { if (all_args) { @@ -6224,7 +6224,7 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject err = -1; break; } - /* PyDict_GetItemString wont raise an error. */ + /* PyDict_GetItemString won't raise an error. */ continue; } @@ -7835,7 +7835,7 @@ StructRNA *pyrna_struct_as_srna(PyObject *self, const bool parent, const char *e BPy_StructRNA *py_srna = NULL; StructRNA *srna; - /* Unfortunately PyObject_GetAttrString wont look up this types tp_dict first :/ */ + /* Unfortunately PyObject_GetAttrString won't look up this types tp_dict first :/ */ if (PyType_Check(self)) { py_srna = (BPy_StructRNA *)PyDict_GetItem(((PyTypeObject *)self)->tp_dict, bpy_intern_str_bl_rna); @@ -8029,7 +8029,7 @@ static int pyrna_deferred_register_class_from_type_hints(StructRNA *srna, PyType } } else { - /* Should never happen, an error wont have been raised, so raise one. */ + /* Should never happen, an error won't have been raised, so raise one. */ PyErr_Format(PyExc_TypeError, "typing.get_type_hints returned: %.200s, expected dict\n", Py_TYPE(annotations_dict)->tp_name); @@ -8437,7 +8437,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param else if (py_srna == NULL) { py_class_instance = NULL; } - else if (py_srna == Py_None) { /* Probably wont ever happen, but possible. */ + else if (py_srna == Py_None) { /* Probably won't ever happen, but possible. */ Py_DECREF(py_srna); py_class_instance = NULL; } @@ -8667,7 +8667,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param if (err != 0) { ReportList *reports; - /* Alert the user, else they wont know unless they see the console. */ + /* Alert the user, else they won't know unless they see the console. */ if ((!is_staticmethod) && (!is_classmethod) && (ptr->data) && (RNA_struct_is_a(ptr->type, &RNA_Operator)) && (is_valid_wm == (CTX_wm_manager(C) != NULL))) { @@ -8675,7 +8675,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param reports = op->reports; } else { - /* Wont alert users, but they can view in 'info' space. */ + /* Won't alert users, but they can view in 'info' space. */ reports = CTX_wm_reports(C); } diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c index 6e0db3eca49..490d9aa5212 100644 --- a/source/blender/python/intern/bpy_rna_operator.c +++ b/source/blender/python/intern/bpy_rna_operator.c @@ -31,6 +31,8 @@ #include "BPY_extern.h" #include "bpy_capi_utils.h" +#include "bpy_rna_operator.h" /* Own include. */ + /* -------------------------------------------------------------------- */ /** \name Operator `poll_message_set` Method * \{ */ diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 1766c7dea66..c6d801aa733 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -2300,7 +2300,7 @@ static int Vector_length_set(VectorObject *self, PyObject *value) dot = dot_vn_vn(self->vec, self->vec, self->size); if (!dot) { - /* cant sqrt zero */ + /* can't sqrt zero */ return 0; } diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h index 7352ac7b12e..6b2861bbefd 100644 --- a/source/blender/render/RE_engine.h +++ b/source/blender/render/RE_engine.h @@ -65,6 +65,7 @@ extern "C" { #define RE_USE_STEREO_VIEWPORT 256 #define RE_USE_GPU_CONTEXT 512 #define RE_USE_CUSTOM_FREESTYLE 1024 +#define RE_USE_NO_IMAGE_SAVE 2048 /* RenderEngine.flag */ #define RE_ENGINE_ANIMATION 1 diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 7af6d3e234f..011bdb056d8 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -466,7 +466,16 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__); - BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); + const float(*precomputed_normals)[3] = CustomData_get_layer(&me->pdata, CD_NORMAL); + const bool calculate_normal = precomputed_normals ? false : true; + + if (precomputed_normals != NULL) { + BKE_mesh_recalc_looptri_with_normals( + me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri, precomputed_normals); + } + else { + BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); + } if (tangent) { BKE_mesh_ensure_normals_for_display(me_eval); @@ -479,9 +488,6 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval loop_normals = CustomData_get_layer(&me_eval->ldata, CD_NORMAL); } - const float *precomputed_normals = CustomData_get_layer(&me->pdata, CD_NORMAL); - const bool calculate_normal = precomputed_normals ? false : true; - for (i = 0; i < tottri; i++) { const MLoopTri *lt = &looptri[i]; const MPoly *mp = &me->mpoly[lt->poly]; @@ -511,7 +517,7 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval copy_v3_v3(triangles[i].normal, no); } else { - copy_v3_v3(triangles[i].normal, &precomputed_normals[lt->poly]); + copy_v3_v3(triangles[i].normal, precomputed_normals[lt->poly]); } } diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index a39214b609d..20f868ca86f 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -2215,35 +2215,42 @@ static int do_write_image_or_movie(Render *re, RenderResult rres; double render_time; bool ok = true; + RenderEngineType *re_type = RE_engines_find(re->r.engine); - RE_AcquireResultImageViews(re, &rres); + /* Only disable file writing if postprocessing is also disabled. */ + const bool do_write_file = !(re_type->flag & RE_USE_NO_IMAGE_SAVE) || + (re_type->flag & RE_USE_POSTPROCESS); - /* write movie or image */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - RE_WriteRenderViewsMovie( - re->reports, &rres, scene, &re->r, mh, re->movie_ctx_arr, totvideos, false); - } - else { - if (name_override) { - BLI_strncpy(name, name_override, sizeof(name)); + if (do_write_file) { + RE_AcquireResultImageViews(re, &rres); + + /* write movie or image */ + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + RE_WriteRenderViewsMovie( + re->reports, &rres, scene, &re->r, mh, re->movie_ctx_arr, totvideos, false); } else { - BKE_image_path_from_imformat(name, - scene->r.pic, - BKE_main_blendfile_path(bmain), - scene->r.cfra, - &scene->r.im_format, - (scene->r.scemode & R_EXTENSION) != 0, - true, - NULL); + if (name_override) { + BLI_strncpy(name, name_override, sizeof(name)); + } + else { + BKE_image_path_from_imformat(name, + scene->r.pic, + BKE_main_blendfile_path(bmain), + scene->r.cfra, + &scene->r.im_format, + (scene->r.scemode & R_EXTENSION) != 0, + true, + NULL); + } + + /* write images as individual images or stereo */ + ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, true, name); } - /* write images as individual images or stereo */ - ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, true, name); + RE_ReleaseResultImageViews(re, &rres); } - RE_ReleaseResultImageViews(re, &rres); - render_time = re->i.lastframetime; re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; @@ -2257,8 +2264,10 @@ static int do_write_image_or_movie(Render *re, * Not sure it's actually even used anyway, we could as well pass NULL? */ render_callback_exec_null(re, G_MAIN, BKE_CB_EVT_RENDER_STATS); - BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime - render_time); - printf(" (Saving: %s)\n", name); + if (do_write_file) { + BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime - render_time); + printf(" (Saving: %s)\n", name); + } fputc('\n', stdout); fflush(stdout); @@ -2330,9 +2339,15 @@ void RE_RenderAnim(Render *re, return; } + RenderEngineType *re_type = RE_engines_find(re->r.engine); + + /* Only disable file writing if postprocessing is also disabled. */ + const bool do_write_file = !(re_type->flag & RE_USE_NO_IMAGE_SAVE) || + (re_type->flag & RE_USE_POSTPROCESS); + render_init_depsgraph(re); - if (is_movie) { + if (is_movie && do_write_file) { size_t width, height; int i; bool is_error = false; @@ -2415,7 +2430,7 @@ void RE_RenderAnim(Render *re, nfra += tfra; /* Touch/NoOverwrite options are only valid for image's */ - if (is_movie == false) { + if (is_movie == false && do_write_file) { if (rd.mode & (R_NO_OVERWRITE | R_TOUCH)) { BKE_image_path_from_imformat(name, rd.pic, @@ -2508,7 +2523,7 @@ void RE_RenderAnim(Render *re, if (G.is_break == true) { /* remove touched file */ - if (is_movie == false) { + if (is_movie == false && do_write_file) { if ((rd.mode & R_TOUCH)) { if (!is_multiview_name) { if ((BLI_file_size(name) == 0)) { @@ -2548,7 +2563,7 @@ void RE_RenderAnim(Render *re, } /* end movie */ - if (is_movie) { + if (is_movie && do_write_file) { re_movie_free_all(re, mh, totvideos); } diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt index 2b402b4750f..9340cfbf03d 100644 --- a/source/blender/sequencer/CMakeLists.txt +++ b/source/blender/sequencer/CMakeLists.txt @@ -79,6 +79,7 @@ set(SRC intern/render.h intern/sequencer.c intern/sequencer.h + intern/sequence_lookup.c intern/sound.c intern/strip_add.c intern/strip_edit.c diff --git a/source/blender/sequencer/SEQ_edit.h b/source/blender/sequencer/SEQ_edit.h index 2711e0a7ee3..6d043dffe72 100644 --- a/source/blender/sequencer/SEQ_edit.h +++ b/source/blender/sequencer/SEQ_edit.h @@ -58,6 +58,8 @@ bool SEQ_edit_remove_gaps(struct Scene *scene, struct ListBase *seqbase, const int initial_frame, const bool remove_all_gaps); +void SEQ_edit_sequence_name_set(struct Scene *scene, struct Sequence *seq, const char *new_name); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index ad0815892f7..fd4181a5a54 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -33,6 +33,7 @@ struct Editing; struct Scene; struct Sequence; struct SequencerToolSettings; +struct SequenceLookup; /* RNA enums, just to be more readable */ enum { @@ -79,6 +80,16 @@ void SEQ_sequence_base_dupli_recursive(const struct Scene *scene_src, int dupe_flag, const int flag); +/* Defined in sequence_lookup.c */ + +typedef enum eSequenceLookupTag { + SEQ_LOOKUP_TAG_INVALID = (1 << 0), +} eSequenceLookupTag; + +struct Sequence *SEQ_sequence_lookup_by_name(const struct Scene *scene, const char *key); +void SEQ_sequence_lookup_free(const struct Scene *scene); +void SEQ_sequence_lookup_tag(const struct Scene *scene, eSequenceLookupTag tag); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index 67d3a2e5960..593a8fe7bf2 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -46,6 +46,7 @@ void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq); void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq); int SEQ_time_cmp_time_startdisp(const void *a, const void *b); bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame); +void SEQ_time_update_meta_strip_range(struct Scene *scene, struct Sequence *seq_meta); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index d587bd0f1a1..837a2de5742 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -30,6 +30,7 @@ extern "C" { struct ListBase; struct Scene; struct Sequence; +struct SeqCollection; int SEQ_transform_get_left_handle_frame(struct Sequence *seq); int SEQ_transform_get_right_handle_frame(struct Sequence *seq); @@ -48,7 +49,8 @@ bool SEQ_transform_seqbase_shuffle_ex(struct ListBase *seqbasep, bool SEQ_transform_seqbase_shuffle(struct ListBase *seqbasep, struct Sequence *test, struct Scene *evil_scene); -bool SEQ_transform_seqbase_shuffle_time(struct ListBase *seqbasep, +bool SEQ_transform_seqbase_shuffle_time(struct SeqCollection *strips_to_shuffle, + struct ListBase *seqbasep, struct Scene *evil_scene, struct ListBase *markers, const bool use_sync_markers); diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h index a4dc80d75db..432e8fdd0c0 100644 --- a/source/blender/sequencer/SEQ_utils.h +++ b/source/blender/sequencer/SEQ_utils.h @@ -36,7 +36,9 @@ struct Sequence; struct StripElem; void SEQ_sort(struct ListBase *seqbase); -void SEQ_sequence_base_unique_name_recursive(struct ListBase *seqbasep, struct Sequence *seq); +void SEQ_sequence_base_unique_name_recursive(struct Scene *scene, + struct ListBase *seqbasep, + struct Sequence *seq); const char *SEQ_sequence_give_name(struct Sequence *seq); struct ListBase *SEQ_get_seqbase_from_sequence(struct Sequence *seq, int *r_offset); const struct Sequence *SEQ_get_topmost_sequence(const struct Scene *scene, int frame); diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 73a9569fcd5..16adc392b3b 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -37,6 +37,7 @@ #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_path_util.h" +#include "BLI_rect.h" #include "BKE_anim_data.h" #include "BKE_animsys.h" @@ -272,11 +273,11 @@ static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibl /* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. * Order of applying these conditions is important. */ -static bool must_render_strip(const Sequence *seq, SeqCollection *strips_under_playhead) +static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame) { bool seq_have_effect_in_stack = false; Sequence *seq_iter; - SEQ_ITERATOR_FOREACH (seq_iter, strips_under_playhead) { + SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) { /* Strips is below another strip with replace blending are not rendered. */ if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) { return false; @@ -334,7 +335,7 @@ static void collection_filter_rendered_strips(SeqCollection *collection) Sequence *seq; /* Remove sound strips and muted strips from collection, because these are not rendered. - * Function must_render_strip() don't have to check for these strips anymore. */ + * Function #must_render_strip() don't have to check for these strips anymore. */ SEQ_ITERATOR_FOREACH (seq, collection) { if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { SEQ_collection_remove_strip(seq, collection); @@ -470,29 +471,6 @@ static bool seq_input_have_to_preprocess(const SeqRenderData *context, return false; } -typedef struct ImageTransformThreadInitData { - ImBuf *ibuf_source; - ImBuf *ibuf_out; - Sequence *seq; - float preview_scale_factor; - bool is_proxy_image; - bool for_render; -} ImageTransformThreadInitData; - -typedef struct ImageTransformThreadData { - ImBuf *ibuf_source; - ImBuf *ibuf_out; - Sequence *seq; - /* image_scale_factor is used to scale proxies to correct preview size. */ - float image_scale_factor; - /* Preview scale factor is needed to correct translation to match preview size. */ - float preview_scale_factor; - float crop_scale_factor; - bool for_render; - int start_line; - int tot_line; -} ImageTransformThreadData; - /** * Effect, mask and scene in strip input strips are rendered in preview resolution. * They are already down-scaled. #input_preprocess() does not expect this to happen. @@ -512,49 +490,21 @@ static bool seq_need_scale_to_render_size(const Sequence *seq, bool is_proxy_ima return false; } -static void sequencer_image_crop_transform_init(void *handle_v, - int start_line, - int tot_line, - void *init_data_v) -{ - ImageTransformThreadData *handle = (ImageTransformThreadData *)handle_v; - const ImageTransformThreadInitData *init_data = (ImageTransformThreadInitData *)init_data_v; - - handle->ibuf_source = init_data->ibuf_source; - handle->ibuf_out = init_data->ibuf_out; - handle->seq = init_data->seq; - - handle->preview_scale_factor = init_data->preview_scale_factor; - if (seq_need_scale_to_render_size(init_data->seq, init_data->is_proxy_image)) { - handle->image_scale_factor = 1.0f; - } - else { - handle->image_scale_factor = handle->preview_scale_factor; - } - - /* Proxy image is smaller, so crop values must be corrected by proxy scale factor. - * Proxy scale factor always matches preview_scale_factor. */ - handle->crop_scale_factor = seq_need_scale_to_render_size(init_data->seq, - init_data->is_proxy_image) ? - init_data->preview_scale_factor : - 1.0f; - - handle->for_render = init_data->for_render; - handle->start_line = start_line; - handle->tot_line = tot_line; -} - -static void sequencer_image_crop_transform_matrix(const ImageTransformThreadData *data, +static void sequencer_image_crop_transform_matrix(const Sequence *seq, + const ImBuf *in, + const ImBuf *out, + const float image_scale_factor, + const float preview_scale_factor, float r_transform_matrix[3][3]) { - const StripTransform *transform = data->seq->strip->transform; - const float scale_x = transform->scale_x * data->image_scale_factor; - const float scale_y = transform->scale_y * data->image_scale_factor; - const float image_center_offs_x = (data->ibuf_out->x - data->ibuf_source->x) / 2; - const float image_center_offs_y = (data->ibuf_out->y - data->ibuf_source->y) / 2; - const float translate_x = transform->xofs * data->preview_scale_factor + image_center_offs_x; - const float translate_y = transform->yofs * data->preview_scale_factor + image_center_offs_y; - const float pivot[2] = {data->ibuf_source->x / 2, data->ibuf_source->y / 2}; + const StripTransform *transform = seq->strip->transform; + const float scale_x = transform->scale_x * image_scale_factor; + const float scale_y = transform->scale_y * image_scale_factor; + const float image_center_offs_x = (out->x - in->x) / 2; + const float image_center_offs_y = (out->y - in->y) / 2; + const float translate_x = transform->xofs * preview_scale_factor + image_center_offs_x; + const float translate_y = transform->yofs * preview_scale_factor + image_center_offs_y; + const float pivot[2] = {in->x / 2, in->y / 2}; loc_rot_size_to_mat3(r_transform_matrix, (const float[]){translate_x, translate_y}, transform->rotation, @@ -563,108 +513,44 @@ static void sequencer_image_crop_transform_matrix(const ImageTransformThreadData invert_m3(r_transform_matrix); } -static void sequencer_image_crop_transform_start_uv(const ImageTransformThreadData *data, - const float transform_matrix[3][3], - float r_start_uv[2]) +static void sequencer_image_crop_init(const Sequence *seq, + const ImBuf *in, + float crop_scale_factor, + rctf *r_crop) { - float orig[2]; - orig[0] = 0.0f; - orig[1] = data->start_line; - mul_v2_m3v2(r_start_uv, transform_matrix, orig); -} + const StripCrop *c = seq->strip->crop; + const int left = c->left * crop_scale_factor; + const int right = c->right * crop_scale_factor; + const int top = c->top * crop_scale_factor; + const int bottom = c->bottom * crop_scale_factor; -static void sequencer_image_crop_transform_uv_min(const float transform_matrix[3][3], - float r_uv_min[2]) -{ - float orig[2]; - orig[0] = 0.0f; - orig[1] = 0.0f; - mul_v2_m3v2(r_uv_min, transform_matrix, orig); + BLI_rctf_init(r_crop, left, in->x - right, bottom, in->y - top); } -static void sequencer_image_crop_transform_delta_x(const ImageTransformThreadData *data, - const float transform_matrix[3][3], - const float uv_min[2], - float r_add_x[2]) +static void sequencer_preprocess_transform_crop( + ImBuf *in, ImBuf *out, const SeqRenderData *context, Sequence *seq, const bool is_proxy_image) { - float uv_max_x[2]; - uv_max_x[0] = data->ibuf_out->x; - uv_max_x[1] = 0.0f; - mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x); - sub_v2_v2(r_add_x, uv_min); - mul_v2_fl(r_add_x, 1.0f / data->ibuf_out->x); -} + const Scene *scene = context->scene; + const float preview_scale_factor = context->preview_render_size == SEQ_RENDER_SIZE_SCENE ? + (float)scene->r.size / 100 : + SEQ_rendersize_to_scale_factor( + context->preview_render_size); + const bool do_scale_to_render_size = seq_need_scale_to_render_size(seq, is_proxy_image); + const float image_scale_factor = do_scale_to_render_size ? 1.0f : preview_scale_factor; -static void sequencer_image_crop_transform_delta_y(const ImageTransformThreadData *data, - const float transform_matrix[3][3], - const float uv_min[2], - float r_add_y[2]) -{ - float uv_max_y[2]; - uv_max_y[0] = 0.0f; - uv_max_y[1] = data->ibuf_out->y; - mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y); - sub_v2_v2(r_add_y, uv_min); - mul_v2_fl(r_add_y, 1.0f / data->ibuf_out->y); -} - -static void sequencer_image_crop_transform_interpolation_coefs( - const ImageTransformThreadData *data, float r_start_uv[2], float r_add_x[2], float r_add_y[2]) -{ float transform_matrix[3][3]; - sequencer_image_crop_transform_matrix(data, transform_matrix); - sequencer_image_crop_transform_start_uv(data, transform_matrix, r_start_uv); - float uv_min[2]; - sequencer_image_crop_transform_uv_min(transform_matrix, uv_min); - sequencer_image_crop_transform_delta_x(data, transform_matrix, uv_min, r_add_x); - sequencer_image_crop_transform_delta_y(data, transform_matrix, uv_min, r_add_y); -} + sequencer_image_crop_transform_matrix( + seq, in, out, image_scale_factor, preview_scale_factor, transform_matrix); -static void *sequencer_image_crop_transform_do_thread(void *data_v) -{ - const ImageTransformThreadData *data = data_v; - - float last_uv[2]; - float add_x[2]; - float add_y[2]; - sequencer_image_crop_transform_interpolation_coefs(data_v, last_uv, add_x, add_y); - - /* Image crop is done by offsetting image boundary limits. */ - const StripCrop *c = data->seq->strip->crop; - const int left = c->left * data->crop_scale_factor; - const int right = c->right * data->crop_scale_factor; - const int top = c->top * data->crop_scale_factor; - const int bottom = c->bottom * data->crop_scale_factor; - - const float source_pixel_range_max[2] = {data->ibuf_source->x - right, - data->ibuf_source->y - top}; - const float source_pixel_range_min[2] = {left, bottom}; - - const int width = data->ibuf_out->x; - - float uv[2]; - for (int yi = data->start_line; yi < data->start_line + data->tot_line; yi++) { - copy_v2_v2(uv, last_uv); - add_v2_v2(last_uv, add_y); - for (int xi = 0; xi < width; xi++) { - if (source_pixel_range_min[0] >= uv[0] || uv[0] >= source_pixel_range_max[0] || - source_pixel_range_min[1] >= uv[1] || uv[1] >= source_pixel_range_max[1]) { - add_v2_v2(uv, add_x); - continue; - } - - if (data->for_render) { - bilinear_interpolation(data->ibuf_source, data->ibuf_out, uv[0], uv[1], xi, yi); - } - else { - nearest_interpolation(data->ibuf_source, data->ibuf_out, uv[0], uv[1], xi, yi); - } - - add_v2_v2(uv, add_x); - } - } + /* Proxy image is smaller, so crop values must be corrected by proxy scale factor. + * Proxy scale factor always matches preview_scale_factor. */ + rctf source_crop; + const float crop_scale_factor = do_scale_to_render_size ? preview_scale_factor : 1.0f; + sequencer_image_crop_init(seq, in, crop_scale_factor, &source_crop); - return NULL; + const eIMBInterpolationFilterMode filter = context->for_render ? IMB_FILTER_BILINEAR : + IMB_FILTER_NEAREST; + IMB_transform(in, out, transform_matrix, &source_crop, filter); } static void multibuf(ImBuf *ibuf, const float fmul) @@ -726,27 +612,8 @@ static ImBuf *input_preprocess(const SeqRenderData *context, const int y = context->recty; preprocessed_ibuf = IMB_allocImBuf(x, y, 32, ibuf->rect_float ? IB_rectfloat : IB_rect); - ImageTransformThreadInitData init_data = {NULL}; - init_data.ibuf_source = ibuf; - init_data.ibuf_out = preprocessed_ibuf; - init_data.seq = seq; - init_data.is_proxy_image = is_proxy_image; - - /* Get scale factor if preview resolution doesn't match project resolution. */ - if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) { - init_data.preview_scale_factor = (float)scene->r.size / 100; - } - else { - init_data.preview_scale_factor = SEQ_rendersize_to_scale_factor( - context->preview_render_size); - } + sequencer_preprocess_transform_crop(ibuf, preprocessed_ibuf, context, seq, is_proxy_image); - init_data.for_render = context->for_render; - IMB_processor_apply_threaded(context->recty, - sizeof(ImageTransformThreadData), - &init_data, - sequencer_image_crop_transform_init, - sequencer_image_crop_transform_do_thread); seq_imbuf_assign_spaces(scene, preprocessed_ibuf); IMB_metadata_copy(preprocessed_ibuf, ibuf); IMB_freeImBuf(ibuf); diff --git a/source/blender/sequencer/intern/sequence_lookup.c b/source/blender/sequencer/intern/sequence_lookup.c new file mode 100644 index 00000000000..72567dc394e --- /dev/null +++ b/source/blender/sequencer/intern/sequence_lookup.c @@ -0,0 +1,161 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup sequencer + */ + +#include "SEQ_sequencer.h" + +#include "DNA_listBase.h" +#include "DNA_scene_types.h" +#include "DNA_sequence_types.h" + +#include "SEQ_iterator.h" + +#include "BLI_ghash.h" +#include "BLI_string.h" +#include "BLI_sys_types.h" +#include "BLI_threads.h" +#include <string.h> + +#include "MEM_guardedalloc.h" + +static ThreadMutex lookup_lock = BLI_MUTEX_INITIALIZER; + +typedef struct SequenceLookup { + GHash *by_name; + eSequenceLookupTag tag; +} SequenceLookup; + +static void seq_sequence_lookup_init(struct SequenceLookup *lookup) +{ + lookup->by_name = BLI_ghash_str_new(__func__); + lookup->tag |= SEQ_LOOKUP_TAG_INVALID; +} + +static void seq_sequence_lookup_build(const struct Scene *scene, struct SequenceLookup *lookup) +{ + SeqCollection *all_strips = SEQ_query_all_strips_recursive(&scene->ed->seqbase); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, all_strips) { + BLI_ghash_insert(lookup->by_name, seq->name + 2, seq); + } + SEQ_collection_free(all_strips); + lookup->tag &= ~SEQ_LOOKUP_TAG_INVALID; +} + +static SequenceLookup *seq_sequence_lookup_new(void) +{ + SequenceLookup *lookup = MEM_callocN(sizeof(SequenceLookup), __func__); + seq_sequence_lookup_init(lookup); + return lookup; +} + +static void seq_sequence_lookup_free(struct SequenceLookup **lookup) +{ + if (*lookup == NULL) { + return; + } + + BLI_ghash_free((*lookup)->by_name, NULL, NULL); + (*lookup)->by_name = NULL; + MEM_freeN(*lookup); + *lookup = NULL; +} + +static void seq_sequence_lookup_rebuild(const struct Scene *scene, struct SequenceLookup **lookup) +{ + seq_sequence_lookup_free(lookup); + *lookup = seq_sequence_lookup_new(); + seq_sequence_lookup_build(scene, *lookup); +} + +static bool seq_sequence_lookup_is_valid(struct SequenceLookup *lookup) +{ + return (lookup->tag & SEQ_LOOKUP_TAG_INVALID) == 0; +} + +static void seq_sequence_lookup_update_if_needed(const struct Scene *scene, + struct SequenceLookup **lookup) +{ + if (!scene->ed) { + return; + } + if (*lookup && seq_sequence_lookup_is_valid(*lookup)) { + return; + } + + seq_sequence_lookup_rebuild(scene, lookup); +} + +/** + * Free lookup hash data. + * + * \param scene: scene that owns lookup hash + */ +void SEQ_sequence_lookup_free(const Scene *scene) +{ + BLI_assert(scene->ed); + BLI_mutex_lock(&lookup_lock); + SequenceLookup *lookup = scene->ed->runtime.sequence_lookup; + seq_sequence_lookup_free(&lookup); + BLI_mutex_unlock(&lookup_lock); +} + +/** + * Find a sequence with a given name. + * If lookup hash doesn't exist, it will be created. If hash is tagged as invalid, it will be + * rebuilt. + * + * \param scene: scene that owns lookup hash + * \param key: Sequence name without SQ prefix (seq->name + 2) + * + * \return pointer to Sequence + */ +Sequence *SEQ_sequence_lookup_by_name(const Scene *scene, const char *key) +{ + BLI_assert(scene->ed); + BLI_mutex_lock(&lookup_lock); + seq_sequence_lookup_update_if_needed(scene, &scene->ed->runtime.sequence_lookup); + SequenceLookup *lookup = scene->ed->runtime.sequence_lookup; + Sequence *seq = BLI_ghash_lookup(lookup->by_name, key); + BLI_mutex_unlock(&lookup_lock); + return seq; +} + +/** + * Find a sequence with a given name. + * + * \param scene: scene that owns lookup hash + * \param tag: tag to set + */ +void SEQ_sequence_lookup_tag(const Scene *scene, eSequenceLookupTag tag) +{ + if (!scene->ed) { + return; + } + + BLI_mutex_lock(&lookup_lock); + SequenceLookup *lookup = scene->ed->runtime.sequence_lookup; + if (lookup != NULL) { + lookup->tag |= tag; + } + BLI_mutex_unlock(&lookup_lock); +} diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index d0bc41062a1..8cd67195c30 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -271,6 +271,7 @@ void SEQ_editing_free(Scene *scene, const bool do_id_user) SEQ_ALL_END; BLI_freelistN(&ed->metastack); + SEQ_sequence_lookup_free(scene); MEM_freeN(ed); scene->ed = NULL; @@ -514,7 +515,7 @@ static Sequence *seq_dupli(const Scene *scene_src, if (scene_src == scene_dst) { if (dupe_flag & SEQ_DUPE_UNIQUE_NAME) { - SEQ_sequence_base_unique_name_recursive(&scene_dst->ed->seqbase, seqn); + SEQ_sequence_base_unique_name_recursive(scene_dst, &scene_dst->ed->seqbase, seqn); } } diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 9e5afb45f2a..142991eeacf 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -56,6 +56,7 @@ #include "IMB_metadata.h" #include "SEQ_add.h" +#include "SEQ_edit.h" #include "SEQ_effects.h" #include "SEQ_relations.h" #include "SEQ_render.h" @@ -98,33 +99,32 @@ void SEQ_add_load_data_init(SeqLoadData *load_data, static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *seq) { - SEQ_sequence_base_unique_name_recursive(seqbase, seq); + SEQ_sequence_base_unique_name_recursive(scene, seqbase, seq); SEQ_time_update_sequence_bounds(scene, seq); SEQ_sort(seqbase); SEQ_relations_invalidate_cache_composite(scene, seq); } -static void seq_add_set_name(Sequence *seq, SeqLoadData *load_data) +static void seq_add_set_name(Scene *scene, Sequence *seq, SeqLoadData *load_data) { if (load_data->name[0] != '\0') { - BLI_strncpy(seq->name + 2, load_data->name, sizeof(seq->name) - 2); + SEQ_edit_sequence_name_set(scene, seq, load_data->name); } else { if (seq->type == SEQ_TYPE_SCENE) { - BLI_strncpy(seq->name + 2, load_data->scene->id.name + 2, sizeof(seq->name) - 2); + SEQ_edit_sequence_name_set(scene, seq, load_data->scene->id.name + 2); } else if (seq->type == SEQ_TYPE_MOVIECLIP) { - BLI_strncpy(seq->name + 2, load_data->clip->id.name + 2, sizeof(seq->name) - 2); + SEQ_edit_sequence_name_set(scene, seq, load_data->clip->id.name + 2); } else if (seq->type == SEQ_TYPE_MASK) { - BLI_strncpy(seq->name + 2, load_data->mask->id.name + 2, sizeof(seq->name) - 2); + SEQ_edit_sequence_name_set(scene, seq, load_data->mask->id.name + 2); } else if ((seq->type & SEQ_TYPE_EFFECT) != 0) { - BLI_strncpy(seq->name + 2, SEQ_sequence_give_name(seq), sizeof(seq->name) - 2); + SEQ_edit_sequence_name_set(scene, seq, SEQ_sequence_give_name(seq)); } else { /* Image, sound and movie. */ - BLI_strncpy_utf8(seq->name + 2, load_data->name, sizeof(seq->name) - 2); - BLI_utf8_invalid_strip(seq->name + 2, strlen(seq->name + 2)); + SEQ_edit_sequence_name_set(scene, seq, load_data->name); } } } @@ -163,7 +163,7 @@ Sequence *SEQ_add_scene_strip(Scene *scene, ListBase *seqbase, struct SeqLoadDat seq->scene = load_data->scene; seq->len = load_data->scene->r.efra - load_data->scene->r.sfra + 1; id_us_ensure_real((ID *)load_data->scene); - seq_add_set_name(seq, load_data); + seq_add_set_name(scene, seq, load_data); seq_add_generic_update(scene, seqbase, seq); return seq; } @@ -184,7 +184,7 @@ Sequence *SEQ_add_movieclip_strip(Scene *scene, ListBase *seqbase, struct SeqLoa seq->clip = load_data->clip; seq->len = BKE_movieclip_get_duration(load_data->clip); id_us_ensure_real((ID *)load_data->clip); - seq_add_set_name(seq, load_data); + seq_add_set_name(scene, seq, load_data); seq_add_generic_update(scene, seqbase, seq); return seq; } @@ -205,7 +205,7 @@ Sequence *SEQ_add_mask_strip(Scene *scene, ListBase *seqbase, struct SeqLoadData seq->mask = load_data->mask; seq->len = BKE_mask_get_duration(load_data->mask); id_us_ensure_real((ID *)load_data->mask); - seq_add_set_name(seq, load_data); + seq_add_set_name(scene, seq, load_data); seq_add_generic_update(scene, seqbase, seq); return seq; } @@ -249,7 +249,7 @@ Sequence *SEQ_add_effect_strip(Scene *scene, ListBase *seqbase, struct SeqLoadDa } SEQ_relations_update_changed_seq_and_deps(scene, seq, 1, 1); /* Runs SEQ_time_update_sequence. */ - seq_add_set_name(seq, load_data); + seq_add_set_name(scene, seq, load_data); seq_add_generic_update(scene, seqbase, seq); return seq; @@ -364,7 +364,7 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL /* Set Last active directory. */ BLI_strncpy(scene->ed->act_imagedir, seq->strip->dir, sizeof(scene->ed->act_imagedir)); seq_add_set_view_transform(scene, seq, load_data); - seq_add_set_name(seq, load_data); + seq_add_set_name(scene, seq, load_data); seq_add_generic_update(scene, seqbase, seq); return seq; @@ -409,7 +409,7 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL Strip *strip = seq->strip; /* We only need 1 element to store the filename. */ - StripElem *se = strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem"); + StripElem *se = strip->stripdata = MEM_callocN(sizeof(StripElem), "stripelem"); BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name)); if (seq != NULL && seq->sound != NULL) { @@ -426,7 +426,7 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL /* Set Last active directory. */ BLI_strncpy(scene->ed->act_sounddir, strip->dir, FILE_MAXDIR); - seq_add_set_name(seq, load_data); + seq_add_set_name(scene, seq, load_data); seq_add_generic_update(scene, seqbase, seq); return seq; @@ -458,7 +458,7 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_ seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_META); /* Set name. */ - seq_add_set_name(seqm, load_data); + seq_add_set_name(scene, seqm, load_data); /* Set frames start and length. */ seqm->start = load_data->start_frame; @@ -590,7 +590,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name)); seq_add_set_view_transform(scene, seq, load_data); - seq_add_set_name(seq, load_data); + seq_add_set_name(scene, seq, load_data); seq_add_generic_update(scene, seqbase, seq); MEM_freeN(anim_arr); diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 4de6ec3583c..3e6eb74fcb3 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -30,6 +30,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" +#include "BLI_string_utf8.h" #include "BLT_translation.h" @@ -202,6 +203,7 @@ void SEQ_edit_remove_flagged_sequences(Scene *scene, ListBase *seqbase) } BLI_remlink(seqbase, seq); SEQ_sequence_free(scene, seq, true); + SEQ_sequence_lookup_tag(scene, SEQ_LOOKUP_TAG_INVALID); } } } @@ -468,3 +470,10 @@ bool SEQ_edit_remove_gaps(Scene *scene, } return true; } + +void SEQ_edit_sequence_name_set(Scene *scene, Sequence *seq, const char *new_name) +{ + BLI_strncpy_utf8(seq->name + 2, new_name, MAX_NAME - 2); + BLI_utf8_invalid_strip(seq->name + 2, strlen(seq->name + 2)); + SEQ_sequence_lookup_tag(scene, SEQ_LOOKUP_TAG_INVALID); +} diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index b8b6f62c7aa..ecec317ed25 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -184,7 +184,7 @@ static void seq_time_update_meta_strip(Scene *scene, Sequence *seq_meta) seq_update_sound_bounds_recursive(scene, seq_meta); } -static void seq_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta) +void SEQ_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta) { seq_time_update_meta_strip(scene, seq_meta); @@ -223,7 +223,7 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) seq->start = seq->startdisp = seq->seq1->startdisp; seq->enddisp = seq->seq1->enddisp; } - /* we cant help if strips don't overlap, it wont give useful results. + /* we can't help if strips don't overlap, it won't give useful results. * but at least ensure 'len' is never negative which causes bad bugs elsewhere. */ if (seq->enddisp < seq->startdisp) { /* simple start/end swap */ @@ -250,7 +250,7 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) Editing *ed = SEQ_editing_get(scene, false); MetaStack *ms = SEQ_meta_stack_active_get(ed); if (ms != NULL) { - seq_time_update_meta_strip_range(scene, ms->parseq); + SEQ_time_update_meta_strip_range(scene, ms->parseq); } SEQ_time_update_sequence_bounds(scene, seq); diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index a02d8a1428e..20cbe933b2f 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -34,6 +34,7 @@ #include "BKE_sound.h" #include "SEQ_effects.h" +#include "SEQ_iterator.h" #include "SEQ_relations.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" @@ -134,7 +135,7 @@ bool SEQ_transform_seqbase_isolated_sel_check(ListBase *seqbase) /** * Use to impose limits when dragging/extending - so impossible situations don't happen. - * Cant use the #SEQ_LEFTSEL and #SEQ_LEFTSEL directly because the strip may be in a meta-strip. + * Can't use the #SEQ_LEFTSEL and #SEQ_LEFTSEL directly because the strip may be in a meta-strip. */ void SEQ_transform_handle_xlimits(Sequence *seq, int leftflag, int rightflag) { @@ -233,14 +234,22 @@ void SEQ_transform_translate_sequence(Scene *evil_scene, Sequence *seq, int delt SEQ_offset_animdata(evil_scene, seq, delta); seq->start += delta; + /* Meta strips requires special handling: their content is to be translated, and then frame range + * of the meta is to be updated for the updated content. */ if (seq->type == SEQ_TYPE_META) { Sequence *seq_child; for (seq_child = seq->seqbase.first; seq_child; seq_child = seq_child->next) { SEQ_transform_translate_sequence(evil_scene, seq_child, delta); } + /* Ensure that meta bounds are updated, but this function prevents resets seq->start and + * start/end point in timeline. */ + SEQ_time_update_meta_strip_range(evil_scene, seq); + /* Move meta start/end points. */ + SEQ_transform_set_left_handle_frame(seq, seq->startdisp + delta); + SEQ_transform_set_right_handle_frame(seq, seq->enddisp + delta); } - SEQ_time_update_sequence_bounds(evil_scene, seq); + SEQ_time_update_sequence(evil_scene, seq); } /* return 0 if there weren't enough space */ @@ -294,73 +303,69 @@ bool SEQ_transform_seqbase_shuffle(ListBase *seqbasep, Sequence *test, Scene *ev return SEQ_transform_seqbase_shuffle_ex(seqbasep, test, evil_scene, 1); } -static int shuffle_seq_time_offset_test(ListBase *seqbasep, char dir) +static int shuffle_seq_time_offset_test(SeqCollection *strips_to_shuffle, + ListBase *seqbasep, + char dir) { int offset = 0; - Sequence *seq, *seq_other; - - for (seq = seqbasep->first; seq; seq = seq->next) { - if (seq->tmp) { - for (seq_other = seqbasep->first; seq_other; seq_other = seq_other->next) { - if (!seq_other->tmp && seq_overlap(seq, seq_other)) { - if (dir == 'L') { - offset = min_ii(offset, seq_other->startdisp - seq->enddisp); - } - else { - offset = max_ii(offset, seq_other->enddisp - seq->startdisp); - } - } + Sequence *seq; + + SEQ_ITERATOR_FOREACH (seq, strips_to_shuffle) { + LISTBASE_FOREACH (Sequence *, seq_other, seqbasep) { + if (!seq_overlap(seq, seq_other)) { + continue; + } + if (dir == 'L') { + offset = min_ii(offset, seq_other->startdisp - seq->enddisp); + } + else { + offset = max_ii(offset, seq_other->enddisp - seq->startdisp); } } } return offset; } -static int shuffle_seq_time_offset(Scene *scene, ListBase *seqbasep, char dir) +static int shuffle_seq_time_offset(SeqCollection *strips_to_shuffle, + ListBase *seqbasep, + Scene *scene, + char dir) { int ofs = 0; int tot_ofs = 0; Sequence *seq; - while ((ofs = shuffle_seq_time_offset_test(seqbasep, dir))) { - for (seq = seqbasep->first; seq; seq = seq->next) { - if (seq->tmp) { - /* seq_test_overlap only tests display values */ - seq->startdisp += ofs; - seq->enddisp += ofs; - } + while ((ofs = shuffle_seq_time_offset_test(strips_to_shuffle, seqbasep, dir))) { + SEQ_ITERATOR_FOREACH (seq, strips_to_shuffle) { + /* seq_test_overlap only tests display values */ + seq->startdisp += ofs; + seq->enddisp += ofs; } tot_ofs += ofs; } - for (seq = seqbasep->first; seq; seq = seq->next) { - if (seq->tmp) { - SEQ_time_update_sequence_bounds(scene, seq); /* corrects dummy startdisp/enddisp values */ - } + SEQ_ITERATOR_FOREACH (seq, strips_to_shuffle) { + SEQ_time_update_sequence_bounds(scene, seq); /* corrects dummy startdisp/enddisp values */ } return tot_ofs; } -bool SEQ_transform_seqbase_shuffle_time(ListBase *seqbasep, +bool SEQ_transform_seqbase_shuffle_time(SeqCollection *strips_to_shuffle, + ListBase *seqbasep, Scene *evil_scene, ListBase *markers, const bool use_sync_markers) { - /* note: seq->tmp is used to tag strips to move */ - - Sequence *seq; - - int offset_l = shuffle_seq_time_offset(evil_scene, seqbasep, 'L'); - int offset_r = shuffle_seq_time_offset(evil_scene, seqbasep, 'R'); + int offset_l = shuffle_seq_time_offset(strips_to_shuffle, seqbasep, evil_scene, 'L'); + int offset_r = shuffle_seq_time_offset(strips_to_shuffle, seqbasep, evil_scene, 'R'); int offset = (-offset_l < offset_r) ? offset_l : offset_r; if (offset) { - for (seq = seqbasep->first; seq; seq = seq->next) { - if (seq->tmp) { - SEQ_transform_translate_sequence(evil_scene, seq, offset); - seq->flag &= ~SEQ_OVERLAP; - } + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips_to_shuffle) { + SEQ_transform_translate_sequence(evil_scene, seq, offset); + seq->flag &= ~SEQ_OVERLAP; } if (use_sync_markers && !(evil_scene->toolsettings->lock_markers) && (markers != NULL)) { diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index 9aeb2961751..a287466dfb2 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -39,6 +39,7 @@ #include "BKE_main.h" #include "BKE_scene.h" +#include "SEQ_edit.h" #include "SEQ_iterator.h" #include "SEQ_relations.h" #include "SEQ_select.h" @@ -140,7 +141,9 @@ static int seqbase_unique_name_recursive_fn(Sequence *seq, void *arg_pt) return 1; } -void SEQ_sequence_base_unique_name_recursive(ListBase *seqbasep, Sequence *seq) +void SEQ_sequence_base_unique_name_recursive(struct Scene *scene, + ListBase *seqbasep, + Sequence *seq) { SeqUniqueInfo sui; char *dot; @@ -167,7 +170,7 @@ void SEQ_sequence_base_unique_name_recursive(ListBase *seqbasep, Sequence *seq) SEQ_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui); } - BLI_strncpy(seq->name + 2, sui.name_dest, sizeof(seq->name) - 2); + SEQ_edit_sequence_name_set(scene, seq, sui.name_dest); } static const char *give_seqname_by_type(int type) @@ -629,7 +632,7 @@ void SEQ_ensure_unique_name(Sequence *seq, Scene *scene) char name[SEQ_NAME_MAXSTR]; BLI_strncpy_utf8(name, seq->name + 2, sizeof(name)); - SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq); + SEQ_sequence_base_unique_name_recursive(scene, &scene->ed->seqbase, seq); SEQ_dupe_animdata(scene, name, seq->name + 2); if (seq->type == SEQ_TYPE_META) { diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index edd5b555e2f..f3c4cb93e7d 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -190,6 +190,7 @@ struct wmWindow *WM_window_open(struct bContext *C, int sizex, int sizey, int space_type, + bool toplevel, bool dialog, bool temp, WindowAlignment alignment); @@ -205,6 +206,13 @@ void WM_autosave_init(struct wmWindowManager *wm); bool WM_recover_last_session(struct bContext *C, struct ReportList *reports); void WM_file_tag_modified(void); +struct ID *WM_file_link_datablock(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + struct View3D *v3d, + const char *filepath, + const short id_code, + const char *id_name); struct ID *WM_file_append_datablock(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, @@ -991,9 +999,9 @@ void WM_xr_action_binding_destroy(wmXrData *xr, unsigned int count_interaction_paths, const char **interaction_paths); -bool WM_xr_active_action_set_set( - wmXrData *xr, const char *action_set_name); /* If action_set_name is NULL, then - * all action sets will be treated as active. */ +/* If action_set_name is NULL, then all action sets will be treated as active. */ +bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name); + bool WM_xr_controller_pose_action_set(wmXrData *xr, const char *action_set_name, const char *action_name); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 168b775d16d..0b26f3c54ae 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -920,6 +920,7 @@ typedef struct wmDragAsset { /* Always freed. */ const char *path; int id_type; + int import_type; /* eFileAssetImportType */ } wmDragAsset; typedef struct wmDrag { diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index fd7f9c2de7c..db18ceb0e7f 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -341,7 +341,7 @@ typedef struct wmGizmoType { const char *idname; /* MAX_NAME */ /** Set to 'sizeof(wmGizmo)' or larger for instances of this type, - * use so we can cant to other types without the hassle of a custom-data pointer. */ + * use so we can cast to other types without the hassle of a custom-data pointer. */ uint struct_size; /** Initialize struct (calloc'd 'struct_size' region). */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c index 7cb1a0bdf4b..63e833d73c3 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c @@ -312,9 +312,10 @@ void WM_gizmo_do_msg_notify_tag_refresh(bContext *UNUSED(C), ARegion *region = msg_val->owner; wmGizmoMap *gzmap = msg_val->user_data; - ED_region_tag_redraw( - region); /* Could possibly avoid a full redraw and only tag for editor overlays - * redraw in some cases, see #ED_region_tag_redraw_editor_overlays(). */ + /* Could possibly avoid a full redraw and only tag for editor overlays + * redraw in some cases, see #ED_region_tag_redraw_editor_overlays(). */ + ED_region_tag_redraw(region); + WM_gizmomap_tag_refresh(gzmap); } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index b7a16b6c5ee..e899cbb22b9 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -26,6 +26,7 @@ #include <string.h> #include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "DNA_windowmanager_types.h" #include "MEM_guardedalloc.h" @@ -153,7 +154,7 @@ wmDrag *WM_event_start_drag( switch (type) { case WM_DRAG_PATH: BLI_strncpy(drag->path, poin, FILE_MAX); - /* As the path is being copied, free it immediately as `drag` wont "own" the data. */ + /* As the path is being copied, free it immediately as `drag` won't "own" the data. */ if (flags & WM_DRAG_FREE_DATA) { MEM_freeN(poin); } @@ -377,9 +378,17 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) { - /* Append only for now, wmDragAsset could have a `link` bool. */ - return WM_file_append_datablock( - G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name); + switch ((eFileAssetImportType)asset_drag->import_type) { + case FILE_ASSET_IMPORT_LINK: + return WM_file_link_datablock( + G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name); + case FILE_ASSET_IMPORT_APPEND: + return WM_file_append_datablock( + G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name); + } + + BLI_assert_unreachable(); + return NULL; } /** @@ -509,7 +518,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) rect->ymin = rect->ymax = cursory; } - /* Should we support multi-line drag draws? Maybe not, more types mixed wont work well. */ + /* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */ GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { const uchar text_col[] = {255, 255, 255, 255}; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index a6588c40f0f..750b4e5e60d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -380,8 +380,7 @@ void wm_event_do_depsgraph(bContext *C, bool is_after_open_file) */ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); if (is_after_open_file) { - DEG_graph_relations_update(depsgraph); - DEG_graph_on_visible_update(bmain, depsgraph, true); + DEG_graph_tag_on_visible_update(depsgraph, true); } DEG_make_active(depsgraph); BKE_scene_graph_update_tagged(depsgraph, bmain); @@ -520,7 +519,7 @@ void wm_event_do_notifiers(bContext *C) if (clear_info_stats) { /* Only do once since adding notifiers is slow when there are many. */ ViewLayer *view_layer = CTX_data_view_layer(C); - ED_info_stats_clear(view_layer); + ED_info_stats_clear(wm, view_layer); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL); } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index d0ee7075516..d165f8c37d5 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -52,12 +52,15 @@ #include "BLI_blenlib.h" #include "BLI_fileops_types.h" #include "BLI_linklist.h" +#include "BLI_math.h" #include "BLI_system.h" #include "BLI_threads.h" #include "BLI_timer.h" #include "BLI_utildefines.h" #include BLI_SYSTEM_PID_H +#include "PIL_time.h" + #include "BLT_translation.h" #include "BLF_api.h" @@ -733,6 +736,97 @@ static void wm_file_read_post(bContext *C, /** \name Read Main Blend-File API * \{ */ +static void file_read_reports_finalize(BlendFileReadReport *bf_reports) +{ + double duration_whole_minutes, duration_whole_seconds; + double duration_libraries_minutes, duration_libraries_seconds; + double duration_lib_override_minutes, duration_lib_override_seconds; + double duration_lib_override_resync_minutes, duration_lib_override_resync_seconds; + double duration_lib_override_recursive_resync_minutes, + duration_lib_override_recursive_resync_seconds; + + BLI_math_time_seconds_decompose(bf_reports->duration.whole, + NULL, + NULL, + &duration_whole_minutes, + &duration_whole_seconds, + NULL); + BLI_math_time_seconds_decompose(bf_reports->duration.libraries, + NULL, + NULL, + &duration_libraries_minutes, + &duration_libraries_seconds, + NULL); + BLI_math_time_seconds_decompose(bf_reports->duration.lib_overrides, + NULL, + NULL, + &duration_lib_override_minutes, + &duration_lib_override_seconds, + NULL); + BLI_math_time_seconds_decompose(bf_reports->duration.lib_overrides_resync, + NULL, + NULL, + &duration_lib_override_resync_minutes, + &duration_lib_override_resync_seconds, + NULL); + BLI_math_time_seconds_decompose(bf_reports->duration.lib_overrides_recursive_resync, + NULL, + NULL, + &duration_lib_override_recursive_resync_minutes, + &duration_lib_override_recursive_resync_seconds, + NULL); + + CLOG_INFO( + &LOG, 0, "Blender file read in %.0fm%.2fs", duration_whole_minutes, duration_whole_seconds); + CLOG_INFO(&LOG, + 0, + " * Loading libraries: %.0fm%.2fs", + duration_libraries_minutes, + duration_libraries_seconds); + CLOG_INFO(&LOG, + 0, + " * Applying overrides: %.0fm%.2fs", + duration_lib_override_minutes, + duration_lib_override_seconds); + CLOG_INFO(&LOG, + 0, + " * Resyncing overrides: %.0fm%.2fs (%d root overrides), including recursive " + "resyncs: %.0fm%.2fs)", + duration_lib_override_resync_minutes, + duration_lib_override_resync_seconds, + bf_reports->count.resynced_lib_overrides, + duration_lib_override_recursive_resync_minutes, + duration_lib_override_recursive_resync_seconds); + if (bf_reports->resynced_lib_overrides_libraries_count != 0) { + for (LinkNode *node_lib = bf_reports->resynced_lib_overrides_libraries; node_lib != NULL; + node_lib = node_lib->next) { + Library *library = node_lib->link; + BKE_reportf( + bf_reports->reports, RPT_INFO, "Library %s needs overrides resync.", library->filepath); + } + } + if (bf_reports->count.missing_libraries != 0 || bf_reports->count.missing_linked_id != 0) { + BKE_reportf(bf_reports->reports, + RPT_WARNING, + "%d libraries and %d linked data-blocks are missing, please check the " + "Info and Outliner editors for details", + bf_reports->count.missing_libraries, + bf_reports->count.missing_linked_id); + } + if (bf_reports->resynced_lib_overrides_libraries_count != 0) { + BKE_reportf(bf_reports->reports, + RPT_WARNING, + "%d libraries have overrides needing resync (auto resynced in %.0fm%.2fs), " + "please check the Info editor for details", + bf_reports->resynced_lib_overrides_libraries_count, + duration_lib_override_recursive_resync_minutes, + duration_lib_override_recursive_resync_seconds); + } + + BLI_linklist_free(bf_reports->resynced_lib_overrides_libraries, NULL); + bf_reports->resynced_lib_overrides_libraries = NULL; +} + bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) { /* assume automated tasks with background, don't write recent file list */ @@ -763,7 +857,9 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) .skip_flags = BLO_READ_SKIP_USERDEF, }; - struct BlendFileData *bfd = BKE_blendfile_read(filepath, ¶ms, reports); + BlendFileReadReport bf_reports = {.reports = reports, + .duration.whole = PIL_check_seconds_timer()}; + struct BlendFileData *bfd = BKE_blendfile_read(filepath, ¶ms, &bf_reports); if (bfd != NULL) { wm_file_read_pre(C, use_data, use_userdef); @@ -773,10 +869,10 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) wm_window_match_init(C, &wmbase); /* This flag is initialized by the operator but overwritten on read. - * need to re-enable it here else drivers + registered scripts wont work. */ + * need to re-enable it here else drivers and registered scripts won't work. */ const int G_f_orig = G.f; - BKE_blendfile_read_setup(C, bfd, ¶ms, reports); + BKE_blendfile_read_setup(C, bfd, ¶ms, &bf_reports); if (G.f != G_f_orig) { const int flags_keep = G_FLAG_ALL_RUNTIME; @@ -807,6 +903,9 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) wm_file_read_post(C, false, false, use_data, use_userdef, false); + bf_reports.duration.whole = PIL_check_seconds_timer() - bf_reports.duration.whole; + file_read_reports_finalize(&bf_reports); + success = true; } } @@ -911,7 +1010,10 @@ void wm_homefile_read(bContext *C, const char *app_template_override, bool *r_is_factory_startup) { - Main *bmain = G_MAIN; /* Context does not always have valid main pointer here... */ +#if 0 /* UNUSED, keep as this may be needed later & the comment below isn't self evident. */ + /* Context does not always have valid main pointer here. */ + Main *bmain = G_MAIN; +#endif ListBase wmbase; bool success = false; @@ -1084,10 +1186,15 @@ void wm_homefile_read(bContext *C, .skip_flags = skip_flags | BLO_READ_SKIP_USERDEF, }; - struct BlendFileData *bfd = BKE_blendfile_read(filepath_startup, ¶ms, NULL); + struct BlendFileData *bfd = BKE_blendfile_read( + filepath_startup, ¶ms, &(BlendFileReadReport){NULL}); if (bfd != NULL) { - BKE_blendfile_read_setup_ex( - C, bfd, ¶ms, NULL, update_defaults && use_data, app_template); + BKE_blendfile_read_setup_ex(C, + bfd, + ¶ms, + &(BlendFileReadReport){NULL}, + update_defaults && use_data, + app_template); success = true; } } @@ -1117,7 +1224,7 @@ void wm_homefile_read(bContext *C, struct BlendFileData *bfd = BKE_blendfile_read_from_memory( datatoc_startup_blend, datatoc_startup_blend_size, ¶ms, NULL); if (bfd != NULL) { - BKE_blendfile_read_setup_ex(C, bfd, ¶ms, NULL, true, NULL); + BKE_blendfile_read_setup_ex(C, bfd, ¶ms, &(BlendFileReadReport){NULL}, true, NULL); success = true; } @@ -1170,7 +1277,7 @@ void wm_homefile_read(bContext *C, BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template)); } - bmain = CTX_data_main(C); + Main *bmain = CTX_data_main(C); if (use_userdef) { /* check userdef before open window, keymaps etc */ @@ -1580,7 +1687,7 @@ static bool wm_file_write(bContext *C, BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST); - /* run this function after because the file cant be written before the blend is */ + /* run this function after because the file can't be written before the blend is */ if (ibuf_thumb) { IMB_thumb_delete(filepath, THB_FAIL); /* without this a failed thumb overrides */ ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb); @@ -1775,7 +1882,7 @@ void wm_open_init_use_scripts(wmOperator *op, bool use_prefs) if (!RNA_property_is_set(op->ptr, prop)) { /* use G_FLAG_SCRIPT_AUTOEXEC rather than the userpref because this means if * the flag has been disabled from the command line, then opening - * from the menu wont enable this setting. */ + * from the menu won't enable this setting. */ bool value = use_prefs ? ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) : ((G.f & G_FLAG_SCRIPT_AUTOEXEC) != 0); @@ -1833,7 +1940,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) &(const struct BlendFileWriteParams){ /* Make all paths absolute when saving the startup file. * On load the `G.relbase_valid` will be false so the paths - * wont have a base for resolving the relative paths. */ + * won't have a base for resolving the relative paths. */ .remap_mode = BLO_WRITE_PATH_REMAP_ABSOLUTE, /* Don't apply any path changes to the current blend file. */ .use_save_as_copy = true, @@ -2482,12 +2589,11 @@ static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op) { struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; uiLayout *layout = op->layout; - uiLayout *col = op->layout; const char *autoexec_text; uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE); - col = uiLayoutColumn(layout, false); + uiLayout *col = uiLayoutColumn(layout, false); if (file_info->is_untrusted) { autoexec_text = IFACE_("Trusted Source [Untrusted Path]"); uiLayoutSetActive(col, false); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 840debad01b..f938c507818 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -244,7 +244,7 @@ static void wm_link_do(WMLinkAppendData *lapp_data, bh = BLO_blendhandle_from_memory(datatoc_startup_blend, datatoc_startup_blend_size); } else { - bh = BLO_blendhandle_from_file(libname, reports); + bh = BLO_blendhandle_from_file(libname, &(BlendFileReadReport){.reports = reports}); } if (bh == NULL) { @@ -521,7 +521,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) wm_link_append_data_free(lapp_data); - /* important we unset, otherwise these object wont + /* important we unset, otherwise these object won't * link into other scenes from this blend file */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); @@ -642,23 +642,23 @@ void WM_OT_append(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Append Single Data-Block & Return it +/** \name Link/Append Single Data-Block & Return it * - * Used for appending workspace from startup files. * \{ */ -ID *WM_file_append_datablock(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - View3D *v3d, - const char *filepath, - const short id_code, - const char *id_name) +static ID *wm_file_link_datablock_ex(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + View3D *v3d, + const char *filepath, + const short id_code, + const char *id_name, + bool clear_pre_existing_flag) { /* Tag everything so we can make local only the new datablock. */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); - /* Define working data, with just the one item we want to append. */ + /* Define working data, with just the one item we want to link. */ WMLinkAppendData *lapp_data = wm_link_append_data_new(0); wm_link_append_data_library_add(lapp_data, filepath); @@ -672,6 +672,36 @@ ID *WM_file_append_datablock(Main *bmain, ID *id = item->new_id; wm_link_append_data_free(lapp_data); + if (clear_pre_existing_flag) { + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + } + + return id; +} + +ID *WM_file_link_datablock(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + View3D *v3d, + const char *filepath, + const short id_code, + const char *id_name) +{ + return wm_file_link_datablock_ex( + bmain, scene, view_layer, v3d, filepath, id_code, id_name, true); +} + +ID *WM_file_append_datablock(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + View3D *v3d, + const char *filepath, + const short id_code, + const char *id_name) +{ + ID *id = wm_file_link_datablock_ex( + bmain, scene, view_layer, v3d, filepath, id_code, id_name, false); + /* Make datablock local. */ BKE_library_make_local(bmain, NULL, NULL, true, false); @@ -978,7 +1008,7 @@ static void lib_relocate_do(Main *bmain, BKE_main_lib_objects_recalc_all(bmain); IMB_colormanagement_check_file_config(bmain); - /* important we unset, otherwise these object wont + /* important we unset, otherwise these object won't * link into other scenes from this blend file */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 15f0978f87d..48ab76317b5 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -272,7 +272,7 @@ void WM_init(bContext *C, int argc, const char **argv) * for scripts that do background processing with preview icons. */ BKE_icons_init(BIFICONID_LAST); - /* reports cant be initialized before the wm, + /* reports can't be initialized before the wm, * but keep before file reading, since that may report errors */ wm_init_reports(C); @@ -331,7 +331,7 @@ void WM_init(bContext *C, int argc, const char **argv) * startup.blend because it may contain PyDrivers. It also needs to be after * initializing space types and other internal data. * - * However cant redo this at the moment. Solution is to load python + * However can't redo this at the moment. Solution is to load python * before wm_homefile_read() or make py-drivers check if python is running. * Will try fix when the crash can be repeated. - campbell. */ diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 0a157e63b09..69b3660038d 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -1787,7 +1787,7 @@ void WM_keyconfig_update(wmWindowManager *wm) } if (wm_keymap_update_flag & WM_KEYMAP_UPDATE_OPERATORTYPE) { - /* an operatortype has been removed, this wont happen often + /* an operatortype has been removed, this won't happen often * but when it does we have to check _every_ keymap item */ ListBase *keymaps_lb[] = { &U.user_keymaps, diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index f33db7d50b5..1f3574f9032 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -343,7 +343,7 @@ static int wm_macro_exec(bContext *C, wmOperator *op) } } else { - CLOG_WARN(WM_LOG_OPERATORS, "'%s' cant exec macro", opm->type->idname); + CLOG_WARN(WM_LOG_OPERATORS, "'%s' can't exec macro", opm->type->idname); } } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 9175eb2dbf7..32b0ca66a33 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1346,7 +1346,7 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2) wmOperator *op; { /* Execute will free the operator. - * In this case, wm_operator_ui_popup_cancel wont run. */ + * In this case, wm_operator_ui_popup_cancel won't run. */ wmOpPopUp *data = arg1; op = data->op; MEM_freeN(data); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index cdd5ea12df8..ae5a2c81582 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -550,7 +550,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, } int scr_w, scr_h; - wm_get_screensize(&scr_w, &scr_h); + wm_get_desktopsize(&scr_w, &scr_h); int posy = (scr_h - win->posy - win->sizey); /* Clear drawable so we can set the new window. */ @@ -756,6 +756,7 @@ static bool wm_window_update_size_position(wmWindow *win) /** * \param space_type: SPACE_VIEW3D, SPACE_INFO, ... (eSpace_Type) + * \param toplevel: Not a child owned by other windows. A peer of main window. * \param dialog: whether this should be made as a dialog-style window * \param temp: whether this is considered a short-lived window * \param alignment: how this window is positioned relative to its parent @@ -768,6 +769,7 @@ wmWindow *WM_window_open(bContext *C, int sizex, int sizey, int space_type, + bool toplevel, bool dialog, bool temp, WindowAlignment alignment) @@ -822,7 +824,7 @@ wmWindow *WM_window_open(bContext *C, /* add new window? */ if (win == NULL) { - win = wm_window_new(bmain, wm, win_prev, dialog); + win = wm_window_new(bmain, wm, toplevel ? NULL : win_prev, dialog); win->posx = rect.xmin; win->posy = rect.ymin; *win->stereo3d_format = *win_prev->stereo3d_format; @@ -925,6 +927,7 @@ int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op)) area->spacetype, false, false, + false, WIN_ALIGN_PARENT_CENTER) != NULL); return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; @@ -2077,7 +2080,7 @@ void WM_init_tablet_api(void) if (g_system) { switch (U.tablet_api) { case USER_TABLET_NATIVE: - GHOST_SetTabletAPI(g_system, GHOST_kTabletNative); + GHOST_SetTabletAPI(g_system, GHOST_kTabletWinPointer); break; case USER_TABLET_WINTAB: GHOST_SetTabletAPI(g_system, GHOST_kTabletWintab); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 39bf2f1e32d..a4b32fac9fc 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -371,14 +371,14 @@ if(WITH_PYTHON) "${BLENDER_VERSION_CYCLE}" STREQUAL "rc") set(ADDON_EXCLUDE_CONDITIONAL "addons_contrib/*") else() - set(ADDON_EXCLUDE_CONDITIONAL "_addons_contrib/*") # dummy, wont do anything + set(ADDON_EXCLUDE_CONDITIONAL "_addons_contrib/*") # dummy, won't do anything endif() # do not install freestyle dir if disabled if(NOT WITH_FREESTYLE) set(FREESTYLE_EXCLUDE_CONDITIONAL "freestyle/*") else() - set(FREESTYLE_EXCLUDE_CONDITIONAL "_freestyle/*") # dummy, wont do anything + set(FREESTYLE_EXCLUDE_CONDITIONAL "_freestyle/*") # dummy, won't do anything endif() install( @@ -631,9 +631,9 @@ if(UNIX AND NOT APPLE) PATTERN "doc" EXCLUDE # ./doc PATTERN "tests" EXCLUDE # ./tests PATTERN "f2py" EXCLUDE # ./f2py - fortran/python interface code, not for blender. - PATTERN "include" EXCLUDE # include dirs all over, we wont use NumPy/CAPI + PATTERN "include" EXCLUDE # include dirs all over, we won't use NumPy/CAPI PATTERN "*.h" EXCLUDE # some includes are not in include dirs - PATTERN "*.a" EXCLUDE # ./core/lib/libnpymath.a - for linking, we dont need. + PATTERN "*.a" EXCLUDE # ./core/lib/libnpymath.a - for linking, we don't need. ) unset(_suffix) endif() diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 8b1ac05f086..3f5ca84fbef 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -1317,6 +1317,7 @@ static int arg_handle_register_extension(int UNUSED(argc), const char **UNUSED(a G.background = 1; } BLI_windows_register_blend_extension(G.background); + TerminateProcess(GetCurrentProcess(), 0); # else (void)data; /* unused */ # endif @@ -1959,7 +1960,9 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data) } BLI_strncpy(filename, argv[0], sizeof(filename)); + BLI_path_slash_native(filename); BLI_path_abs_from_cwd(filename, sizeof(filename)); + BLI_path_normalize(NULL, filename); /* load the file */ BKE_reports_init(&reports, RPT_PRINT); diff --git a/tests/gtests/CMakeLists.txt b/tests/gtests/CMakeLists.txt index 2a89aaa4aea..6e2dd380793 100644 --- a/tests/gtests/CMakeLists.txt +++ b/tests/gtests/CMakeLists.txt @@ -1,6 +1,6 @@ if(WITH_GTESTS) - # Otherwise we get warnings here that we cant fix in external projects + # Otherwise we get warnings here that we can't fix in external projects remove_strict_flags() # Build common test executable used by most tests diff --git a/tests/python/batch_import.py b/tests/python/batch_import.py index 7e64081cfbb..134ee091bb7 100644 --- a/tests/python/batch_import.py +++ b/tests/python/batch_import.py @@ -167,7 +167,7 @@ def main(): parser.add_option("-S", "--start", dest="start", help="From collected files, start with this index", metavar='int') parser.add_option("-E", "--end", dest="end", help="From collected files, end with this index", metavar='int') - options, _args = parser.parse_args(argv) # In this example we wont use the args + options, _args = parser.parse_args(argv) # In this example we won't use the args if not argv: parser.print_help() diff --git a/tests/python/bl_run_operators.py b/tests/python/bl_run_operators.py index 26fe6dac93d..6554dd8e414 100644 --- a/tests/python/bl_run_operators.py +++ b/tests/python/bl_run_operators.py @@ -89,7 +89,7 @@ op_blacklist = ( "wm.memory_statistics", # another annoying one "wm.dependency_relations", # another annoying one "wm.keymap_restore", # another annoying one - "wm.addon_*", # harmless, but dont change state + "wm.addon_*", # harmless, but don't change state "console.*", # just annoying - but harmless "wm.url_open_preset", # Annoying but harmless (opens web pages). @@ -178,7 +178,7 @@ if USE_ATTRSET: if issubclass(cls, skip_classes): continue - # # to support skip-save we cant get all props + # # to support skip-save we can't get all props # properties = cls.bl_rna.properties.keys() properties = [] for prop_id, prop in cls.bl_rna.properties.items(): diff --git a/tests/python/bl_test.py b/tests/python/bl_test.py index 110b4238f6c..6315ffbfa9d 100644 --- a/tests/python/bl_test.py +++ b/tests/python/bl_test.py @@ -32,18 +32,9 @@ def replace_bpy_app_version(): app = bpy.app app_fake = type(bpy)("bpy.app") - app_attr_exclude = { - # This causes a noisy warning every time. - "binary_path_python", - } - for attr in dir(app): - if attr.startswith("_"): - continue - if attr in app_attr_exclude: - continue - - setattr(app_fake, attr, getattr(app, attr)) + if not attr.startswith("_"): + setattr(app_fake, attr, getattr(app, attr)) app_fake.version = 0, 0, 0 app_fake.version_string = "0.00 (sub 0)" |