diff options
author | Antonis Ryakiotakis <kalast@gmail.com> | 2022-02-07 21:37:15 +0300 |
---|---|---|
committer | Antonis Ryakiotakis <kalast@gmail.com> | 2022-02-07 21:37:15 +0300 |
commit | d8c05502272990173381bfd2590884bbc95aa5f5 (patch) | |
tree | 8bd65efacbe6484b838d4550597d0f4a54ddddd9 | |
parent | b64d551f3b2fa409b4eeefb641fc581eb6cd0bd6 (diff) | |
parent | fe1816f67fbc6aaf383ec77847d668367335d093 (diff) |
Merge branch 'master' into KTX_supportKTX_support
934 files changed, 25230 insertions, 16944 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index cac8bee526e..2af91538110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,8 +157,9 @@ option(WITH_BLENDER "Build blender (disable to build only the blender player)" O mark_as_advanced(WITH_BLENDER) if(APPLE) - # Currently this causes a build error linking, disable. - set(WITH_BLENDER_THUMBNAILER OFF) + # In future, can be used with `quicklookthumbnailing/qlthumbnailreply` to create file + # thumbnails for say Finder. Turn it off for now. + option(WITH_BLENDER_THUMBNAILER "Build \"blender-thumbnailer\" thumbnail extraction utility" OFF) elseif(WIN32) option(WITH_BLENDER_THUMBNAILER "Build \"BlendThumb.dll\" helper for Windows explorer integration" ON) else() @@ -273,11 +274,13 @@ endif() if(UNIX AND NOT APPLE) option(WITH_SYSTEM_GLEW "Use GLEW OpenGL wrapper library provided by the operating system" OFF) - option(WITH_SYSTEM_GLES "Use OpenGL ES library provided by the operating system" ON) + option(WITH_SYSTEM_GLEW "Use GLEW OpenGL wrapper library provided by the operating system" OFF) + option(WITH_SYSTEM_FREETYPE "Use the freetype library provided by the operating system" OFF) else() # not an option for other OS's set(WITH_SYSTEM_GLEW OFF) set(WITH_SYSTEM_GLES OFF) + set(WITH_SYSTEM_FREETYPE OFF) endif() @@ -684,7 +687,7 @@ if(WIN32 OR XCODE) option(IDE_GROUP_PROJECTS_IN_FOLDERS "Organize the projects according to source folder structure." ON) mark_as_advanced(IDE_GROUP_PROJECTS_IN_FOLDERS) - if (IDE_GROUP_PROJECTS_IN_FOLDERS) + if(IDE_GROUP_PROJECTS_IN_FOLDERS) set_property(GLOBAL PROPERTY USE_FOLDERS ON) endif() endif() diff --git a/build_files/build_environment/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake index 5170a3a123e..f2944a41af8 100644 --- a/build_files/build_environment/cmake/boost.cmake +++ b/build_files/build_environment/cmake/boost.cmake @@ -25,8 +25,13 @@ else() endif() if(WIN32) - set(BOOST_TOOLSET toolset=msvc-14.1) - set(BOOST_COMPILER_STRING -vc141) + if(MSVC_VERSION GREATER_EQUAL 1920) # 2019 + set(BOOST_TOOLSET toolset=msvc-14.2) + set(BOOST_COMPILER_STRING -vc142) + else() # 2017 + set(BOOST_TOOLSET toolset=msvc-14.1) + set(BOOST_COMPILER_STRING -vc141) + endif() set(BOOST_CONFIGURE_COMMAND bootstrap.bat) set(BOOST_BUILD_COMMAND b2) diff --git a/build_files/build_environment/cmake/freetype.cmake b/build_files/build_environment/cmake/freetype.cmake index 52261b47618..c6663c287b1 100644 --- a/build_files/build_environment/cmake/freetype.cmake +++ b/build_files/build_environment/cmake/freetype.cmake @@ -19,13 +19,10 @@ set(FREETYPE_EXTRA_ARGS -DCMAKE_RELEASE_POSTFIX:STRING=2ST -DCMAKE_DEBUG_POSTFIX:STRING=2ST_d - -DWITH_BZip2=OFF - -DWITH_HarfBuzz=OFF - -DFT_WITH_HARFBUZZ=OFF - -DFT_WITH_BZIP2=OFF - -DFT_WITH_BROTLI=ON - -DCMAKE_DISABLE_FIND_PACKAGE_HarfBuzz=TRUE - -DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE + -DFT_DISABLE_BZIP2=ON + -DFT_DISABLE_HARFBUZZ=ON + -DFT_DISABLE_PNG=ON + -DFT_REQUIRE_BROTLI=ON -DPC_BROTLIDEC_INCLUDEDIR=${LIBDIR}/brotli/include -DPC_BROTLIDEC_LIBDIR=${LIBDIR}/brotli/lib ) diff --git a/build_files/build_environment/cmake/python_site_packages.cmake b/build_files/build_environment/cmake/python_site_packages.cmake index a8918fdb784..e58c3d90b4d 100644 --- a/build_files/build_environment/cmake/python_site_packages.cmake +++ b/build_files/build_environment/cmake/python_site_packages.cmake @@ -31,7 +31,7 @@ ExternalProject_Add(external_python_site_packages CONFIGURE_COMMAND ${PIP_CONFIGURE_COMMAND} BUILD_COMMAND "" PREFIX ${BUILD_DIR}/site_packages - INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all: + INSTALL_COMMAND ${PYTHON_BINARY} -m pip install --no-cache-dir ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all: ) if(USE_PIP_NUMPY) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 7abde5e5fc3..28a27023818 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -83,9 +83,9 @@ else() set(OPENEXR_VERSION_POSTFIX) endif() -set(FREETYPE_VERSION 2.11.0) +set(FREETYPE_VERSION 2.11.1) set(FREETYPE_URI http://prdownloads.sourceforge.net/freetype/freetype-${FREETYPE_VERSION}.tar.gz) -set(FREETYPE_HASH cf09172322f6b50cf8f568bf8fe14bde) +set(FREETYPE_HASH bd4e3b007474319909a6b79d50908e85) set(FREETYPE_HASH_TYPE MD5) set(FREETYPE_FILE freetype-${FREETYPE_VERSION}.tar.gz) @@ -189,11 +189,11 @@ set(OSL_HASH 1abd7ce40481771a9fa937f19595d2f2) set(OSL_HASH_TYPE MD5) set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz) -set(PYTHON_VERSION 3.9.7) -set(PYTHON_SHORT_VERSION 3.9) -set(PYTHON_SHORT_VERSION_NO_DOTS 39) +set(PYTHON_VERSION 3.10.2) +set(PYTHON_SHORT_VERSION 3.10) +set(PYTHON_SHORT_VERSION_NO_DOTS 310) set(PYTHON_URI https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz) -set(PYTHON_HASH fddb060b483bc01850a3f412eea1d954) +set(PYTHON_HASH 14e8c22458ed7779a1957b26cde01db9) set(PYTHON_HASH_TYPE MD5) set(PYTHON_FILE Python-${PYTHON_VERSION}.tar.xz) @@ -215,18 +215,20 @@ set(NANOVDB_HASH e7b9e863ec2f3b04ead171dec2322807) set(NANOVDB_HASH_TYPE MD5) set(NANOVDB_FILE nano-vdb-${NANOVDB_GIT_UID}.tar.gz) -set(IDNA_VERSION 3.2) -set(CHARSET_NORMALIZER_VERSION 2.0.6) -set(URLLIB3_VERSION 1.26.7) +set(IDNA_VERSION 3.3) +set(CHARSET_NORMALIZER_VERSION 2.0.10) +set(URLLIB3_VERSION 1.26.8) set(CERTIFI_VERSION 2021.10.8) -set(REQUESTS_VERSION 2.26.0) -set(CYTHON_VERSION 0.29.24) -set(ZSTANDARD_VERSION 0.15.2 ) - -set(NUMPY_VERSION 1.21.2) -set(NUMPY_SHORT_VERSION 1.21) +set(REQUESTS_VERSION 2.27.1) +set(CYTHON_VERSION 0.29.26) +# The version of the zstd library used to build the Python package should match ZSTD_VERSION defined below. +# At this time of writing, 0.17.0 was already released, but built against zstd 1.5.1, while we use 1.5.0. +set(ZSTANDARD_VERSION 0.16.0) + +set(NUMPY_VERSION 1.22.0) +set(NUMPY_SHORT_VERSION 1.22) set(NUMPY_URI https://github.com/numpy/numpy/releases/download/v${NUMPY_VERSION}/numpy-${NUMPY_VERSION}.zip) -set(NUMPY_HASH 5638d5dae3ca387be562912312db842e) +set(NUMPY_HASH 252de134862a27bd66705d29622edbfe) set(NUMPY_HASH_TYPE MD5) set(NUMPY_FILE numpy-${NUMPY_VERSION}.zip) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index ce7a251bfba..75c0b3c0009 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -379,27 +379,27 @@ USE_CXX11=true CLANG_FORMAT_VERSION_MIN="6.0" CLANG_FORMAT_VERSION_MEX="10.0" -PYTHON_VERSION="3.9.7" -PYTHON_VERSION_SHORT="3.9" -PYTHON_VERSION_MIN="3.7" -PYTHON_VERSION_MEX="3.11" +PYTHON_VERSION="3.10.2" +PYTHON_VERSION_SHORT="3.10" +PYTHON_VERSION_MIN="3.9" +PYTHON_VERSION_MEX="3.12" PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT PYTHON_FORCE_BUILD=false PYTHON_FORCE_REBUILD=false PYTHON_SKIP=false # Additional Python modules. -PYTHON_IDNA_VERSION="3.2" +PYTHON_IDNA_VERSION="3.3" PYTHON_IDNA_VERSION_MIN="2.0" PYTHON_IDNA_VERSION_MEX="4.0" PYTHON_IDNA_NAME="idna" -PYTHON_CHARSET_NORMALIZER_VERSION="2.0.6" +PYTHON_CHARSET_NORMALIZER_VERSION="2.0.10" PYTHON_CHARSET_NORMALIZER_VERSION_MIN="2.0.6" PYTHON_CHARSET_NORMALIZER_VERSION_MEX="2.1.0" # requests uses `charset_normalizer~=2.0.0` PYTHON_CHARSET_NORMALIZER_NAME="charset-normalizer" -PYTHON_URLLIB3_VERSION="1.26.7" +PYTHON_URLLIB3_VERSION="1.26.8" PYTHON_URLLIB3_VERSION_MIN="1.0" PYTHON_URLLIB3_VERSION_MEX="2.0" PYTHON_URLLIB3_NAME="urllib3" @@ -409,17 +409,17 @@ PYTHON_CERTIFI_VERSION_MIN="2021.0" PYTHON_CERTIFI_VERSION_MEX="2023.0" PYTHON_CERTIFI_NAME="certifi" -PYTHON_REQUESTS_VERSION="2.23.0" +PYTHON_REQUESTS_VERSION="2.27.1" PYTHON_REQUESTS_VERSION_MIN="2.0" PYTHON_REQUESTS_VERSION_MEX="3.0" PYTHON_REQUESTS_NAME="requests" -PYTHON_ZSTANDARD_VERSION="0.15.2" +PYTHON_ZSTANDARD_VERSION="0.16.0" PYTHON_ZSTANDARD_VERSION_MIN="0.15.2" -PYTHON_ZSTANDARD_VERSION_MEX="0.16.0" +PYTHON_ZSTANDARD_VERSION_MEX="0.20.0" PYTHON_ZSTANDARD_NAME="zstandard" -PYTHON_NUMPY_VERSION="1.21.2" +PYTHON_NUMPY_VERSION="1.22.0" PYTHON_NUMPY_VERSION_MIN="1.14" PYTHON_NUMPY_VERSION_MEX="2.0" PYTHON_NUMPY_NAME="numpy" @@ -499,7 +499,7 @@ LLVM_FORCE_REBUILD=false LLVM_SKIP=false # OSL needs to be compiled for now! -OSL_VERSION="1.11.14.1" +OSL_VERSION="1.11.17.0" OSL_VERSION_SHORT="1.11" OSL_VERSION_MIN="1.11" OSL_VERSION_MEX="2.0" @@ -4036,14 +4036,14 @@ install_DEB() { INFO "Forced Python building, as requested..." _do_compile_python=true else - check_package_version_ge_lt_DEB python3-dev $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX + check_package_version_ge_lt_DEB python${PYTHON_VERSION_SHORT}-dev $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then - PYTHON_VERSION_INSTALLED=$(echo `get_package_version_DEB python3-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') - - install_packages_DEB python3-dev + install_packages_DEB python${PYTHON_VERSION_SHORT}-dev clean_Python PRINT "" + PYTHON_VERSION_INSTALLED=$(echo `get_package_version_DEB python${PYTHON_VERSION_SHORT}-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') + for module in "${PYTHON_MODULES_PACKAGES[@]}" do module=($module) @@ -4681,11 +4681,11 @@ install_RPM() { else check_package_version_ge_lt_RPM python3-devel $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then - PYTHON_VERSION_INSTALLED=$(echo `get_package_version_RPM python3-devel` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') - install_packages_RPM python3-devel clean_Python + PYTHON_VERSION_INSTALLED=$(echo `get_package_version_RPM python3-devel` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') + for module in "${PYTHON_MODULES_PACKAGES[@]}" do module=($module) @@ -5224,12 +5224,12 @@ install_ARCH() { else check_package_version_ge_lt_ARCH python $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then - PYTHON_VERSION_INSTALLED=$(echo `get_package_version_ARCH python` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') - install_packages_ARCH python clean_Python PRINT "" + PYTHON_VERSION_INSTALLED=$(echo `get_package_version_ARCH python` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') + for module in "${PYTHON_MODULES_PACKAGES[@]}" do module=($module) diff --git a/build_files/build_environment/windows/build_deps.cmd b/build_files/build_environment/windows/build_deps.cmd index 5174af8e20d..c0e13ac3a55 100644 --- a/build_files/build_environment/windows/build_deps.cmd +++ b/build_files/build_environment/windows/build_deps.cmd @@ -1,19 +1,5 @@ @echo off if NOT "%1" == "" ( - if "%1" == "2013" ( - echo "Building for VS2013" - set VSVER=12.0 - set VSVER_SHORT=12 - set BuildDir=VS12 - goto par2 - ) - if "%1" == "2015" ( - echo "Building for VS2015" - set VSVER=14.0 - set VSVER_SHORT=14 - set BuildDir=VS14 - goto par2 - ) if "%1" == "2017" ( echo "Building for VS2017" set VSVER=15.0 @@ -21,44 +7,33 @@ if NOT "%1" == "" ( set BuildDir=VS15 goto par2 ) + if "%1" == "2019" ( + echo "Building for VS2019" + set VSVER=15.0 + set VSVER_SHORT=15 + set BuildDir=VS15 + goto par2 + ) ) :usage -Echo Usage build_deps 2013/2015/2017 x64/x86 +Echo Usage build_deps 2017/2019 x64 goto exit :par2 if NOT "%2" == "" ( - if "%2" == "x86" ( - echo "Building for x86" - set HARVESTROOT=Windows_vc - set ARCH=86 - if "%1" == "2013" ( - set CMAKE_BUILDER=Visual Studio 12 2013 - ) - if "%1" == "2015" ( - set CMAKE_BUILDER=Visual Studio 14 2015 - ) - if "%1" == "2017" ( - set CMAKE_BUILDER=Visual Studio 15 2017 - ) - - goto start - ) if "%2" == "x64" ( echo "Building for x64" set HARVESTROOT=Win64_vc set ARCH=64 - if "%1" == "2013" ( - set CMAKE_BUILDER=Visual Studio 12 2013 Win64 - ) - if "%1" == "2015" ( - set CMAKE_BUILDER=Visual Studio 14 2015 Win64 + if "%1" == "2019" ( + set CMAKE_BUILDER=Visual Studio 16 2019 + set CMAKE_BUILD_ARCH=-A x64 ) if "%1" == "2017" ( set CMAKE_BUILDER=Visual Studio 15 2017 Win64 + set CMAKE_BUILD_ARCH= ) - goto start ) ) @@ -120,7 +95,7 @@ set path=%BUILD_DIR%\downloads\mingw\mingw64\msys\1.0\bin\;%BUILD_DIR%\downloads mkdir %STAGING%\%BuildDir%%ARCH%R cd %Staging%\%BuildDir%%ARCH%R echo %DATE% %TIME% : Start > %StatusFile% -cmake -G "%CMAKE_BUILDER%" -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DBUILD_MODE=Release -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/ +cmake -G "%CMAKE_BUILDER%" %CMAKE_BUILD_ARCH% -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DBUILD_MODE=Release -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/ echo %DATE% %TIME% : Release Configuration done >> %StatusFile% if "%dobuild%" == "1" ( msbuild /m "ll.vcxproj" /p:Configuration=Release /fl /flp:logfile=BlenderDeps_llvm.log;Verbosity=normal @@ -133,7 +108,7 @@ if "%NODEBUG%" == "1" goto exit cd %BUILD_DIR% mkdir %STAGING%\%BuildDir%%ARCH%D cd %Staging%\%BuildDir%%ARCH%D -cmake -G "%CMAKE_BUILDER%" -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DCMAKE_BUILD_TYPE=Debug -DBUILD_MODE=Debug -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/ %CMAKE_DEBUG_OPTIONS% +cmake -G "%CMAKE_BUILDER%" %CMAKE_BUILD_ARCH% -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DCMAKE_BUILD_TYPE=Debug -DBUILD_MODE=Debug -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/ %CMAKE_DEBUG_OPTIONS% echo %DATE% %TIME% : Debug Configuration done >> %StatusFile% if "%dobuild%" == "1" ( msbuild /m "ll.vcxproj" /p:Configuration=Debug /fl /flp:logfile=BlenderDeps_llvm.log;;Verbosity=normal diff --git a/build_files/cmake/Modules/FindBrotli.cmake b/build_files/cmake/Modules/FindBrotli.cmake new file mode 100644 index 00000000000..d1e40b1966a --- /dev/null +++ b/build_files/cmake/Modules/FindBrotli.cmake @@ -0,0 +1,83 @@ +# - Find Brotli library (compression for freetype/woff2). +# This module defines +# BROTLI_INCLUDE_DIRS, where to find Brotli headers, Set when +# BROTLI_INCLUDE_DIR is found. +# BROTLI_LIBRARIES, libraries to link against to use Brotli. +# BROTLI_ROOT_DIR, The base directory to search for Brotli. +# This can also be an environment variable. +# BROTLI_FOUND, If false, do not try to use Brotli. +# + +#============================================================================= +# Copyright 2022 Blender Foundation. +# +# Distributed under the OSI-approved BSD 3-Clause License, +# see accompanying file BSD-3-Clause-license.txt for details. +#============================================================================= + +# If BROTLI_ROOT_DIR was defined in the environment, use it. +IF(NOT BROTLI_ROOT_DIR AND NOT $ENV{BROTLI_ROOT_DIR} STREQUAL "") + SET(BROTLI_ROOT_DIR $ENV{BROTLI_ROOT_DIR}) +ENDIF() + +SET(_BROTLI_SEARCH_DIRS + ${BROTLI_ROOT_DIR} +) + +FIND_PATH(BROTLI_INCLUDE_DIR + NAMES + brotli/decode.h + HINTS + ${_BROTLI_SEARCH_DIRS} + PATH_SUFFIXES + include + DOC "Brotli header files" +) + +FIND_LIBRARY(BROTLI_LIBRARY_COMMON + NAMES + # Some builds use a special `-static` postfix in their static libraries names. + brotlicommon-static + brotlicommon + HINTS + ${_BROTLI_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib lib/static + DOC "Brotli static common library" +) +FIND_LIBRARY(BROTLI_LIBRARY_DEC + NAMES + # Some builds use a special `-static` postfix in their static libraries names. + brotlidec-static + brotlidec + HINTS + ${_BROTLI_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib lib/static + DOC "Brotli static decode library" +) + + +IF(${BROTLI_LIBRARY_COMMON_NOTFOUND} or ${BROTLI_LIBRARY_DEC_NOTFOUND}) + set(BROTLI_FOUND FALSE) +ELSE() + # handle the QUIETLY and REQUIRED arguments and set BROTLI_FOUND to TRUE if + # all listed variables are TRUE + INCLUDE(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Brotli DEFAULT_MSG BROTLI_LIBRARY_COMMON BROTLI_LIBRARY_DEC BROTLI_INCLUDE_DIR) + + IF(BROTLI_FOUND) + get_filename_component(BROTLI_LIBRARY_DIR ${BROTLI_LIBRARY_COMMON} DIRECTORY) + SET(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) + SET(BROTLI_LIBRARIES ${BROTLI_LIBRARY_DEC} ${BROTLI_LIBRARY_COMMON}) + ENDIF() +ENDIF() + +MARK_AS_ADVANCED( + BROTLI_INCLUDE_DIR + BROTLI_LIBRARY_COMMON + BROTLI_LIBRARY_DEC + BROTLI_LIBRARY_DIR +) + +UNSET(_BROTLI_SEARCH_DIRS) diff --git a/build_files/cmake/Modules/FindPythonLibsUnix.cmake b/build_files/cmake/Modules/FindPythonLibsUnix.cmake index a6fb463432b..e0fe8dd00cb 100644 --- a/build_files/cmake/Modules/FindPythonLibsUnix.cmake +++ b/build_files/cmake/Modules/FindPythonLibsUnix.cmake @@ -34,7 +34,7 @@ IF(NOT PYTHON_ROOT_DIR AND NOT $ENV{PYTHON_ROOT_DIR} STREQUAL "") SET(PYTHON_ROOT_DIR $ENV{PYTHON_ROOT_DIR}) ENDIF() -SET(PYTHON_VERSION 3.9 CACHE STRING "Python Version (major and minor only)") +SET(PYTHON_VERSION 3.10 CACHE STRING "Python Version (major and minor only)") MARK_AS_ADVANCED(PYTHON_VERSION) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index e0219cd1121..94d5fe1e31b 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1197,21 +1197,21 @@ endfunction() macro(openmp_delayload projectname ) - if(MSVC) - if(WITH_OPENMP) - if(MSVC_CLANG) - set(OPENMP_DLL_NAME "libomp") - elseif(MSVC_VERSION EQUAL 1800) - set(OPENMP_DLL_NAME "vcomp120") - else() - set(OPENMP_DLL_NAME "vcomp140") - endif() - set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib") - set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /DELAYLOAD:${OPENMP_DLL_NAME}d.dll delayimp.lib") - set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib") - set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib") + if(MSVC) + if(WITH_OPENMP) + if(MSVC_CLANG) + set(OPENMP_DLL_NAME "libomp") + elseif(MSVC_VERSION EQUAL 1800) + set(OPENMP_DLL_NAME "vcomp120") + else() + set(OPENMP_DLL_NAME "vcomp140") endif() + set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib") + set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /DELAYLOAD:${OPENMP_DLL_NAME}d.dll delayimp.lib") + set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib") + set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib") endif() + endif() endmacro() macro(set_and_warn_dependency diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index 8acea1be841..379370a989a 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -128,25 +128,20 @@ if(WITH_CODEC_SNDFILE) endif() if(WITH_PYTHON) - # we use precompiled libraries for py 3.9 and up by default - set(PYTHON_VERSION 3.9) + # Use precompiled libraries by default. + set(PYTHON_VERSION 3.10) if(NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK) - # normally cached but not since we include them with blender + # Normally cached but not since we include them with blender. set(PYTHON_INCLUDE_DIR "${LIBDIR}/python/include/python${PYTHON_VERSION}") set(PYTHON_EXECUTABLE "${LIBDIR}/python/bin/python${PYTHON_VERSION}") set(PYTHON_LIBRARY ${LIBDIR}/python/lib/libpython${PYTHON_VERSION}.a) set(PYTHON_LIBPATH "${LIBDIR}/python/lib/python${PYTHON_VERSION}") - # set(PYTHON_LINKFLAGS "-u _PyMac_Error") # won't build with this enabled else() - # module must be compiled against Python framework + # Module must be compiled against Python framework. set(_py_framework "/Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}") - set(PYTHON_INCLUDE_DIR "${_py_framework}/include/python${PYTHON_VERSION}") set(PYTHON_EXECUTABLE "${_py_framework}/bin/python${PYTHON_VERSION}") set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}") - # set(PYTHON_LIBRARY python${PYTHON_VERSION}) - # set(PYTHON_LINKFLAGS "-u _PyMac_Error -framework Python") # won't build with this enabled - unset(_py_framework) endif() diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 5aace62211c..6a896709cc2 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -48,6 +48,9 @@ if(NOT DEFINED LIBDIR) unset(LIBDIR_CENTOS7_ABI) endif() +# Support restoring this value once pre-compiled libraries have been handled. +set(WITH_STATIC_LIBS_INIT ${WITH_STATIC_LIBS}) + if(EXISTS ${LIBDIR}) message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}") @@ -101,12 +104,21 @@ find_package_wrapper(PNG REQUIRED) find_package_wrapper(ZLIB REQUIRED) find_package_wrapper(Zstd REQUIRED) -# FreeType compiled with Brotli compression for woff2. -find_package_wrapper(Freetype REQUIRED) -list(APPEND FREETYPE_LIBRARIES - ${LIBDIR}/brotli/lib/libbrotlidec-static.a - ${LIBDIR}/brotli/lib/libbrotlicommon-static.a -) +if(NOT WITH_SYSTEM_FREETYPE) + # FreeType compiled with Brotli compression for woff2. + find_package_wrapper(Freetype REQUIRED) + if(EXISTS ${LIBDIR}) + find_package_wrapper(Brotli REQUIRED) + + # NOTE: This is done on WIN32 & APPLE but fails on some Linux systems. + # See: https://devtalk.blender.org/t/22536 + # So `BROTLI_LIBRARIES` need to be added directly after `FREETYPE_LIBRARIES`. + # + # list(APPEND FREETYPE_LIBRARIES + # ${BROTLI_LIBRARIES} + # ) + endif() +endif() if(WITH_PYTHON) # No way to set py35, remove for now. @@ -542,6 +554,21 @@ add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE # # Keep last, so indirectly linked libraries don't override our own pre-compiled libs. +if(EXISTS ${LIBDIR}) + # Clear the prefix path as it causes the `LIBDIR` to override system locations. + unset(CMAKE_PREFIX_PATH) + + # Since the pre-compiled `LIBDIR` directories have been handled, don't prefer static libraries. + set(WITH_STATIC_LIBS ${WITH_STATIC_LIBS_INIT}) +endif() + +if(WITH_SYSTEM_FREETYPE) + find_package_wrapper(Freetype) + if(NOT FREETYPE_FOUND) + message(FATAL_ERROR "Failed finding system FreeType version!") + endif() +endif() + if(WITH_LZO AND WITH_SYSTEM_LZO) find_package_wrapper(LZO) if(NOT LZO_FOUND) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 2a4e16e56d1..9486619c0cc 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -55,6 +55,10 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang") message(WARNING "stripped pdb not supported with clang, disabling..") set(WITH_WINDOWS_STRIPPED_PDB OFF) endif() +else() + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.28.29921) # MSVC 2019 16.9.16 + message(FATAL_ERROR "Compiler is unsupported, MSVC 2019 16.9.16 or newer is required for building blender.") + endif() endif() if(NOT WITH_PYTHON_MODULE) @@ -265,12 +269,6 @@ if(NOT DEFINED LIBDIR) 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) - message(STATUS "Visual Studio 2017 detected.") - set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) - elseif(MSVC_VERSION EQUAL 1900) - message(STATUS "Visual Studio 2015 detected.") - set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) endif() else() message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}") @@ -471,7 +469,7 @@ if(WITH_JACK) endif() if(WITH_PYTHON) - set(PYTHON_VERSION 3.9) # CACHE STRING) + set(PYTHON_VERSION 3.10) # CACHE STRING) string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION}) set(PYTHON_LIBRARY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.lib) diff --git a/build_files/windows/autodetect_msvc.cmd b/build_files/windows/autodetect_msvc.cmd index a4ab8929040..fd0c19764e9 100644 --- a/build_files/windows/autodetect_msvc.cmd +++ b/build_files/windows/autodetect_msvc.cmd @@ -3,9 +3,6 @@ echo No explicit msvc version requested, autodetecting version. call "%~dp0\detect_msvc2019.cmd" if %ERRORLEVEL% EQU 0 goto DetectionComplete -call "%~dp0\detect_msvc2017.cmd" -if %ERRORLEVEL% EQU 0 goto DetectionComplete - call "%~dp0\detect_msvc2022.cmd" if %ERRORLEVEL% EQU 0 goto DetectionComplete diff --git a/build_files/windows/check_libraries.cmd b/build_files/windows/check_libraries.cmd index c495ee6eee9..e8c04fb3258 100644 --- a/build_files/windows/check_libraries.cmd +++ b/build_files/windows/check_libraries.cmd @@ -1,4 +1,3 @@ -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 diff --git a/build_files/windows/configure_msbuild.cmd b/build_files/windows/configure_msbuild.cmd index a9267e8cfbb..9fbdef25641 100644 --- a/build_files/windows/configure_msbuild.cmd +++ b/build_files/windows/configure_msbuild.cmd @@ -19,12 +19,6 @@ if "%WITH_PYDEBUG%"=="1" ( set PYDEBUG_CMAKE_ARGS=-DWINDOWS_PYTHON_DEBUG=On ) -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% if NOT EXIST %BUILD_DIR%\nul ( diff --git a/build_files/windows/configure_ninja.cmd b/build_files/windows/configure_ninja.cmd index 90085feb2bd..684673b7611 100644 --- a/build_files/windows/configure_ninja.cmd +++ b/build_files/windows/configure_ninja.cmd @@ -37,15 +37,9 @@ set LLVM_DIR= :DetectionComplete set CC=%LLVM_DIR%\bin\clang-cl set CXX=%LLVM_DIR%\bin\clang-cl - if "%BUILD_VS_YEAR%" == "2019" ( - rem build and tested against 2019 16.2 - set CFLAGS=-m64 -fmsc-version=1922 - set CXXFLAGS=-m64 -fmsc-version=1922 - ) else ( - rem build and tested against 2017 15.7 - set CFLAGS=-m64 -fmsc-version=1914 - set CXXFLAGS=-m64 -fmsc-version=1914 - ) + rem build and tested against 2019 16.2 + set CFLAGS=-m64 -fmsc-version=1922 + set CXXFLAGS=-m64 -fmsc-version=1922 ) if "%WITH_ASAN%"=="1" ( diff --git a/build_files/windows/detect_msvc2017.cmd b/build_files/windows/detect_msvc2017.cmd deleted file mode 100644 index 5f760275f78..00000000000 --- a/build_files/windows/detect_msvc2017.cmd +++ /dev/null @@ -1,3 +0,0 @@ -set BUILD_VS_VER=15 -set BUILD_VS_YEAR=2017 -call "%~dp0\detect_msvc_vswhere.cmd" diff --git a/build_files/windows/find_dependencies.cmd b/build_files/windows/find_dependencies.cmd index 9fa3b156a4f..fec2bd2e752 100644 --- a/build_files/windows/find_dependencies.cmd +++ b/build_files/windows/find_dependencies.cmd @@ -3,7 +3,32 @@ for %%X in (svn.exe) do (set SVN=%%~$PATH:X) for %%X in (cmake.exe) do (set CMAKE=%%~$PATH:X) for %%X in (ctest.exe) do (set CTEST=%%~$PATH:X) for %%X in (git.exe) do (set GIT=%%~$PATH:X) +REM For python, default on 39 but if that does not exist also check +REM the 310,311 and 312 folders to see if those are there, it checks +REM this far ahead to ensure good lib folder compatiblity in the future. set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe +if EXIST %PYTHON% ( + goto detect_python_done +) +set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\310\bin\python.exe +if EXIST %PYTHON% ( + goto detect_python_done +) +set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\311\bin\python.exe +if EXIST %PYTHON% ( + goto detect_python_done +) +set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\312\bin\python.exe +if EXIST %PYTHON% ( + goto detect_python_done +) + +if NOT EXIST %PYTHON% ( + echo Warning: Python not found, there is likely an issue with the library folder + set PYTHON="" +) + +:detect_python_done if NOT "%verbose%" == "" ( echo svn : "%SVN%" echo cmake : "%CMAKE%" @@ -11,7 +36,3 @@ if NOT "%verbose%" == "" ( echo git : "%GIT%" echo python : "%PYTHON%" ) -if "%CMAKE%" == "" ( - echo Cmake not found in path, required for building, exiting... - exit /b 1 -) diff --git a/build_files/windows/format.cmd b/build_files/windows/format.cmd index d5003c9f8d8..95440cb1818 100644 --- a/build_files/windows/format.cmd +++ b/build_files/windows/format.cmd @@ -9,17 +9,11 @@ exit /b 1 :detect_done echo found clang-format in %CF_PATH% -if EXIST %PYTHON% ( - set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe - goto detect_python_done +if NOT EXIST %PYTHON% ( + echo python not found, required for this operation + exit /b 1 ) -echo python not found in lib folder -exit /b 1 - -:detect_python_done -echo found python (%PYTHON%) - set FORMAT_PATHS=%BLENDER_DIR%\source\tools\utils_maintenance\clang_format_paths.py REM The formatting script expects clang-format to be in the current PATH. diff --git a/build_files/windows/icons.cmd b/build_files/windows/icons.cmd index d51b27d8953..9390ccf827c 100644 --- a/build_files/windows/icons.cmd +++ b/build_files/windows/icons.cmd @@ -1,18 +1,8 @@ -if EXIST "%PYTHON%" ( - goto detect_python_done +if NOT EXIST %PYTHON% ( + echo python not found, required for this operation + exit /b 1 ) -set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe -if EXIST %PYTHON% ( - goto detect_python_done -) - -echo python not found at %PYTHON% -exit /b 1 - -:detect_python_done -echo found python (%PYTHON%) - call "%~dp0\find_inkscape.cmd" if EXIST "%INKSCAPE_BIN%" ( diff --git a/build_files/windows/icons_geom.cmd b/build_files/windows/icons_geom.cmd index 18312daf35b..4cb3bf5b4df 100644 --- a/build_files/windows/icons_geom.cmd +++ b/build_files/windows/icons_geom.cmd @@ -1,18 +1,8 @@ -if EXIST %PYTHON% ( - goto detect_python_done +if NOT EXIST %PYTHON% ( + echo python not found, required for this operation + exit /b 1 ) -set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe -if EXIST %PYTHON% ( - goto detect_python_done -) - -echo python not found at %PYTHON% -exit /b 1 - -:detect_python_done -echo found python (%PYTHON%) - call "%~dp0\find_blender.cmd" if EXIST "%BLENDER_BIN%" ( diff --git a/build_files/windows/parse_arguments.cmd b/build_files/windows/parse_arguments.cmd index dcef46c2c9a..eaf4a85f7ac 100644 --- a/build_files/windows/parse_arguments.cmd +++ b/build_files/windows/parse_arguments.cmd @@ -50,14 +50,6 @@ if NOT "%1" == "" ( goto ERR ) else if "%1" == "x64" ( set BUILD_ARCH=x64 - ) else if "%1" == "2017" ( - set BUILD_VS_YEAR=2017 - ) else if "%1" == "2017pre" ( - set BUILD_VS_YEAR=2017 - set VSWHERE_ARGS=-prerelease - ) else if "%1" == "2017b" ( - set BUILD_VS_YEAR=2017 - set VSWHERE_ARGS=-products Microsoft.VisualStudio.Product.BuildTools ) else if "%1" == "2019" ( set BUILD_VS_YEAR=2019 ) else if "%1" == "2019pre" ( diff --git a/build_files/windows/show_help.cmd b/build_files/windows/show_help.cmd index d914ecab2b8..00d5bd7a9a8 100644 --- a/build_files/windows/show_help.cmd +++ b/build_files/windows/show_help.cmd @@ -24,12 +24,12 @@ echo - nobuildinfo ^(disable buildinfo^) echo - debug ^(Build an unoptimized debuggable build^) echo - packagename [newname] ^(override default cpack package name^) echo - builddir [newdir] ^(override default build folder^) -echo - 2017 ^(build with visual studio 2017^) -echo - 2017pre ^(build with visual studio 2017 pre-release^) -echo - 2017b ^(build with visual studio 2017 Build Tools^) echo - 2019 ^(build with visual studio 2019^) echo - 2019pre ^(build with visual studio 2019 pre-release^) echo - 2019b ^(build with visual studio 2019 Build Tools^) +echo - 2022 ^(build with visual studio 2022^) +echo - 2022pre ^(build with visual studio 2022 pre-release^) +echo - 2022b ^(build with visual studio 2022 Build Tools^) echo. echo Documentation Targets ^(Not associated with building^) diff --git a/build_files/windows/svn_fix.cmd b/build_files/windows/svn_fix.cmd index a9dcdf36847..9ab48de10f1 100644 --- a/build_files/windows/svn_fix.cmd +++ b/build_files/windows/svn_fix.cmd @@ -1,4 +1,3 @@ -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 diff --git a/build_files/windows/update_sources.cmd b/build_files/windows/update_sources.cmd index f8fbd383090..f99ce43f40f 100644 --- a/build_files/windows/update_sources.cmd +++ b/build_files/windows/update_sources.cmd @@ -1,10 +1,7 @@ -if EXIST %PYTHON% ( - goto detect_python_done +if NOT EXIST %PYTHON% ( + echo python not found, required for this operation + exit /b 1 ) - -echo python not found in lib folder -exit /b 1 - :detect_python_done REM Use -B to avoid writing __pycache__ in lib directory and causing update conflicts. diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 89954d8a155..2f004491c89 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.1 +PROJECT_NUMBER = V3.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 0ae3b24578b..2133f5fcb9f 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -417,7 +417,8 @@ MODULE_GROUPING = { BLENDER_REVISION = str(bpy.app.build_hash, 'utf_8') # '2.83.0 Beta' or '2.83.0' or '2.83.1' -BLENDER_VERSION_DOTS = bpy.app.version_string +BLENDER_VERSION_STRING = bpy.app.version_string +BLENDER_VERSION_DOTS = "%d.%d" % (bpy.app.version[0], bpy.app.version[1]) if BLENDER_REVISION != "Unknown": # SHA1 Git hash @@ -1724,11 +1725,11 @@ def write_sphinx_conf_py(basepath): fw("import sys, os\n\n") fw("extensions = ['sphinx.ext.intersphinx']\n\n") fw("intersphinx_mapping = {'blender_manual': ('https://docs.blender.org/manual/en/dev/', None)}\n\n") - fw("project = 'Blender %s Python API'\n" % BLENDER_VERSION_DOTS) + fw("project = 'Blender %s Python API'\n" % BLENDER_VERSION_STRING) fw("master_doc = 'index'\n") fw("copyright = u'Blender Foundation'\n") - fw("version = '%s'\n" % BLENDER_VERSION_HASH) - fw("release = '%s'\n" % BLENDER_VERSION_HASH) + fw("version = '%s'\n" % BLENDER_VERSION_DOTS) + fw("release = '%s'\n" % BLENDER_VERSION_DOTS) # Quiet file not in table-of-contents warnings. fw("exclude_patterns = [\n") @@ -1749,6 +1750,7 @@ except ModuleNotFoundError: fw("if html_theme == 'sphinx_rtd_theme':\n") fw(" html_theme_options = {\n") + fw(" 'display_version': False,\n") # fw(" 'analytics_id': '',\n") # fw(" 'collapse_navigation': True,\n") fw(" 'sticky_navigation': False,\n") @@ -1765,10 +1767,15 @@ except ModuleNotFoundError: fw("html_show_search_summary = True\n") fw("html_split_index = True\n") fw("html_static_path = ['static']\n") + fw("templates_path = ['templates']\n") + fw("html_context = {'commit': '%s'}\n" % BLENDER_VERSION_HASH) fw("html_extra_path = ['static/favicon.ico', 'static/blender_logo.svg']\n") fw("html_favicon = 'static/favicon.ico'\n") fw("html_logo = 'static/blender_logo.svg'\n") fw("html_last_updated_fmt = '%m/%d/%Y'\n\n") + fw("if html_theme == 'sphinx_rtd_theme':\n") + fw(" html_css_files = ['css/version_switch.css']\n") + fw(" html_js_files = ['js/version_switch.js']\n") # needed for latex, pdf gen fw("latex_elements = {\n") @@ -2125,6 +2132,9 @@ def copy_theme_assets(basepath): shutil.copytree(os.path.join(SCRIPT_DIR, "static"), os.path.join(basepath, "static"), copy_function=shutil.copy) + shutil.copytree(os.path.join(SCRIPT_DIR, "templates"), + os.path.join(basepath, "templates"), + copy_function=shutil.copy) def rna2sphinx(basepath): diff --git a/doc/python_api/static/css/version_switch.css b/doc/python_api/static/css/version_switch.css new file mode 100644 index 00000000000..360ff2eea0e --- /dev/null +++ b/doc/python_api/static/css/version_switch.css @@ -0,0 +1,127 @@ +/* Override RTD theme */ +.rst-versions { + border-top: 0px; + overflow: visible; +} +.version-btn.vdeact { + cursor: default; + color: dimgray; +} + +.version-btn.vdeact::after { + content: ""; +} +#versionwrap { + display: flex; + padding-top: 2px; + font-size: 90%; + justify-content: center; + flex-wrap: wrap; +} +.version-btn { + display: inline-block; + background-color: #272525; + width: 140px; + text-align: center; + padding: 3px 10px; + margin: 0px 5px 4px; + vertical-align: middle; + color: #27AE60; + border: solid 1px #444444; + border-radius: 3px; + cursor: pointer; + z-index: 400; + transition: border-color 0.4s; +} +.version-btn::after { + content:"\f0d8"; + display: inline; + font: normal normal normal 16px/1 FontAwesome; + color: #8d8c8c; + vertical-align: top; + padding-left: 0.5em; +} +.version-btn-open::after { + color: gray; +} +.version-btn:hover, .version-btn:focus { + border-color: #525252; +} +.version-btn-open { + color: gray; + border: solid 1px gray; +} +.version-btn.wait { + cursor: wait; +} +.version-btn.disabled { + cursor: not-allowed; + color: dimgray; +} +.version-dialog { + display: none; + position: absolute; + bottom: 28px; + width: 140px; + margin: 0 5px; + padding-bottom: 4px; + background-color: #0003; + border-radius: 3px; + box-shadow: 0 0 6px #000C; + z-index: 999; + max-height: calc(100vh - 30px); + overflow-y: auto; + cursor: default; +} +.version-title { + padding: 5px; + color: black; + text-align: center; + font-size: 102%; + background-color: #27ae60; + border-bottom: solid 1.5px #444; +} +.version-list { + margin-bottom: 4px; + text-align: center; + background-color: #000C; + border: solid 1px gray; + border-radius: 0px 0px 3px 3px; +} +.version-list a, .version-list span, .version-list li { + position: relative; + display: block; + font-size: 98%; + line-height: 1.15; + width: 100%; + margin: 0; + padding: 4px 0px; + color: #404040; +} +.version-list li { + background-color: #ede9e9; + color: #404040; + padding: 1px; +} +.version-list li:hover, .version-list li a:focus { + background-color: #b9cfda; +} +.version-list li.selected, .version-list li.selected:hover { + background-color: #8d8c8c; +} +.version-list li.selected span { + cursor: default; + outline-color: red; +} +.version-arrow { + position: absolute; + width: 8px; + height: 8px; + left: 50%; + bottom: 4px; + margin-left: -4px; + transform: rotate(225deg); + background: #ede9e9; + border: 1px solid gray; + border-width: 1px 0 0 1px; +} diff --git a/doc/python_api/static/js/version_switch.js b/doc/python_api/static/js/version_switch.js new file mode 100644 index 00000000000..d69b17c31bb --- /dev/null +++ b/doc/python_api/static/js/version_switch.js @@ -0,0 +1,323 @@ +(function() { // switch: v1.2 +"use strict"; + +var versionsFileUrl = "https://docs.blender.org/PROD/versions.json" + +var all_versions; + +var Popover = function() { + function Popover(id) + { + this.isOpen = false; + this.type = (id === "version-popover"); + this.$btn = $('#' + id); + this.$dialog = this.$btn.next(); + this.$list = this.$dialog.children("ul"); + this.sel = null; + this.beforeInit(); + } + + Popover.prototype = { + beforeInit : function() { + var that = this; + this.$btn.on("click", function(e) { + that.init(); + e.preventDefault(); + e.stopPropagation(); + }); + this.$btn.on("keydown", function(e) { + if (that.btnKeyFilter(e)) { + that.init(); + e.preventDefault(); + e.stopPropagation(); + } + }); + }, + init : function() { + this.$btn.off("click"); + this.$btn.off("keydown"); + + if (all_versions === undefined) { + this.$btn.addClass("wait"); + this.loadVL(this); + } + else { + this.afterLoad(); + } + }, + loadVL : function(that) { + $.getJSON(versionsFileUrl, function(data) { + all_versions = data; + that.afterLoad(); + return true; + }).fail(function() { + console.log("Version Switch Error: versions.json could not be loaded."); + that.$btn.addClass("disabled"); + return false; + }); + }, + afterLoad : function() { + var release = DOCUMENTATION_OPTIONS.VERSION; + const m = release.match(/\d\.\d+/g); + if (m) { + release = m[0]; + } + + this.warnOld(release, all_versions); + + var version = this.getNamed(release); + var list = this.buildList(version); + + this.$list.children(":first-child").remove(); + this.$list.append(list); + var that = this; + this.$list.on("keydown", function(e) { + that.keyMove(e); + }); + + this.$btn.removeClass("wait"); + this.btnOpenHandler(); + this.$btn.on("mousedown", function(e) { + that.btnOpenHandler(); + e.preventDefault() + }); + this.$btn.on("keydown", function(e) { + if (that.btnKeyFilter(e)) { + that.btnOpenHandler(); + } + }); + }, + warnOld : function(release, all_versions) { + // Note this is effectively disabled now, two issues must fixed: + // * versions.js does not contain a current entry, because that leads to + // duplicate version numbers in the menu. These need to be deduplicated. + // * It only shows the warning after opening the menu to switch version + // when versions.js is loaded. This is too late to be useful. + var current = all_versions.current + if (!current) + { + // console.log("Version Switch Error: no 'current' in version.json."); + return; + } + const m = current.match(/\d\.\d+/g); + if (m) { + current = parseFloat(m[0]); + } + if (release < current) { + var currentURL = window.location.pathname.replace(release, current); + var warning = $('<div class="admonition warning"> ' + + '<p class="first admonition-title">Note</p> ' + + '<p class="last"> ' + + 'You are not using the most up to date version of the documentation. ' + + '<a href="#"></a> is the newest version.' + + '</p>' + + '</div>'); + + warning.find('a').attr('href', currentURL).text(current); + + var body = $("div.body"); + if (!body.length) { + body = $("div.document"); + } + body.prepend(warning); + } + }, + buildList : function(v) { + var url = new URL(window.location.href); + let pathSplit = [ "", "api", v ]; + if (url.pathname.startsWith("/api/")) { + pathSplit.push(url.pathname.split('/').slice(3).join('/')); + } + else { + pathSplit.push(url.pathname.substring(1)); + } + if (this.type) { + var dyn = all_versions; + var cur = v; + } + var buf = []; + var that = this; + $.each(dyn, function(ix, title) { + buf.push("<li"); + if (ix === cur) { + buf.push( + ' class="selected" tabindex="-1" role="presentation"><span tabindex="-1" role="menuitem" aria-current="page">' + + title + '</spanp></li>'); + } + else { + pathSplit[2 + that.type] = ix; + var href = new URL(url); + href.pathname = pathSplit.join('/'); + buf.push(' tabindex="-1" role="presentation"><a href ="' + href + '" tabindex="-1">' + + title + '</a></li>'); + } + }); + return buf.join(''); + }, + getNamed : function(v) { + $.each(all_versions, function(ix, title) { + if (ix === "master" || ix === "latest") { + var m = title.match(/\d\.\d[\w\d\.]*/)[0]; + if (parseFloat(m) == v) { + v = ix; + return false; + } + } + }); + return v; + }, + dialogToggle : function(speed) { + var wasClose = !this.isOpen; + var that = this; + if (!this.isOpen) { + this.$btn.addClass("version-btn-open"); + this.$btn.attr("aria-pressed", true); + this.$dialog.attr("aria-hidden", false); + this.$dialog.fadeIn(speed, function() { + that.$btn.parent().on("focusout", function(e) { + that.focusoutHandler(); + e.stopImmediatePropagation(); + }) + that.$btn.parent().on("mouseleave", function(e) { + that.mouseoutHandler(); + e.stopImmediatePropagation(); + }); + }); + this.isOpen = true; + } + else { + this.$btn.removeClass("version-btn-open"); + this.$btn.attr("aria-pressed", false); + this.$dialog.attr("aria-hidden", true); + this.$btn.parent().off("focusout"); + this.$btn.parent().off("mouseleave"); + this.$dialog.fadeOut(speed, function() { + if (this.$sel) { + this.$sel.attr("tabindex", -1); + } + that.$btn.attr("tabindex", 0); + if (document.activeElement !== null && document.activeElement !== document && + document.activeElement !== document.body) { + that.$btn.focus(); + } + }); + this.isOpen = false; + } + + if (wasClose) { + if (this.$sel) { + this.$sel.attr("tabindex", -1); + } + if (document.activeElement !== null && document.activeElement !== document && + document.activeElement !== document.body) { + var $nw = this.listEnter(); + $nw.attr("tabindex", 0); + $nw.focus(); + this.$sel = $nw; + } + } + }, + btnOpenHandler : function() { + this.dialogToggle(300); + }, + focusoutHandler : function() { + var list = this.$list; + var that = this; + setTimeout(function() { + if (list.find(":focus").length === 0) { + that.dialogToggle(200); + } + }, 200); + }, + mouseoutHandler : function() { + this.dialogToggle(200); + }, + btnKeyFilter : function(e) { + if (e.ctrlKey || e.shiftKey) { + return false; + } + if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) || + e.key === "ArrowDown" || e.key === "ArrowUp") { + return true; + } + return false; + }, + keyMove : function(e) { + if (e.ctrlKey || e.shiftKey) { + return true; + } + var p = true; + var $nw = $(e.target); + switch (e.key) { + case "ArrowUp": + $nw = this.listPrev($nw); + break; + case "ArrowDown": + $nw = this.listNext($nw); + break; + case "Home": + $nw = this.listFirst(); + break; + case "End": + $nw = this.listLast(); + break; + case "Escape": + $nw = this.listExit(); + break; + case "ArrowLeft": + $nw = this.listExit(); + break; + case "ArrowRight": + $nw = this.listExit(); + break; + default: + p = false; + } + if (p) { + $nw.attr("tabindex", 0); + $nw.focus(); + if (this.$sel) { + this.$sel.attr("tabindex", -1); + } + this.$sel = $nw; + e.preventDefault(); + e.stopPropagation(); + } + }, + listPrev : function($nw) { + if ($nw.parent().prev().length !== 0) { + return $nw.parent().prev().children(":first-child"); + } + else { + return this.listLast(); + } + }, + listNext : function($nw) { + if ($nw.parent().next().length !== 0) { + return $nw.parent().next().children(":first-child"); + } + else { + return this.listFirst(); + } + }, + listFirst : function() { + return this.$list.children(":first-child").children(":first-child"); + }, + listLast : function() { + return this.$list.children(":last-child").children(":first-child"); + }, + listExit : function() { + this.mouseoutHandler(); + return this.$btn; + }, + listEnter : function() { + return this.$list.children(":first-child").children(":first-child"); + } + }; + return Popover +}(); + +$(document).ready(function() { + var lng_popover = new Popover("version-popover"); +}); +})(); diff --git a/doc/python_api/templates/versions.html b/doc/python_api/templates/versions.html new file mode 100644 index 00000000000..64b47185ba7 --- /dev/null +++ b/doc/python_api/templates/versions.html @@ -0,0 +1,17 @@ +<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="document versions"> + <ul id="versionwrap" role="presentation"> + <li role="presentation"> + <span id="version-popover" class="version-btn" tabindex="0" role="button" aria-label="versions selector" aria-haspopup="true" aria-controls="version-vsnlist" aria-disabled="true"> + {{ release }} + </span> + <div class="version-dialog" aria-hidden="true"> + <div class="version-arrow" aria-hidden="true"></div> + <div class="version-title">Versions</div> + <ul id="version-vsnlist" class="version-list" role="menu" aria-labelledby="version-popover" aria-hidden="true"> + <li role="presentation">Loading...</li> + </ul> + </div> + </li> + </ul> +</div> +
\ No newline at end of file diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 1fdc8e60167..be76e8b0db1 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -113,6 +113,6 @@ if(WITH_MOD_FLUID) add_subdirectory(mantaflow) endif() -if (WITH_COMPOSITOR) +if(WITH_COMPOSITOR) add_subdirectory(smaa_areatex) endif() diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index f669adb9f37..1afb321da3d 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -74,7 +74,7 @@ enum_panorama_types = ( "Similar to most fisheye modern lens, takes sensor dimensions into consideration"), ('MIRRORBALL', "Mirror Ball", "Uses the mirror ball mapping"), ('FISHEYE_LENS_POLYNOMIAL', "Fisheye Lens Polynomial", - "Defines the lens projection as polynomial to allow real world camera lenses to be mimicked."), + "Defines the lens projection as polynomial to allow real world camera lenses to be mimicked"), ) enum_curve_shape = ( @@ -667,6 +667,11 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Use special type BVH optimized for hair (uses more ram but renders faster)", default=True, ) + debug_use_compact_bvh: BoolProperty( + name="Use Compact BVH", + description="Use compact BVH structure (uses less ram but renders slower)", + default=True, + ) debug_bvh_time_steps: IntProperty( name="BVH Time Steps", description="Split BVH primitives by this number of time steps to speed up render time in cost of memory", @@ -896,27 +901,27 @@ class CyclesCameraSettings(bpy.types.PropertyGroup): fisheye_polynomial_k0: FloatProperty( name="Fisheye Polynomial K0", - description="Coefficient K0 of the lens polinomial", + description="Coefficient K0 of the lens polynomial", default=camera.default_fisheye_polynomial[0], precision=6, step=0.1, subtype='ANGLE', ) fisheye_polynomial_k1: FloatProperty( name="Fisheye Polynomial K1", - description="Coefficient K1 of the lens polinomial", + description="Coefficient K1 of the lens polynomial", default=camera.default_fisheye_polynomial[1], precision=6, step=0.1, subtype='ANGLE', ) fisheye_polynomial_k2: FloatProperty( name="Fisheye Polynomial K2", - description="Coefficient K2 of the lens polinomial", + description="Coefficient K2 of the lens polynomial", default=camera.default_fisheye_polynomial[2], precision=6, step=0.1, subtype='ANGLE', ) fisheye_polynomial_k3: FloatProperty( name="Fisheye Polynomial K3", - description="Coefficient K3 of the lens polinomial", + description="Coefficient K3 of the lens polynomial", default=camera.default_fisheye_polynomial[3], precision=6, step=0.1, subtype='ANGLE', ) fisheye_polynomial_k4: FloatProperty( name="Fisheye Polynomial K4", - description="Coefficient K4 of the lens polinomial", + description="Coefficient K4 of the lens polynomial", default=camera.default_fisheye_polynomial[4], precision=6, step=0.1, subtype='ANGLE', ) @@ -1447,6 +1452,19 @@ class CyclesPreferences(bpy.types.AddonPreferences): num += 1 return num + def has_multi_device(self): + import _cycles + compute_device_type = self.get_compute_device_type() + device_list = _cycles.available_devices(compute_device_type) + for device in device_list: + if device[1] == compute_device_type: + continue + for dev in self.devices: + if dev.use and dev.id == device[2]: + return True + + return False + def has_active_device(self): return self.get_num_gpu_devices() > 0 diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 5b600692152..e4b2fef87c3 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -118,6 +118,12 @@ def use_optix(context): return (get_device_type(context) == 'OPTIX' and cscene.device == 'GPU') +def use_multi_device(context): + cscene = context.scene.cycles + if cscene.device != 'GPU': + return False + return context.preferences.addons[__package__].preferences.has_multi_device() + def show_device_active(context): cscene = context.scene.cycles @@ -661,6 +667,10 @@ class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Pa bl_label = "Acceleration Structure" bl_parent_id = "CYCLES_RENDER_PT_performance" + @classmethod + def poll(cls, context): + return not use_optix(context) or use_multi_device(context) + def draw(self, context): import _cycles @@ -673,21 +683,33 @@ class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Pa col = layout.column() - use_embree = False + use_embree = _cycles.with_embree + if use_cpu(context): - use_embree = _cycles.with_embree - if not use_embree: + col.prop(cscene, "debug_use_spatial_splits") + if use_embree: + col.prop(cscene, "debug_use_compact_bvh") + else: + sub = col.column() + sub.active = not cscene.debug_use_spatial_splits + sub.prop(cscene, "debug_bvh_time_steps") + + col.prop(cscene, "debug_use_hair_bvh") + sub = col.column(align=True) sub.label(text="Cycles built without Embree support") sub.label(text="CPU raytracing performance will be poor") + else: + col.prop(cscene, "debug_use_spatial_splits") + sub = col.column() + sub.active = not cscene.debug_use_spatial_splits + sub.prop(cscene, "debug_bvh_time_steps") - col.prop(cscene, "debug_use_spatial_splits") - sub = col.column() - sub.active = not use_embree - sub.prop(cscene, "debug_use_hair_bvh") - sub = col.column() - sub.active = not cscene.debug_use_spatial_splits and not use_embree - sub.prop(cscene, "debug_bvh_time_steps") + col.prop(cscene, "debug_use_hair_bvh") + + # CPU is used in addition to a GPU + if use_multi_device(context) and use_embree: + col.prop(cscene, "debug_use_compact_bvh") class CYCLES_RENDER_PT_performance_final_render(CyclesButtonsPanel, Panel): diff --git a/intern/cycles/blender/curves.cpp b/intern/cycles/blender/curves.cpp index 65a02d041cc..102ddf5ee32 100644 --- a/intern/cycles/blender/curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <optional> + #include "blender/sync.h" #include "blender/util.h" @@ -624,15 +626,36 @@ void BlenderSync::sync_particle_hair( } } -#ifdef WITH_HAIR_NODES -static float4 hair_point_as_float4(BL::HairPoint b_point) +#ifdef WITH_NEW_CURVES_TYPE + +static std::optional<BL::FloatAttribute> find_curves_radius_attribute(BL::Curves b_curves) { - float4 mP = float3_to_float4(get_float3(b_point.co())); - mP.w = b_point.radius(); + for (BL::Attribute &b_attribute : b_curves.attributes) { + if (b_attribute.name() != "radius") { + continue; + } + if (b_attribute.domain() != BL::Attribute::domain_POINT) { + continue; + } + if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) { + continue; + } + return BL::FloatAttribute{b_attribute}; + } + return std::nullopt; +} + +static float4 hair_point_as_float4(BL::Curves b_curves, + std::optional<BL::FloatAttribute> b_attr_radius, + const int index) +{ + float4 mP = float3_to_float4(get_float3(b_curves.position_data[index].vector())); + mP.w = b_attr_radius ? b_attr_radius->data[index].value() : 0.0f; return mP; } -static float4 interpolate_hair_points(BL::Hair b_hair, +static float4 interpolate_hair_points(BL::Curves b_curves, + std::optional<BL::FloatAttribute> b_attr_radius, const int first_point_index, const int num_points, const float step) @@ -641,12 +664,12 @@ static float4 interpolate_hair_points(BL::Hair b_hair, const int point_a = clamp((int)curve_t, 0, num_points - 1); const int point_b = min(point_a + 1, num_points - 1); const float t = curve_t - (float)point_a; - return lerp(hair_point_as_float4(b_hair.points[first_point_index + point_a]), - hair_point_as_float4(b_hair.points[first_point_index + point_b]), + return lerp(hair_point_as_float4(b_curves, b_attr_radius, first_point_index + point_a), + hair_point_as_float4(b_curves, b_attr_radius, first_point_index + point_b), t); } -static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) +static void export_hair_curves(Scene *scene, Hair *hair, BL::Curves b_curves) { /* TODO: optimize so we can straight memcpy arrays from Blender? */ @@ -666,17 +689,19 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) } /* Reserve memory. */ - const int num_keys = b_hair.points.length(); - const int num_curves = b_hair.curves.length(); + const int num_keys = b_curves.points.length(); + const int num_curves = b_curves.curves.length(); hair->reserve_curves(num_curves, num_keys); + std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves); + /* Export curves and points. */ vector<float> points_length; - for (BL::HairCurve &b_curve : b_hair.curves) { - const int first_point_index = b_curve.first_point_index(); - const int num_points = b_curve.num_points(); + for (int i = 0; i < num_curves; i++) { + const int first_point_index = b_curves.curve_offset_data[i].value(); + const int num_points = b_curves.curve_offset_data[i + 1].value() - first_point_index; float3 prev_co = zero_float3(); float length = 0.0f; @@ -687,10 +712,9 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) /* Position and radius. */ for (int i = 0; i < num_points; i++) { - BL::HairPoint b_point = b_hair.points[first_point_index + i]; - - const float3 co = get_float3(b_point.co()); - const float radius = b_point.radius(); + const float3 co = get_float3(b_curves.position_data[first_point_index + i].vector()); + const float radius = b_attr_radius ? b_attr_radius->data[first_point_index + i].value() : + 0.0f; hair->add_curve_key(co, radius); if (attr_intercept) { @@ -715,7 +739,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) /* Random number per curve. */ if (attr_random != NULL) { - attr_random->add(hash_uint2_to_float(b_curve.index(), 0)); + attr_random->add(hash_uint2_to_float(i, 0)); } /* Curve. */ @@ -724,7 +748,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) } } -static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_step) +static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motion_step) { /* Find or add attribute. */ Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); @@ -737,14 +761,17 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st /* Export motion keys. */ const int num_keys = hair->get_curve_keys().size(); + const int num_curves = b_curves.curves.length(); float4 *mP = attr_mP->data_float4() + motion_step * num_keys; bool have_motion = false; int num_motion_keys = 0; int curve_index = 0; - for (BL::HairCurve &b_curve : b_hair.curves) { - const int first_point_index = b_curve.first_point_index(); - const int num_points = b_curve.num_points(); + std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves); + + for (int i = 0; i < num_curves; i++) { + const int first_point_index = b_curves.curve_offset_data[i].value(); + const int num_points = b_curves.curve_offset_data[i + 1].value() - first_point_index; Hair::Curve curve = hair->get_curve(curve_index); curve_index++; @@ -755,7 +782,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st int point_index = first_point_index + i; if (point_index < num_keys) { - mP[num_motion_keys] = hair_point_as_float4(b_hair.points[point_index]); + mP[num_motion_keys] = hair_point_as_float4(b_curves, b_attr_radius, point_index); num_motion_keys++; if (!have_motion) { @@ -774,7 +801,8 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st const float step_size = curve.num_keys > 1 ? 1.0f / (curve.num_keys - 1) : 0.0f; for (int i = 0; i < curve.num_keys; i++) { const float step = i * step_size; - mP[num_motion_keys] = interpolate_hair_points(b_hair, first_point_index, num_points, step); + mP[num_motion_keys] = interpolate_hair_points( + b_curves, b_attr_radius, first_point_index, num_points, step); num_motion_keys++; } have_motion = true; @@ -791,12 +819,12 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step) { /* Convert Blender hair to Cycles curves. */ - BL::Hair b_hair(b_ob_info.object_data); + BL::Curves b_curves(b_ob_info.object_data); if (motion) { - export_hair_curves_motion(hair, b_hair, motion_step); + export_hair_curves_motion(hair, b_curves, motion_step); } else { - export_hair_curves(scene, hair, b_hair); + export_hair_curves(scene, hair, b_curves); } } #else @@ -819,8 +847,8 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, H new_hair.set_used_shaders(used_shaders); if (view_layer.use_hair) { -#ifdef WITH_HAIR_NODES - if (b_ob_info.object_data.is_a(&RNA_Hair)) { +#ifdef WITH_NEW_CURVES_TYPE + if (b_ob_info.object_data.is_a(&RNA_Curves)) { /* Hair object. */ sync_hair(&new_hair, b_ob_info, false); } @@ -873,8 +901,8 @@ void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph, /* Export deformed coordinates. */ if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) { -#ifdef WITH_HAIR_NODES - if (b_ob_info.object_data.is_a(&RNA_Hair)) { +#ifdef WITH_NEW_CURVES_TYPE + if (b_ob_info.object_data.is_a(&RNA_Curves)) { /* Hair object. */ sync_hair(hair, b_ob_info, true, motion_step); return; diff --git a/intern/cycles/blender/geometry.cpp b/intern/cycles/blender/geometry.cpp index 78c803b7adb..a9b61f2578f 100644 --- a/intern/cycles/blender/geometry.cpp +++ b/intern/cycles/blender/geometry.cpp @@ -32,8 +32,8 @@ CCL_NAMESPACE_BEGIN static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair) { -#ifdef WITH_HAIR_NODES - if (b_ob_info.object_data.is_a(&RNA_Hair) || use_particle_hair) { +#ifdef WITH_NEW_CURVES_TYPE + if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) { #else if (use_particle_hair) { #endif @@ -231,8 +231,8 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph, if (progress.get_cancel()) return; -#ifdef WITH_HAIR_NODES - if (b_ob_info.object_data.is_a(&RNA_Hair) || use_particle_hair) { +#ifdef WITH_NEW_CURVES_TYPE + if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) { #else if (use_particle_hair) { #endif diff --git a/intern/cycles/blender/object.cpp b/intern/cycles/blender/object.cpp index 65a04a39660..22acc09c538 100644 --- a/intern/cycles/blender/object.cpp +++ b/intern/cycles/blender/object.cpp @@ -72,7 +72,7 @@ bool BlenderSync::object_is_geometry(BObjectInfo &b_ob_info) BL::Object::type_enum type = b_ob_info.iter_object.type(); - if (type == BL::Object::type_VOLUME || type == BL::Object::type_HAIR || + if (type == BL::Object::type_VOLUME || type == BL::Object::type_CURVES || type == BL::Object::type_POINTCLOUD) { /* Will be exported attached to mesh. */ return true; @@ -97,7 +97,7 @@ bool BlenderSync::object_can_have_geometry(BL::Object &b_ob) case BL::Object::type_SURFACE: case BL::Object::type_META: case BL::Object::type_FONT: - case BL::Object::type_HAIR: + case BL::Object::type_CURVES: case BL::Object::type_POINTCLOUD: case BL::Object::type_VOLUME: return true; diff --git a/intern/cycles/blender/output_driver.cpp b/intern/cycles/blender/output_driver.cpp index d5cc0c60bae..f35b48493cb 100644 --- a/intern/cycles/blender/output_driver.cpp +++ b/intern/cycles/blender/output_driver.cpp @@ -51,8 +51,6 @@ bool BlenderOutputDriver::read_render_tile(const Tile &tile) BL::RenderLayer b_rlay = *b_single_rlay; - vector<float> pixels(static_cast<size_t>(tile.size.x) * tile.size.y * 4); - /* Copy each pass. * TODO:copy only the required ones for better performance? */ for (BL::RenderPass &b_pass : b_rlay.passes) { diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index 5604c2989fd..39e49ac3478 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -689,6 +689,9 @@ static ShaderNode *add_node(Scene *scene, else if (b_node.is_a(&RNA_ShaderNodeHairInfo)) { node = graph->create_node<HairInfoNode>(); } + else if (b_node.is_a(&RNA_ShaderNodePointInfo)) { + node = graph->create_node<PointInfoNode>(); + } else if (b_node.is_a(&RNA_ShaderNodeVolumeInfo)) { node = graph->create_node<VolumeInfoNode>(); } diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index 588e057b9ad..7e6f1535d66 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -787,6 +787,7 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background) params.bvh_type = BVH_TYPE_DYNAMIC; params.use_bvh_spatial_split = RNA_boolean_get(&cscene, "debug_use_spatial_splits"); + params.use_bvh_compact_structure = RNA_boolean_get(&cscene, "debug_use_compact_bvh"); params.use_bvh_unaligned_nodes = RNA_boolean_get(&cscene, "debug_use_hair_bvh"); params.num_bvh_time_steps = RNA_int_get(&cscene, "debug_bvh_time_steps"); diff --git a/intern/cycles/bvh/embree.cpp b/intern/cycles/bvh/embree.cpp index 618dd9438d5..616b6273e6a 100644 --- a/intern/cycles/bvh/embree.cpp +++ b/intern/cycles/bvh/embree.cpp @@ -66,6 +66,26 @@ static_assert(Object::MAX_MOTION_STEPS == Geometry::MAX_MOTION_STEPS, * as well as filtering for volume objects happen here. * Cycles' own BVH does that directly inside the traversal calls. */ +static void rtc_filter_intersection_func(const RTCFilterFunctionNArguments *args) +{ + /* Current implementation in Cycles assumes only single-ray intersection queries. */ + assert(args->N == 1); + + RTCHit *hit = (RTCHit *)args->hit; + CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt; + const KernelGlobalsCPU *kg = ctx->kg; + const Ray *cray = ctx->ray; + + if (kernel_embree_is_self_intersection(kg, hit, cray)) { + *args->valid = 0; + } +} + +/* This gets called by Embree at every valid ray/object intersection. + * Things like recording subsurface or shadow hits for later evaluation + * as well as filtering for volume objects happen here. + * Cycles' own BVH does that directly inside the traversal calls. + */ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) { /* Current implementation in Cycles assumes only single-ray intersection queries. */ @@ -75,12 +95,16 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) RTCHit *hit = (RTCHit *)args->hit; CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt; const KernelGlobalsCPU *kg = ctx->kg; + const Ray *cray = ctx->ray; switch (ctx->type) { case CCLIntersectContext::RAY_SHADOW_ALL: { Intersection current_isect; kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - + if (intersection_skip_self_shadow(cray->self, current_isect.object, current_isect.prim)) { + *args->valid = 0; + return; + } /* If no transparent shadows or max number of hits exceeded, all light is blocked. */ const int flags = intersection_get_shader_flags(kg, current_isect.prim, current_isect.type); if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->num_hits >= ctx->max_hits) { @@ -160,6 +184,10 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) break; } } + if (intersection_skip_self_local(cray->self, current_isect.prim)) { + *args->valid = 0; + return; + } /* No intersection information requested, just return a hit. */ if (ctx->max_hits == 0) { @@ -225,6 +253,11 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) if (ctx->num_hits < ctx->max_hits) { Intersection current_isect; kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); + if (intersection_skip_self(cray->self, current_isect.object, current_isect.prim)) { + *args->valid = 0; + return; + } + Intersection *isect = &ctx->isect_s[ctx->num_hits]; ++ctx->num_hits; *isect = current_isect; @@ -236,12 +269,15 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) } /* This tells Embree to continue tracing. */ *args->valid = 0; - break; } + break; } case CCLIntersectContext::RAY_REGULAR: default: - /* Nothing to do here. */ + if (kernel_embree_is_self_intersection(kg, hit, cray)) { + *args->valid = 0; + return; + } break; } } @@ -257,6 +293,14 @@ static void rtc_filter_func_backface_cull(const RTCFilterFunctionNArguments *arg *args->valid = 0; return; } + + CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt; + const KernelGlobalsCPU *kg = ctx->kg; + const Ray *cray = ctx->ray; + + if (kernel_embree_is_self_intersection(kg, hit, cray)) { + *args->valid = 0; + } } static void rtc_filter_occluded_func_backface_cull(const RTCFilterFunctionNArguments *args) @@ -355,10 +399,12 @@ void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_) } const bool dynamic = params.bvh_type == BVH_TYPE_DYNAMIC; + const bool compact = params.use_compact_structure; scene = rtcNewScene(rtc_device); const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) | - RTC_SCENE_FLAG_COMPACT | RTC_SCENE_FLAG_ROBUST; + (compact ? RTC_SCENE_FLAG_COMPACT : RTC_SCENE_FLAG_NONE) | + RTC_SCENE_FLAG_ROBUST; rtcSetSceneFlags(scene, scene_flags); build_quality = dynamic ? RTC_BUILD_QUALITY_LOW : (params.use_spatial_split ? RTC_BUILD_QUALITY_HIGH : @@ -503,6 +549,7 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i) rtcSetGeometryUserData(geom_id, (void *)prim_offset); rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); + rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_intersection_func); rtcSetGeometryMask(geom_id, ob->visibility_for_tracing()); rtcCommitGeometry(geom_id); @@ -765,6 +812,7 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) rtcSetGeometryUserData(geom_id, (void *)prim_offset); if (hair->curve_shape == CURVE_RIBBON) { + rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_intersection_func); rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); } else { diff --git a/intern/cycles/bvh/params.h b/intern/cycles/bvh/params.h index 16edf2e88e4..61fa5484ce0 100644 --- a/intern/cycles/bvh/params.h +++ b/intern/cycles/bvh/params.h @@ -97,6 +97,9 @@ class BVHParams { */ bool use_unaligned_nodes; + /* Use compact acceleration structure (Embree)*/ + bool use_compact_structure; + /* Split time range to this number of steps and create leaf node for each * of this time steps. * @@ -139,6 +142,7 @@ class BVHParams { top_level = false; bvh_layout = BVH_LAYOUT_BVH2; + use_compact_structure = true; use_unaligned_nodes = false; num_motion_curve_steps = 0; diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index f46d18a4926..8d9631e5b44 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -559,10 +559,10 @@ if(WITH_CYCLES_DEVICE_METAL) find_library(METAL_LIBRARY Metal) # This file was added in the 12.0 SDK, use it as a way to detect the version. - if (METAL_LIBRARY AND NOT EXISTS "${METAL_LIBRARY}/Headers/MTLFunctionStitching.h") + if(METAL_LIBRARY AND NOT EXISTS "${METAL_LIBRARY}/Headers/MTLFunctionStitching.h") message(STATUS "Metal version too old, must be SDK 12.0 or newer, disabling WITH_CYCLES_DEVICE_METAL") set(WITH_CYCLES_DEVICE_METAL OFF) - elseif (NOT METAL_LIBRARY) + elseif(NOT METAL_LIBRARY) message(STATUS "Metal not found, disabling WITH_CYCLES_DEVICE_METAL") set(WITH_CYCLES_DEVICE_METAL OFF) else() diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp index 4f1cbabc89b..85ed3dc5b55 100644 --- a/intern/cycles/device/hip/device_impl.cpp +++ b/intern/cycles/device/hip/device_impl.cpp @@ -905,8 +905,8 @@ void HIPDevice::tex_alloc(device_texture &mem) address_mode = hipAddressModeClamp; break; case EXTENSION_CLIP: - // TODO : (Arya) setting this to Mode Clamp instead of Mode Border because it's unsupported - // in hip + /* TODO(@arya): setting this to Mode Clamp instead of Mode Border + * because it's unsupported in HIP. */ address_mode = hipAddressModeClamp; break; default: diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 009661b2dec..cb6c36d5ea6 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -226,7 +226,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features) pipeline_options.usesMotionBlur = false; pipeline_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING; - pipeline_options.numPayloadValues = 6; + pipeline_options.numPayloadValues = 8; pipeline_options.numAttributeValues = 2; /* u, v */ pipeline_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE; pipeline_options.pipelineLaunchParamsVariableName = "__params"; /* See globals.h */ diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp index 2a5f99f358b..28cdeeb630a 100644 --- a/intern/cycles/integrator/denoiser.cpp +++ b/intern/cycles/integrator/denoiser.cpp @@ -125,20 +125,41 @@ static Device *find_best_device(Device *device, DenoiserType type) return best_device; } +static DeviceInfo find_best_denoiser_device_info(const vector<DeviceInfo> &device_infos, + DenoiserType denoiser_type) +{ + for (const DeviceInfo &device_info : device_infos) { + if ((device_info.denoisers & denoiser_type) == 0) { + continue; + } + + /* TODO(sergey): Use one of the already configured devices, so that OptiX denoising can happen + * on a physical CUDA device which is already used for rendering. */ + + /* TODO(sergey): Choose fastest device for denoising. */ + + return device_info; + } + + DeviceInfo none_device; + none_device.type = DEVICE_NONE; + return none_device; +} + static unique_ptr<Device> create_denoiser_device(Device *path_trace_device, - const uint device_type_mask) + const uint device_type_mask, + DenoiserType denoiser_type) { const vector<DeviceInfo> device_infos = Device::available_devices(device_type_mask); if (device_infos.empty()) { return nullptr; } - /* TODO(sergey): Use one of the already configured devices, so that OptiX denoising can happen on - * a physical CUDA device which is already used for rendering. */ - - /* TODO(sergey): Choose fastest device for denoising. */ - - const DeviceInfo denoiser_device_info = device_infos.front(); + const DeviceInfo denoiser_device_info = find_best_denoiser_device_info(device_infos, + denoiser_type); + if (denoiser_device_info.type == DEVICE_NONE) { + return nullptr; + } unique_ptr<Device> denoiser_device( Device::create(denoiser_device_info, path_trace_device->stats, path_trace_device->profiler)); @@ -186,7 +207,8 @@ Device *Denoiser::ensure_denoiser_device(Progress *progress) device_creation_attempted_ = true; const uint device_type_mask = get_device_type_mask(); - local_denoiser_device_ = create_denoiser_device(path_trace_device_, device_type_mask); + local_denoiser_device_ = create_denoiser_device( + path_trace_device_, device_type_mask, params_.type); denoiser_device_ = local_denoiser_device_.get(); return denoiser_device_; diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index a08aec513fc..4676e69c4fb 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -37,8 +37,6 @@ OIDNDenoiser::OIDNDenoiser(Device *path_trace_device, const DenoiseParams ¶m : Denoiser(path_trace_device, params) { DCHECK_EQ(params.type, DENOISER_OPENIMAGEDENOISE); - - DCHECK(openimagedenoise_supported()) << "OpenImageDenoiser is not supported on this platform."; } #ifdef WITH_OPENIMAGEDENOISE @@ -585,6 +583,9 @@ bool OIDNDenoiser::denoise_buffer(const BufferParams &buffer_params, const int num_samples, bool allow_inplace_modification) { + DCHECK(openimagedenoise_supported()) + << "OpenImageDenoiser is not supported on this platform or build."; + #ifdef WITH_OPENIMAGEDENOISE thread_scoped_lock lock(mutex_); @@ -635,4 +636,20 @@ uint OIDNDenoiser::get_device_type_mask() const return DEVICE_MASK_CPU; } +Device *OIDNDenoiser::ensure_denoiser_device(Progress *progress) +{ +#ifndef WITH_OPENIMAGEDENOISE + path_trace_device_->set_error("Build without OpenImageDenoiser"); + return nullptr; +#else + if (!openimagedenoise_supported()) { + path_trace_device_->set_error( + "OpenImageDenoiser is not supported on this CPU: missing SSE 4.1 support"); + return nullptr; + } + + return Denoiser::ensure_denoiser_device(progress); +#endif +} + CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_oidn.h b/intern/cycles/integrator/denoiser_oidn.h index a0ec3e26b9c..2b815be973e 100644 --- a/intern/cycles/integrator/denoiser_oidn.h +++ b/intern/cycles/integrator/denoiser_oidn.h @@ -38,6 +38,7 @@ class OIDNDenoiser : public Denoiser { protected: virtual uint get_device_type_mask() const override; + virtual Device *ensure_denoiser_device(Progress *progress) override; /* We only perform one denoising at a time, since OpenImageDenoise itself is multithreaded. * Use this mutex whenever images are passed to the OIDN and needs to be denoised. */ diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 0b55d1078a8..fd697836f52 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -820,8 +820,15 @@ void PathTrace::tile_buffer_read() return; } + /* Read buffers back from device. */ + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { + path_trace_work->copy_render_buffers_from_device(); + }); + + /* Read (subset of) passes from output driver. */ PathTraceTile tile(*this); if (output_driver_->read_render_tile(tile)) { + /* Copy buffers to device again. */ tbb::parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) { path_trace_work->copy_render_buffers_to_device(); }); diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp index 95a1adeb016..0edd3810c39 100644 --- a/intern/cycles/integrator/shader_eval.cpp +++ b/intern/cycles/integrator/shader_eval.cpp @@ -157,7 +157,7 @@ bool ShaderEval::eval_gpu(Device *device, queue->init_execution(); /* Execute work on GPU in chunk, so we can cancel. - * TODO : query appropriate size from device.*/ + * TODO: query appropriate size from device. */ const int32_t chunk_size = 65536; device_ptr d_input = input.device_pointer; diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index 67804fb1d0d..1797bf60720 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -173,15 +173,16 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, uint p3 = 0; uint p4 = visibility; uint p5 = PRIMITIVE_NONE; + uint p6 = ((uint64_t)ray) & 0xFFFFFFFF; + uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF; uint ray_mask = visibility & 0xFF; - uint ray_flags = OPTIX_RAY_FLAG_NONE; + uint ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT; if (0 == ray_mask && (visibility & ~0xFF) != 0) { ray_mask = 0xFF; - ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT; } else if (visibility & PATH_RAY_SHADOW_OPAQUE) { - ray_flags = OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT; + ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT; } optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, @@ -200,7 +201,9 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, p2, p3, p4, - p5); + p5, + p6, + p7); isect->t = __uint_as_float(p0); isect->u = __uint_as_float(p1); @@ -242,6 +245,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, } MetalRTIntersectionPayload payload; + payload.self = ray->self; payload.u = 0.0f; payload.v = 0.0f; payload.visibility = visibility; @@ -309,6 +313,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_REGULAR); IntersectContext rtc_ctx(&ctx); RTCRayHit ray_hit; + ctx.ray = ray; kernel_embree_setup_rayhit(*ray, ray_hit, visibility); rtcIntersect1(kernel_data.bvh.scene, &rtc_ctx.context, &ray_hit); if (ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID && @@ -356,6 +361,9 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, uint p2 = pointer_pack_to_uint_0(local_isect); uint p3 = pointer_pack_to_uint_1(local_isect); uint p4 = local_object; + uint p6 = ((uint64_t)ray) & 0xFFFFFFFF; + uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF; + /* Is set to zero on miss or if ray is aborted, so can be used as return value. */ uint p5 = max_hits; @@ -379,7 +387,9 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, p2, p3, p4, - p5); + p5, + p6, + p7); return p5; # elif defined(__METALRT__) @@ -417,6 +427,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, } MetalRTIntersectionLocalPayload payload; + payload.self = ray->self; payload.local_object = local_object; payload.max_hits = max_hits; payload.local_isect.num_hits = 0; @@ -460,6 +471,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, kg, has_bvh ? CCLIntersectContext::RAY_SSS : CCLIntersectContext::RAY_LOCAL); ctx.lcg_state = lcg_state; ctx.max_hits = max_hits; + ctx.ray = ray; ctx.local_isect = local_isect; if (local_isect) { local_isect->num_hits = 0; @@ -532,6 +544,8 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, uint p3 = max_hits; uint p4 = visibility; uint p5 = false; + uint p6 = ((uint64_t)ray) & 0xFFFFFFFF; + uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF; uint ray_mask = visibility & 0xFF; if (0 == ray_mask && (visibility & ~0xFF) != 0) { @@ -555,7 +569,9 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, p2, p3, p4, - p5); + p5, + p6, + p7); *num_recorded_hits = uint16_unpack_from_uint_0(p2); *throughput = __uint_as_float(p1); @@ -588,6 +604,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, } MetalRTIntersectionShadowPayload payload; + payload.self = ray->self; payload.visibility = visibility; payload.max_hits = max_hits; payload.num_hits = 0; @@ -634,6 +651,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, Intersection *isect_array = (Intersection *)state->shadow_isect; ctx.isect_s = isect_array; ctx.max_hits = max_hits; + ctx.ray = ray; IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; kernel_embree_setup_ray(*ray, rtc_ray, visibility); @@ -685,6 +703,8 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, uint p3 = 0; uint p4 = visibility; uint p5 = PRIMITIVE_NONE; + uint p6 = ((uint64_t)ray) & 0xFFFFFFFF; + uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF; uint ray_mask = visibility & 0xFF; if (0 == ray_mask && (visibility & ~0xFF) != 0) { @@ -708,7 +728,9 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, p2, p3, p4, - p5); + p5, + p6, + p7); isect->t = __uint_as_float(p0); isect->u = __uint_as_float(p1); @@ -744,6 +766,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, } MetalRTIntersectionPayload payload; + payload.self = ray->self; payload.visibility = visibility; typename metalrt_intersector_type::result_type intersection; @@ -820,6 +843,7 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg, ctx.isect_s = isect; ctx.max_hits = max_hits; ctx.num_hits = 0; + ctx.ray = ray; IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; kernel_embree_setup_ray(*ray, rtc_ray, visibility); diff --git a/intern/cycles/kernel/bvh/embree.h b/intern/cycles/kernel/bvh/embree.h index 9edd4f90a7e..19c4b9f6f3d 100644 --- a/intern/cycles/kernel/bvh/embree.h +++ b/intern/cycles/kernel/bvh/embree.h @@ -22,6 +22,8 @@ #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" +#include "kernel/bvh/util.h" + #include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -38,6 +40,9 @@ struct CCLIntersectContext { KernelGlobals kg; RayType type; + /* For avoiding self intersections */ + const Ray *ray; + /* for shadow rays */ Intersection *isect_s; uint max_hits; @@ -56,6 +61,7 @@ struct CCLIntersectContext { { kg = kg_; type = type_; + ray = NULL; max_hits = 1; num_hits = 0; num_recorded_hits = 0; @@ -102,7 +108,34 @@ ccl_device_inline void kernel_embree_setup_rayhit(const Ray &ray, { kernel_embree_setup_ray(ray, rayhit.ray, visibility); rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; - rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID; + rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; +} + +ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg, + const RTCHit *hit, + const Ray *ray) +{ + bool status = false; + if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) { + const int oID = hit->instID[0] / 2; + if ((ray->self.object == oID) || (ray->self.light_object == oID)) { + RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( + rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); + const int pID = hit->primID + + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); + status = intersection_skip_self_shadow(ray->self, oID, pID); + } + } + else { + const int oID = hit->geomID / 2; + if ((ray->self.object == oID) || (ray->self.light_object == oID)) { + const int pID = hit->primID + (intptr_t)rtcGetGeometryUserData( + rtcGetGeometry(kernel_data.bvh.scene, hit->geomID)); + status = intersection_skip_self_shadow(ray->self, oID, pID); + } + } + + return status; } ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg, diff --git a/intern/cycles/kernel/bvh/local.h b/intern/cycles/kernel/bvh/local.h index 4d0e6aac901..4ef6deef98d 100644 --- a/intern/cycles/kernel/bvh/local.h +++ b/intern/cycles/kernel/bvh/local.h @@ -157,7 +157,11 @@ ccl_device_inline } } + /* Skip self intersection. */ const int prim = kernel_tex_fetch(__prim_index, prim_addr); + if (intersection_skip_self_local(ray->self, prim)) { + continue; + } if (triangle_intersect_local(kg, local_isect, @@ -188,7 +192,11 @@ ccl_device_inline } } + /* Skip self intersection. */ const int prim = kernel_tex_fetch(__prim_index, prim_addr); + if (intersection_skip_self_local(ray->self, prim)) { + continue; + } if (motion_triangle_intersect_local(kg, local_isect, diff --git a/intern/cycles/kernel/bvh/metal.h b/intern/cycles/kernel/bvh/metal.h index 55456d15f50..5ab413d9314 100644 --- a/intern/cycles/kernel/bvh/metal.h +++ b/intern/cycles/kernel/bvh/metal.h @@ -15,6 +15,7 @@ */ struct MetalRTIntersectionPayload { + RaySelfPrimitives self; uint visibility; float u, v; int prim; @@ -25,6 +26,7 @@ struct MetalRTIntersectionPayload { }; struct MetalRTIntersectionLocalPayload { + RaySelfPrimitives self; uint local_object; uint lcg_state; short max_hits; @@ -34,6 +36,7 @@ struct MetalRTIntersectionLocalPayload { }; struct MetalRTIntersectionShadowPayload { + RaySelfPrimitives self; uint visibility; #if defined(__METALRT_MOTION__) float time; diff --git a/intern/cycles/kernel/bvh/shadow_all.h b/intern/cycles/kernel/bvh/shadow_all.h index 0fb86bfda77..59a7ba63045 100644 --- a/intern/cycles/kernel/bvh/shadow_all.h +++ b/intern/cycles/kernel/bvh/shadow_all.h @@ -160,6 +160,9 @@ ccl_device_inline kernel_tex_fetch(__prim_object, prim_addr) : object; const int prim = kernel_tex_fetch(__prim_index, prim_addr); + if (intersection_skip_self_shadow(ray->self, prim_object, prim)) { + continue; + } switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { diff --git a/intern/cycles/kernel/bvh/traversal.h b/intern/cycles/kernel/bvh/traversal.h index dc2d1422df6..17cd357a069 100644 --- a/intern/cycles/kernel/bvh/traversal.h +++ b/intern/cycles/kernel/bvh/traversal.h @@ -133,35 +133,29 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, --stack_ptr; /* primitive intersection */ - switch (type & PRIMITIVE_ALL) { - case PRIMITIVE_TRIANGLE: { - for (; prim_addr < prim_addr2; prim_addr++) { - kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); - - const int prim_object = (object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, prim_addr) : - object; - const int prim = kernel_tex_fetch(__prim_index, prim_addr); + for (; prim_addr < prim_addr2; prim_addr++) { + kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); + + const int prim_object = (object == OBJECT_NONE) ? + kernel_tex_fetch(__prim_object, prim_addr) : + object; + const int prim = kernel_tex_fetch(__prim_index, prim_addr); + if (intersection_skip_self_shadow(ray->self, prim_object, prim)) { + continue; + } + switch (type & PRIMITIVE_ALL) { + case PRIMITIVE_TRIANGLE: { if (triangle_intersect( kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr)) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; } + break; } - break; - } #if BVH_FEATURE(BVH_MOTION) - case PRIMITIVE_MOTION_TRIANGLE: { - for (; prim_addr < prim_addr2; prim_addr++) { - kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); - - const int prim_object = (object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, prim_addr) : - object; - const int prim = kernel_tex_fetch(__prim_index, prim_addr); - + case PRIMITIVE_MOTION_TRIANGLE: { if (motion_triangle_intersect(kg, isect, P, @@ -176,28 +170,21 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; } + break; } - break; - } #endif /* BVH_FEATURE(BVH_MOTION) */ #if BVH_FEATURE(BVH_HAIR) - case PRIMITIVE_CURVE_THICK: - case PRIMITIVE_MOTION_CURVE_THICK: - case PRIMITIVE_CURVE_RIBBON: - case PRIMITIVE_MOTION_CURVE_RIBBON: { - for (; prim_addr < prim_addr2; prim_addr++) { + case PRIMITIVE_CURVE_THICK: + case PRIMITIVE_MOTION_CURVE_THICK: + case PRIMITIVE_CURVE_RIBBON: + case PRIMITIVE_MOTION_CURVE_RIBBON: { if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) { const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr); if (ray->time < prim_time.x || ray->time > prim_time.y) { - continue; + break; } } - const int prim_object = (object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, prim_addr) : - object; - const int prim = kernel_tex_fetch(__prim_index, prim_addr); - const int curve_type = kernel_tex_fetch(__prim_type, prim_addr); const bool hit = curve_intersect( kg, isect, P, dir, isect->t, prim_object, prim, ray->time, curve_type); @@ -206,26 +193,19 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; } + break; } - break; - } #endif /* BVH_FEATURE(BVH_HAIR) */ #if BVH_FEATURE(BVH_POINTCLOUD) - case PRIMITIVE_POINT: - case PRIMITIVE_MOTION_POINT: { - for (; prim_addr < prim_addr2; prim_addr++) { + case PRIMITIVE_POINT: + case PRIMITIVE_MOTION_POINT: { if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) { const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr); if (ray->time < prim_time.x || ray->time > prim_time.y) { - continue; + break; } } - const int prim_object = (object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, prim_addr) : - object; - const int prim = kernel_tex_fetch(__prim_index, prim_addr); - const int point_type = kernel_tex_fetch(__prim_type, prim_addr); const bool hit = point_intersect( kg, isect, P, dir, isect->t, prim_object, prim, ray->time, point_type); @@ -234,10 +214,10 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; } + break; } - break; - } #endif /* BVH_FEATURE(BVH_POINTCLOUD) */ + } } } else { diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h index bd79c6e19c6..39c3ecd78c0 100644 --- a/intern/cycles/kernel/bvh/util.h +++ b/intern/cycles/kernel/bvh/util.h @@ -21,54 +21,22 @@ CCL_NAMESPACE_BEGIN /* Ray offset to avoid self intersection. * * This function should be used to compute a modified ray start position for - * rays leaving from a surface. */ - + * rays leaving from a surface. This is from "A Fast and Robust Method for Avoiding + * Self-Intersection" see https://research.nvidia.com/publication/2019-03_A-Fast-and + */ ccl_device_inline float3 ray_offset(float3 P, float3 Ng) { -#ifdef __INTERSECTION_REFINE__ - const float epsilon_f = 1e-5f; - /* ideally this should match epsilon_f, but instancing and motion blur - * precision makes it problematic */ - const float epsilon_test = 1.0f; - const int epsilon_i = 32; - - float3 res; - - /* x component */ - if (fabsf(P.x) < epsilon_test) { - res.x = P.x + Ng.x * epsilon_f; - } - else { - uint ix = __float_as_uint(P.x); - ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i; - res.x = __uint_as_float(ix); - } - - /* y component */ - if (fabsf(P.y) < epsilon_test) { - res.y = P.y + Ng.y * epsilon_f; - } - else { - uint iy = __float_as_uint(P.y); - iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i; - res.y = __uint_as_float(iy); - } - - /* z component */ - if (fabsf(P.z) < epsilon_test) { - res.z = P.z + Ng.z * epsilon_f; - } - else { - uint iz = __float_as_uint(P.z); - iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i; - res.z = __uint_as_float(iz); - } - - return res; -#else - const float epsilon_f = 1e-4f; - return P + epsilon_f * Ng; -#endif + const float int_scale = 256.0f; + int3 of_i = make_int3((int)(int_scale * Ng.x), (int)(int_scale * Ng.y), (int)(int_scale * Ng.z)); + + float3 p_i = make_float3(__int_as_float(__float_as_int(P.x) + ((P.x < 0) ? -of_i.x : of_i.x)), + __int_as_float(__float_as_int(P.y) + ((P.y < 0) ? -of_i.y : of_i.y)), + __int_as_float(__float_as_int(P.z) + ((P.z < 0) ? -of_i.z : of_i.z))); + const float origin = 1.0f / 32.0f; + const float float_scale = 1.0f / 65536.0f; + return make_float3(fabsf(P.x) < origin ? P.x + float_scale * Ng.x : p_i.x, + fabsf(P.y) < origin ? P.y + float_scale * Ng.y : p_i.y, + fabsf(P.z) < origin ? P.z + float_scale * Ng.z : p_i.z); } #if defined(__KERNEL_CPU__) @@ -227,4 +195,25 @@ ccl_device_inline float intersection_curve_shadow_transparency(KernelGlobals kg, return (1.0f - u) * f0 + u * f1; } +ccl_device_inline bool intersection_skip_self(ccl_private const RaySelfPrimitives &self, + const int object, + const int prim) +{ + return (self.prim == prim) && (self.object == object); +} + +ccl_device_inline bool intersection_skip_self_shadow(ccl_private const RaySelfPrimitives &self, + const int object, + const int prim) +{ + return ((self.prim == prim) && (self.object == object)) || + ((self.light_prim == prim) && (self.light_object == object)); +} + +ccl_device_inline bool intersection_skip_self_local(ccl_private const RaySelfPrimitives &self, + const int prim) +{ + return (self.prim == prim); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/volume.h b/intern/cycles/kernel/bvh/volume.h index c0746c8efc3..95bba4f071d 100644 --- a/intern/cycles/kernel/bvh/volume.h +++ b/intern/cycles/kernel/bvh/volume.h @@ -144,6 +144,9 @@ ccl_device_inline kernel_tex_fetch(__prim_object, prim_addr) : object; const int prim = kernel_tex_fetch(__prim_index, prim_addr); + if (intersection_skip_self(ray->self, prim_object, prim)) { + continue; + } int object_flag = kernel_tex_fetch(__object_flag, prim_object); if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { @@ -164,6 +167,9 @@ ccl_device_inline kernel_tex_fetch(__prim_object, prim_addr) : object; const int prim = kernel_tex_fetch(__prim_index, prim_addr); + if (intersection_skip_self(ray->self, prim_object, prim)) { + continue; + } int object_flag = kernel_tex_fetch(__object_flag, prim_object); if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; diff --git a/intern/cycles/kernel/bvh/volume_all.h b/intern/cycles/kernel/bvh/volume_all.h index a88c9d95d46..9f53e987cf1 100644 --- a/intern/cycles/kernel/bvh/volume_all.h +++ b/intern/cycles/kernel/bvh/volume_all.h @@ -147,6 +147,9 @@ ccl_device_inline kernel_tex_fetch(__prim_object, prim_addr) : object; const int prim = kernel_tex_fetch(__prim_index, prim_addr); + if (intersection_skip_self(ray->self, prim_object, prim)) { + continue; + } int object_flag = kernel_tex_fetch(__object_flag, prim_object); if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; @@ -188,6 +191,9 @@ ccl_device_inline kernel_tex_fetch(__prim_object, prim_addr) : object; const int prim = kernel_tex_fetch(__prim_index, prim_addr); + if (intersection_skip_self(ray->self, prim_object, prim)) { + continue; + } int object_flag = kernel_tex_fetch(__object_flag, prim_object); if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; diff --git a/intern/cycles/kernel/device/metal/kernel.metal b/intern/cycles/kernel/device/metal/kernel.metal index 3303b541487..6b77940660f 100644 --- a/intern/cycles/kernel/device/metal/kernel.metal +++ b/intern/cycles/kernel/device/metal/kernel.metal @@ -40,6 +40,27 @@ struct TriangleIntersectionResult enum { METALRT_HIT_TRIANGLE, METALRT_HIT_BOUNDING_BOX }; +ccl_device_inline bool intersection_skip_self(ray_data const RaySelfPrimitives& self, + const int object, + const int prim) +{ + return (self.prim == prim) && (self.object == object); +} + +ccl_device_inline bool intersection_skip_self_shadow(ray_data const RaySelfPrimitives& self, + const int object, + const int prim) +{ + return ((self.prim == prim) && (self.object == object)) || + ((self.light_prim == prim) && (self.light_object == object)); +} + +ccl_device_inline bool intersection_skip_self_local(ray_data const RaySelfPrimitives& self, + const int prim) +{ + return (self.prim == prim); +} + template<typename TReturn, uint intersection_type> TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal, ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload, @@ -53,8 +74,8 @@ TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal, #ifdef __BVH_LOCAL__ uint prim = primitive_id + kernel_tex_fetch(__object_prim_offset, object); - if (object != payload.local_object) { - /* Only intersect with matching object */ + if ((object != payload.local_object) || intersection_skip_self_local(payload.self, prim)) { + /* Only intersect with matching object and skip self-intersecton. */ result.accept = false; result.continue_search = true; return result; @@ -166,6 +187,11 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal, } # endif + if (intersection_skip_self_shadow(payload.self, object, prim)) { + /* continue search */ + return true; + } + float u = 0.0f, v = 0.0f; int type = 0; if (intersection_type == METALRT_HIT_TRIANGLE) { @@ -322,21 +348,35 @@ inline TReturnType metalrt_visibility_test(constant KernelParamsMetal &launch_pa } # endif -# ifdef __VISIBILITY_FLAG__ uint visibility = payload.visibility; +# ifdef __VISIBILITY_FLAG__ if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { result.accept = false; result.continue_search = true; return result; } +# endif /* Shadow ray early termination. */ if (visibility & PATH_RAY_SHADOW_OPAQUE) { - result.accept = true; - result.continue_search = false; - return result; + if (intersection_skip_self_shadow(payload.self, object, prim)) { + result.accept = false; + result.continue_search = true; + return result; + } + else { + result.accept = true; + result.continue_search = false; + return result; + } + } + else { + if (intersection_skip_self(payload.self, object, prim)) { + result.accept = false; + result.continue_search = true; + return result; + } } -# endif result.accept = true; result.continue_search = true; diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index aa210b31a95..8e3d57bff8a 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -45,6 +45,11 @@ template<typename T> ccl_device_forceinline T *get_payload_ptr_2() return pointer_unpack_from_uint<T>(optixGetPayload_2(), optixGetPayload_3()); } +template<typename T> ccl_device_forceinline T *get_payload_ptr_6() +{ + return (T *)(((uint64_t)optixGetPayload_7() << 32) | optixGetPayload_6()); +} + ccl_device_forceinline int get_object_id() { #ifdef __OBJECT_MOTION__ @@ -111,6 +116,12 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() return optixIgnoreIntersection(); } + const int prim = optixGetPrimitiveIndex(); + ccl_private Ray *const ray = get_payload_ptr_6<Ray>(); + if (intersection_skip_self_local(ray->self, prim)) { + return optixIgnoreIntersection(); + } + const uint max_hits = optixGetPayload_5(); if (max_hits == 0) { /* Special case for when no hit information is requested, just report that something was hit */ @@ -149,8 +160,6 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() local_isect->num_hits = 1; } - const int prim = optixGetPrimitiveIndex(); - Intersection *isect = &local_isect->hits[hit]; isect->t = optixGetRayTmax(); isect->prim = prim; @@ -185,6 +194,11 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() } # endif + ccl_private Ray *const ray = get_payload_ptr_6<Ray>(); + if (intersection_skip_self_shadow(ray->self, object, prim)) { + return optixIgnoreIntersection(); + } + float u = 0.0f, v = 0.0f; int type = 0; if (optixIsTriangleHit()) { @@ -314,6 +328,12 @@ extern "C" __global__ void __anyhit__kernel_optix_volume_test() if ((kernel_tex_fetch(__object_flag, object) & SD_OBJECT_HAS_VOLUME) == 0) { return optixIgnoreIntersection(); } + + const int prim = optixGetPrimitiveIndex(); + ccl_private Ray *const ray = get_payload_ptr_6<Ray>(); + if (intersection_skip_self(ray->self, object, prim)) { + return optixIgnoreIntersection(); + } } extern "C" __global__ void __anyhit__kernel_optix_visibility_test() @@ -330,18 +350,31 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test() # endif #endif -#ifdef __VISIBILITY_FLAG__ const uint object = get_object_id(); const uint visibility = optixGetPayload_4(); +#ifdef __VISIBILITY_FLAG__ if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { return optixIgnoreIntersection(); } +#endif + + const int prim = optixGetPrimitiveIndex(); + ccl_private Ray *const ray = get_payload_ptr_6<Ray>(); - /* Shadow ray early termination. */ if (visibility & PATH_RAY_SHADOW_OPAQUE) { - return optixTerminateRay(); + if (intersection_skip_self_shadow(ray->self, object, prim)) { + return optixIgnoreIntersection(); + } + else { + /* Shadow ray early termination. */ + return optixTerminateRay(); + } + } + else { + if (intersection_skip_self(ray->self, object, prim)) { + return optixIgnoreIntersection(); + } } -#endif } extern "C" __global__ void __closesthit__kernel_optix_hit() diff --git a/intern/cycles/kernel/geom/curve.h b/intern/cycles/kernel/geom/curve.h index 8a63f01643b..48ee8226e89 100644 --- a/intern/cycles/kernel/geom/curve.h +++ b/intern/cycles/kernel/geom/curve.h @@ -226,6 +226,18 @@ ccl_device float curve_thickness(KernelGlobals kg, ccl_private const ShaderData return r * 2.0f; } +/* Curve random */ + +ccl_device float curve_random(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + if (sd->type & PRIMITIVE_CURVE) { + const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_CURVE_RANDOM); + return (desc.offset != ATTR_STD_NOT_FOUND) ? curve_attribute_float(kg, sd, desc, NULL, NULL) : + 0.0f; + } + return 0.0f; +} + /* Curve location for motion pass, linear interpolation between keys and * ignoring radius because we do the same for the motion keys */ diff --git a/intern/cycles/kernel/geom/motion_triangle_intersect.h b/intern/cycles/kernel/geom/motion_triangle_intersect.h index cb6d210d90f..a11cb88385b 100644 --- a/intern/cycles/kernel/geom/motion_triangle_intersect.h +++ b/intern/cycles/kernel/geom/motion_triangle_intersect.h @@ -29,46 +29,19 @@ CCL_NAMESPACE_BEGIN -/* Refine triangle intersection to more precise hit point. For rays that travel - * far the precision is often not so good, this reintersects the primitive from - * a closer distance. +/** + * Use the barycentric coordinates to get the intersection location */ - -ccl_device_inline float3 motion_triangle_refine(KernelGlobals kg, - ccl_private ShaderData *sd, - float3 P, - float3 D, - float t, - const int isect_object, - const int isect_prim, - float3 verts[3]) +ccl_device_inline float3 motion_triangle_point_from_uv(KernelGlobals kg, + ccl_private ShaderData *sd, + const int isect_object, + const int isect_prim, + const float u, + const float v, + float3 verts[3]) { -#ifdef __INTERSECTION_REFINE__ - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - if (UNLIKELY(t == 0.0f)) { - return P; - } - const Transform tfm = object_get_inverse_transform(kg, sd); - - P = transform_point(&tfm, P); - D = transform_direction(&tfm, D * t); - D = normalize_len(D, &t); - } - - P = P + D * t; - - /* Compute refined intersection distance. */ - const float3 e1 = verts[0] - verts[2]; - const float3 e2 = verts[1] - verts[2]; - const float3 s1 = cross(D, e2); - - const float invdivisor = 1.0f / dot(s1, e1); - const float3 d = P - verts[2]; - const float3 s2 = cross(d, e1); - float rt = dot(e2, s2) * invdivisor; - - /* Compute refined position. */ - P = P + D * rt; + float w = 1.0f - u - v; + float3 P = u * verts[0] + v * verts[1] + w * verts[2]; if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_transform(kg, sd); @@ -76,71 +49,8 @@ ccl_device_inline float3 motion_triangle_refine(KernelGlobals kg, } return P; -#else - return P + D * t; -#endif } -/* Same as above, except that t is assumed to be in object space - * for instancing. - */ - -#ifdef __BVH_LOCAL__ -# if defined(__KERNEL_CUDA__) && (defined(i386) || defined(_M_IX86)) -ccl_device_noinline -# else -ccl_device_inline -# endif - float3 - motion_triangle_refine_local(KernelGlobals kg, - ccl_private ShaderData *sd, - float3 P, - float3 D, - float t, - const int isect_object, - const int isect_prim, - float3 verts[3]) -{ -# if defined(__KERNEL_GPU_RAYTRACING__) - /* t is always in world space with OptiX and MetalRT. */ - return motion_triangle_refine(kg, sd, P, D, t, isect_object, isect_prim, verts); -# else -# ifdef __INTERSECTION_REFINE__ - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - const Transform tfm = object_get_inverse_transform(kg, sd); - - P = transform_point(&tfm, P); - D = transform_direction(&tfm, D); - D = normalize(D); - } - - P = P + D * t; - - /* compute refined intersection distance */ - const float3 e1 = verts[0] - verts[2]; - const float3 e2 = verts[1] - verts[2]; - const float3 s1 = cross(D, e2); - - const float invdivisor = 1.0f / dot(s1, e1); - const float3 d = P - verts[2]; - const float3 s2 = cross(d, e1); - float rt = dot(e2, s2) * invdivisor; - - P = P + D * rt; - - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - const Transform tfm = object_get_transform(kg, sd); - P = transform_point(&tfm, P); - } - - return P; -# else /* __INTERSECTION_REFINE__ */ - return P + D * t; -# endif /* __INTERSECTION_REFINE__ */ -# endif -} -#endif /* __BVH_LOCAL__ */ - /* Ray intersection. We simply compute the vertex positions at the given ray * time and do a ray intersection with the resulting triangle. */ diff --git a/intern/cycles/kernel/geom/motion_triangle_shader.h b/intern/cycles/kernel/geom/motion_triangle_shader.h index fc7c181882e..15730c83969 100644 --- a/intern/cycles/kernel/geom/motion_triangle_shader.h +++ b/intern/cycles/kernel/geom/motion_triangle_shader.h @@ -68,15 +68,7 @@ ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg, verts[1] = (1.0f - t) * verts[1] + t * next_verts[1]; verts[2] = (1.0f - t) * verts[2] + t * next_verts[2]; /* Compute refined position. */ -#ifdef __BVH_LOCAL__ - if (is_local) { - sd->P = motion_triangle_refine_local(kg, sd, P, D, ray_t, isect_object, isect_prim, verts); - } - else -#endif /* __BVH_LOCAL__*/ - { - sd->P = motion_triangle_refine(kg, sd, P, D, ray_t, isect_object, isect_prim, verts); - } + sd->P = motion_triangle_point_from_uv(kg, sd, isect_object, isect_prim, sd->u, sd->v, verts); /* Compute face normal. */ float3 Ng; if (sd->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { diff --git a/intern/cycles/kernel/geom/point.h b/intern/cycles/kernel/geom/point.h index 23764d49095..545b5c7fa43 100644 --- a/intern/cycles/kernel/geom/point.h +++ b/intern/cycles/kernel/geom/point.h @@ -109,17 +109,59 @@ ccl_device float4 point_attribute_float4(KernelGlobals kg, } } +/* Point position */ + +ccl_device float3 point_position(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + if (sd->type & PRIMITIVE_POINT) { + /* World space center. */ + float3 P = (sd->type & PRIMITIVE_MOTION) ? + float4_to_float3(motion_point(kg, sd->object, sd->prim, sd->time)) : + float4_to_float3(kernel_tex_fetch(__points, sd->prim)); + + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_position_transform(kg, sd, &P); + } + + return P; + } + + return zero_float3(); +} + /* Point radius */ ccl_device float point_radius(KernelGlobals kg, ccl_private const ShaderData *sd) { if (sd->type & PRIMITIVE_POINT) { - return kernel_tex_fetch(__points, sd->prim).w; + /* World space radius. */ + const float r = kernel_tex_fetch(__points, sd->prim).w; + + if (sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED) { + return r; + } + else { + float3 dir = make_float3(r, r, r); + object_dir_transform(kg, sd, &dir); + return average(dir); + } } return 0.0f; } +/* Point random */ + +ccl_device float point_random(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + if (sd->type & PRIMITIVE_POINT) { + const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POINT_RANDOM); + return (desc.offset != ATTR_STD_NOT_FOUND) ? point_attribute_float(kg, sd, desc, NULL, NULL) : + 0.0f; + } + return 0.0f; +} + /* Point location for motion pass, linear interpolation between keys and * ignoring radius because we do the same for the motion keys */ diff --git a/intern/cycles/kernel/geom/shader_data.h b/intern/cycles/kernel/geom/shader_data.h index f5055d8b285..fdf914d85e0 100644 --- a/intern/cycles/kernel/geom/shader_data.h +++ b/intern/cycles/kernel/geom/shader_data.h @@ -89,7 +89,7 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg, sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); /* vectors */ - sd->P = triangle_refine(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim); + sd->P = triangle_point_from_uv(kg, sd, isect->object, isect->prim, isect->u, isect->v); sd->Ng = Ng; sd->N = Ng; @@ -190,40 +190,46 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg, #ifdef __OBJECT_MOTION__ shader_setup_object_transforms(kg, sd, time); #endif - } - else if (lamp != LAMP_NONE) { - sd->lamp = lamp; - } - /* transform into world space */ - if (object_space) { - object_position_transform_auto(kg, sd, &sd->P); - object_normal_transform_auto(kg, sd, &sd->Ng); - sd->N = sd->Ng; - object_dir_transform_auto(kg, sd, &sd->I); - } + /* transform into world space */ + if (object_space) { + object_position_transform_auto(kg, sd, &sd->P); + object_normal_transform_auto(kg, sd, &sd->Ng); + sd->N = sd->Ng; + object_dir_transform_auto(kg, sd, &sd->I); + } - if (sd->type == PRIMITIVE_TRIANGLE) { - /* smooth normal */ - if (sd->shader & SHADER_SMOOTH_NORMAL) { - sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); + if (sd->type == PRIMITIVE_TRIANGLE) { + /* smooth normal */ + if (sd->shader & SHADER_SMOOTH_NORMAL) { + sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_normal_transform_auto(kg, sd, &sd->N); + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_normal_transform_auto(kg, sd, &sd->N); + } } - } - /* dPdu/dPdv */ + /* dPdu/dPdv */ #ifdef __DPDU__ - triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); + triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_dir_transform_auto(kg, sd, &sd->dPdu); - object_dir_transform_auto(kg, sd, &sd->dPdv); + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_dir_transform_auto(kg, sd, &sd->dPdu); + object_dir_transform_auto(kg, sd, &sd->dPdv); + } +#endif } + else { +#ifdef __DPDU__ + sd->dPdu = zero_float3(); + sd->dPdv = zero_float3(); #endif + } } else { + if (lamp != LAMP_NONE) { + sd->lamp = lamp; + } #ifdef __DPDU__ sd->dPdu = zero_float3(); sd->dPdv = zero_float3(); diff --git a/intern/cycles/kernel/geom/triangle_intersect.h b/intern/cycles/kernel/geom/triangle_intersect.h index 0169b40bc34..8458cf020a0 100644 --- a/intern/cycles/kernel/geom/triangle_intersect.h +++ b/intern/cycles/kernel/geom/triangle_intersect.h @@ -142,116 +142,23 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg, } #endif /* __BVH_LOCAL__ */ -/* Refine triangle intersection to more precise hit point. For rays that travel - * far the precision is often not so good, this reintersects the primitive from - * a closer distance. */ - -/* Reintersections uses the paper: - * - * Tomas Moeller - * Fast, minimum storage ray/triangle intersection - * http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf +/** + * Use the barycentric coordinates to get the intersection location */ - -ccl_device_inline float3 triangle_refine(KernelGlobals kg, - ccl_private ShaderData *sd, - float3 P, - float3 D, - float t, - const int isect_object, - const int isect_prim) +ccl_device_inline float3 triangle_point_from_uv(KernelGlobals kg, + ccl_private ShaderData *sd, + const int isect_object, + const int isect_prim, + const float u, + const float v) { -#ifdef __INTERSECTION_REFINE__ - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - if (UNLIKELY(t == 0.0f)) { - return P; - } - const Transform tfm = object_get_inverse_transform(kg, sd); - - P = transform_point(&tfm, P); - D = transform_direction(&tfm, D * t); - D = normalize_len(D, &t); - } - - P = P + D * t; - const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w; const packed_float3 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0), tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1), tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2); - float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z); - float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z); - float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z); - float3 qvec = cross(tvec, edge1); - float3 pvec = cross(D, edge2); - float det = dot(edge1, pvec); - if (det != 0.0f) { - /* If determinant is zero it means ray lies in the plane of - * the triangle. It is possible in theory due to watertight - * nature of triangle intersection. For such cases we simply - * don't refine intersection hoping it'll go all fine. - */ - float rt = dot(edge2, qvec) / det; - P = P + D * rt; - } - - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - const Transform tfm = object_get_transform(kg, sd); - P = transform_point(&tfm, P); - } - - return P; -#else - return P + D * t; -#endif -} - -/* Same as above, except that t is assumed to be in object space for - * instancing. - */ -ccl_device_inline float3 triangle_refine_local(KernelGlobals kg, - ccl_private ShaderData *sd, - float3 P, - float3 D, - float t, - const int isect_object, - const int isect_prim) -{ -#if defined(__KERNEL_GPU_RAYTRACING__) - /* t is always in world space with OptiX and MetalRT. */ - return triangle_refine(kg, sd, P, D, t, isect_object, isect_prim); -#else - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - const Transform tfm = object_get_inverse_transform(kg, sd); - - P = transform_point(&tfm, P); - D = transform_direction(&tfm, D); - D = normalize(D); - } + float w = 1.0f - u - v; - P = P + D * t; - -# ifdef __INTERSECTION_REFINE__ - const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w; - const packed_float3 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0), - tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1), - tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2); - float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z); - float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z); - float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z); - float3 qvec = cross(tvec, edge1); - float3 pvec = cross(D, edge2); - float det = dot(edge1, pvec); - if (det != 0.0f) { - /* If determinant is zero it means ray lies in the plane of - * the triangle. It is possible in theory due to watertight - * nature of triangle intersection. For such cases we simply - * don't refine intersection hoping it'll go all fine. - */ - float rt = dot(edge2, qvec) / det; - P = P + D * rt; - } -# endif /* __INTERSECTION_REFINE__ */ + float3 P = u * tri_a + v * tri_b + w * tri_c; if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_transform(kg, sd); @@ -259,7 +166,6 @@ ccl_device_inline float3 triangle_refine_local(KernelGlobals kg, } return P; -#endif } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index df710dc1d82..4c5265189fa 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -328,6 +328,12 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, /* Scene Intersection. */ Intersection isect ccl_optional_struct_init; + isect.object = OBJECT_NONE; + isect.prim = PRIM_NONE; + ray.self.object = last_isect_object; + ray.self.prim = last_isect_prim; + ray.self.light_object = OBJECT_NONE; + ray.self.light_prim = PRIM_NONE; bool hit = scene_intersect(kg, &ray, visibility, &isect); /* TODO: remove this and do it in the various intersection functions instead. */ diff --git a/intern/cycles/kernel/integrator/intersect_shadow.h b/intern/cycles/kernel/integrator/intersect_shadow.h index 90422445fad..1ba8724826b 100644 --- a/intern/cycles/kernel/integrator/intersect_shadow.h +++ b/intern/cycles/kernel/integrator/intersect_shadow.h @@ -156,7 +156,10 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt /* Read ray from integrator state into local memory. */ Ray ray ccl_optional_struct_init; integrator_state_read_shadow_ray(kg, state, &ray); - + ray.self.object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, object); + ray.self.prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, prim); + ray.self.light_object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, object); + ray.self.light_prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, prim); /* Compute visibility. */ const uint visibility = integrate_intersect_shadow_visibility(kg, state); diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h index 9fa5ff63ad2..ee3d82ebacb 100644 --- a/intern/cycles/kernel/integrator/intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -38,7 +38,10 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, Ray volume_ray ccl_optional_struct_init; volume_ray.P = from_P; volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t); - + volume_ray.self.object = INTEGRATOR_STATE(state, isect, object); + volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim); + volume_ray.self.light_object = OBJECT_NONE; + volume_ray.self.light_prim = PRIM_NONE; /* Store to avoid global fetches on every intersection step. */ const uint volume_stack_size = kernel_data.volume_stack_size; @@ -68,7 +71,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, volume_stack_enter_exit(kg, state, stack_sd); /* Move ray forward. */ - volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); + volume_ray.P = stack_sd->P; if (volume_ray.t != FLT_MAX) { volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t); } @@ -91,6 +94,10 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s * fewest hits. */ volume_ray.D = make_float3(0.0f, 0.0f, 1.0f); volume_ray.t = FLT_MAX; + volume_ray.self.object = OBJECT_NONE; + volume_ray.self.prim = PRIM_NONE; + volume_ray.self.light_object = OBJECT_NONE; + volume_ray.self.light_prim = PRIM_NONE; int stack_index = 0, enclosed_index = 0; @@ -203,7 +210,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s } /* Move ray forward. */ - volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); + volume_ray.P = stack_sd->P; ++step; } #endif diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index 97ca430752c..0a82c9cadef 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -37,8 +37,9 @@ ccl_device_inline void integrate_light(KernelGlobals kg, /* Advance ray beyond light. */ /* TODO: can we make this more numerically robust to avoid reintersecting the - * same light in some cases? */ - const float3 new_ray_P = ray_offset(ray_P + ray_D * isect.t, ray_D); + * same light in some cases? Ray should not intersect surface anymore as the + * object and prim ids will prevent self intersection. */ + const float3 new_ray_P = ray_P + ray_D * isect.t; INTEGRATOR_STATE_WRITE(state, ray, P) = new_ray_P; INTEGRATOR_STATE_WRITE(state, ray, t) -= isect.t; @@ -46,7 +47,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg, const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); ray_P -= ray_D * mis_ray_t; isect.t += mis_ray_t; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = mis_ray_t + isect.t; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = isect.t; LightSample ls ccl_optional_struct_init; const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls); diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h index a68fcaa7a64..3e8eba29ef7 100644 --- a/intern/cycles/kernel/integrator/shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -83,7 +83,10 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, /* Setup shader data. */ Ray ray ccl_optional_struct_init; integrator_state_read_shadow_ray(kg, state, &ray); - + ray.self.object = OBJECT_NONE; + ray.self.prim = PRIM_NONE; + ray.self.light_object = OBJECT_NONE; + ray.self.light_prim = PRIM_NONE; /* Modify ray position and length to match current segment. */ const float start_t = (hit == 0) ? 0.0f : INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t); @@ -149,7 +152,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, const float last_hit_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, num_recorded_hits - 1, t); const float3 ray_P = INTEGRATOR_STATE(state, shadow_ray, P); const float3 ray_D = INTEGRATOR_STATE(state, shadow_ray, D); - INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_offset(ray_P + last_hit_t * ray_D, ray_D); + INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_P + last_hit_t * ray_D; INTEGRATOR_STATE_WRITE(state, shadow_ray, t) -= last_hit_t; } diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 9f6077e5d66..10d3cbf7f57 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -182,23 +182,35 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, /* Write shadow ray and associated state to global memory. */ integrator_state_write_shadow_ray(kg, shadow_state, &ray); + // Save memory by storing the light and object indices in the shadow_isect + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim; /* Copy state from main path to shadow path. */ const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; - shadow_flag |= (shadow_flag & PATH_RAY_ANY_PASS) ? 0 : PATH_RAY_SURFACE_PASS; const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * bsdf_eval_sum(&bsdf_eval); if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { - const packed_float3 pass_diffuse_weight = - (bounce == 0) ? packed_float3(bsdf_eval_pass_diffuse_weight(&bsdf_eval)) : - INTEGRATOR_STATE(state, path, pass_diffuse_weight); - const packed_float3 pass_glossy_weight = (bounce == 0) ? - packed_float3( - bsdf_eval_pass_glossy_weight(&bsdf_eval)) : - INTEGRATOR_STATE(state, path, pass_glossy_weight); + packed_float3 pass_diffuse_weight; + packed_float3 pass_glossy_weight; + + if (shadow_flag & PATH_RAY_ANY_PASS) { + /* Indirect bounce, use weights from earlier surface or volume bounce. */ + pass_diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight); + pass_glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight); + } + else { + /* Direct light, use BSDFs at this bounce. */ + shadow_flag |= PATH_RAY_SURFACE_PASS; + pass_diffuse_weight = packed_float3(bsdf_eval_pass_diffuse_weight(&bsdf_eval)); + pass_glossy_weight = packed_float3(bsdf_eval_pass_glossy_weight(&bsdf_eval)); + } + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight; INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = pass_glossy_weight; } @@ -266,13 +278,11 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( } /* Setup ray. Note that clipping works through transparent bounces. */ - INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, - (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); + INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in); INTEGRATOR_STATE_WRITE(state, ray, t) = (label & LABEL_TRANSPARENT) ? INTEGRATOR_STATE(state, ray, t) - sd->ray_length : FLT_MAX; - #ifdef __RAY_DIFFERENTIALS__ INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in); @@ -316,7 +326,7 @@ ccl_device_forceinline bool integrate_surface_volume_only_bounce(IntegratorState } /* Setup ray position, direction stays unchanged. */ - INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, -sd->Ng); + INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; /* Clipping works through transparent. */ INTEGRATOR_STATE_WRITE(state, ray, t) -= sd->ray_length; @@ -360,10 +370,14 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, } Ray ray ccl_optional_struct_init; - ray.P = ray_offset(sd->P, sd->Ng); + ray.P = sd->P; ray.D = ao_D; ray.t = kernel_data.integrator.ao_bounces_distance; ray.time = sd->time; + ray.self.object = sd->object; + ray.self.prim = sd->prim; + ray.self.light_object = OBJECT_NONE; + ray.self.light_prim = PRIM_NONE; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); @@ -375,6 +389,10 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, /* Write shadow ray and associated state to global memory. */ integrator_state_write_shadow_ray(kg, shadow_state, &ray); + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim; /* Copy state from main path to shadow path. */ const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index 00fa256d894..c59234553a7 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -791,22 +791,36 @@ ccl_device_forceinline void integrate_volume_direct_light( /* Write shadow ray and associated state to global memory. */ integrator_state_write_shadow_ray(kg, shadow_state, &ray); + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim; /* Copy state from main path to shadow path. */ const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; - shadow_flag |= (shadow_flag & PATH_RAY_ANY_PASS) ? 0 : PATH_RAY_VOLUME_PASS; const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval); if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { - const packed_float3 pass_diffuse_weight = (bounce == 0) ? - packed_float3(one_float3()) : - INTEGRATOR_STATE( - state, path, pass_diffuse_weight); + packed_float3 pass_diffuse_weight; + packed_float3 pass_glossy_weight; + + if (shadow_flag & PATH_RAY_ANY_PASS) { + /* Indirect bounce, use weights from earlier surface or volume bounce. */ + pass_diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight); + pass_glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight); + } + else { + /* Direct light, no diffuse/glossy distinction needed for volumes. */ + shadow_flag |= PATH_RAY_VOLUME_PASS; + pass_diffuse_weight = packed_float3(one_float3()); + pass_glossy_weight = packed_float3(zero_float3()); + } + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight; - INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = zero_float3(); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = pass_glossy_weight; } INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( @@ -873,11 +887,13 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in); INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; - # ifdef __RAY_DIFFERENTIALS__ INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in); # endif + // Save memory by storing last hit prim and object in isect + INTEGRATOR_STATE_WRITE(state, isect, prim) = sd->prim; + INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object; /* Update throughput. */ const float3 throughput = INTEGRATOR_STATE(state, path, throughput); diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h index 625a429d3db..86fcabdcd82 100644 --- a/intern/cycles/kernel/integrator/shadow_state_template.h +++ b/intern/cycles/kernel/integrator/shadow_state_template.h @@ -61,6 +61,7 @@ KERNEL_STRUCT_MEMBER(shadow_ray, packed_float3, D, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, int, object, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_END(shadow_ray) /*********************** Shadow Intersection result **************************/ diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h index 59b0cd2596c..6c0f815afea 100644 --- a/intern/cycles/kernel/integrator/subsurface.h +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -57,7 +57,6 @@ ccl_device int subsurface_bounce(KernelGlobals kg, /* Pass along object info, reusing isect to save memory. */ INTEGRATOR_STATE_WRITE(state, subsurface, Ng) = sd->Ng; - INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object; uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA) | ((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? PATH_RAY_SUBSURFACE_DISK : @@ -165,10 +164,8 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) { float3 P = INTEGRATOR_STATE(state, ray, P); - const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng); - const float3 offset_P = ray_offset(P, -Ng); - integrator_volume_stack_update_for_subsurface(kg, state, offset_P, ray.P); + integrator_volume_stack_update_for_subsurface(kg, state, P, ray.P); } } # endif /* __VOLUME__ */ diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h index cc6f5048cda..f5641d1fa5e 100644 --- a/intern/cycles/kernel/integrator/subsurface_disk.h +++ b/intern/cycles/kernel/integrator/subsurface_disk.h @@ -99,6 +99,10 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, ray.dP = ray_dP; ray.dD = differential_zero_compact(); ray.time = time; + ray.self.object = OBJECT_NONE; + ray.self.prim = PRIM_NONE; + ray.self.light_object = OBJECT_NONE; + ray.self.light_prim = OBJECT_NONE; /* Intersect with the same object. if multiple intersections are found it * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */ diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h index 7a8b467e199..993c54d9050 100644 --- a/intern/cycles/kernel/integrator/subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h @@ -195,6 +195,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, const float time = INTEGRATOR_STATE(state, ray, time); const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng); const int object = INTEGRATOR_STATE(state, isect, object); + const int prim = INTEGRATOR_STATE(state, isect, prim); /* Sample diffuse surface scatter into the object. */ float3 D; @@ -205,12 +206,16 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, } /* Setup ray. */ - ray.P = ray_offset(P, -Ng); + ray.P = P; ray.D = D; ray.t = FLT_MAX; ray.time = time; ray.dP = ray_dP; ray.dD = differential_zero_compact(); + ray.self.object = object; + ray.self.prim = prim; + ray.self.light_object = OBJECT_NONE; + ray.self.light_prim = PRIM_NONE; #ifndef __KERNEL_GPU_RAYTRACING__ /* Compute or fetch object transforms. */ @@ -377,7 +382,15 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, * If yes, we will later use backwards guided sampling in order to have a decent * chance of connecting to it. * TODO: Maybe use less than 10 times the mean free path? */ - ray.t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t; + if (bounce == 0) { + ray.t = max(t, 10.0f / (min3(sigma_t))); + } + else { + ray.t = t; + /* After the first bounce the object can intersect the same surface again */ + ray.self.object = OBJECT_NONE; + ray.self.prim = PRIM_NONE; + } scene_intersect_local(kg, &ray, &ss_isect, object, NULL, 1); hit = (ss_isect.num_hits > 0); @@ -408,13 +421,6 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, if (hit) { t = ray.t; } - else if (bounce == 0) { - /* Restore original position if nothing was hit after the first bounce, - * without the ray_offset() that was added to avoid self-intersection. - * Otherwise if that offset is relatively large compared to the scattering - * radius, we never go back up high enough to exit the surface. */ - ray.P = P; - } /* Advance to new scatter location. */ ray.P += t * ray.D; diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index 6e445f862db..d05fe47cc2c 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -113,22 +113,30 @@ ccl_device_inline bool light_sample(KernelGlobals kg, ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]); if (type == LIGHT_SPOT) { - ls->Ng = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); - float radius = klight->spot.radius; + const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); + const float radius = klight->spot.radius; + const float3 dir = make_float3( + klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); + /* disk oriented normal */ + const float3 lightN = normalize(P - center); + ls->P = center; if (radius > 0.0f) - /* sphere light */ - ls->P += disk_light_sample(ls->Ng, randu, randv) * radius; + /* disk light */ + ls->P += disk_light_sample(lightN, randu, randv) * radius; + + const float invarea = klight->spot.invarea; + ls->pdf = invarea; ls->D = normalize_len(ls->P - P, &ls->t); + /* we set the light normal to the outgoing direction to support texturing */ + ls->Ng = -ls->D; - float invarea = klight->spot.invarea; ls->eval_fac = (0.25f * M_1_PI_F) * invarea; - ls->pdf = invarea; /* spot light attenuation */ ls->eval_fac *= spot_light_attenuation( - ls->Ng, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D); + dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D); if (!in_volume_segment && ls->eval_fac == 0.0f) { return false; } @@ -137,32 +145,33 @@ ccl_device_inline bool light_sample(KernelGlobals kg, ls->u = uv.x; ls->v = uv.y; - ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t); + ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t); } else if (type == LIGHT_POINT) { float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); float radius = klight->spot.radius; + /* disk oriented normal */ + const float3 lightN = normalize(P - center); ls->P = center; - float pdf = 1.0; if (radius > 0.0f) { - ls->Ng = normalize(P - center); - ls->P += disk_light_sample(ls->Ng, randu, randv) * radius; - pdf = klight->spot.invarea; - ls->D = normalize_len(ls->P - P, &ls->t); - } - else { - ls->Ng = normalize(P - center); + ls->P += disk_light_sample(lightN, randu, randv) * radius; } + ls->pdf = klight->spot.invarea; ls->D = normalize_len(ls->P - P, &ls->t); - ls->pdf = pdf; + /* we set the light normal to the outgoing direction to support texturing */ + ls->Ng = -ls->D; + ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; + if (!in_volume_segment && ls->eval_fac == 0.0f) { + return false; + } float2 uv = map_to_sphere(ls->Ng); ls->u = uv.x; ls->v = uv.y; - ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t); + ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t); } else { /* area light */ @@ -263,14 +272,16 @@ ccl_device bool lights_intersect(KernelGlobals kg, if (type == LIGHT_SPOT) { /* Spot/Disk light. */ + const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); + const float3 ray_P = ray->P - ray->D * mis_ray_t; + const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); - const float3 lightN = make_float3( - klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); const float radius = klight->spot.radius; if (radius == 0.0f) { continue; } - + /* disk oriented normal */ + const float3 lightN = normalize(ray_P - lightP); /* One sided. */ if (dot(ray->D, lightN) >= 0.0f) { continue; @@ -292,9 +303,10 @@ ccl_device bool lights_intersect(KernelGlobals kg, continue; } + /* disk oriented normal */ + const float3 lightN = normalize(ray_P - lightP); float3 P; - const float3 lsN = normalize(ray_P - lightP); - if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lsN, radius, &P, &t)) { + if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lightN, radius, &P, &t)) { continue; } } @@ -418,8 +430,8 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, LightType type = (LightType)klight->type; ls->type = type; ls->shader = klight->shader_id; - ls->object = PRIM_NONE; - ls->prim = PRIM_NONE; + ls->object = isect->object; + ls->prim = isect->prim; ls->lamp = lamp; /* todo: missing texture coordinates */ ls->t = isect->t; @@ -427,7 +439,12 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, ls->D = ray_D; if (type == LIGHT_SPOT) { - ls->Ng = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); + const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); + const float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); + /* the normal of the oriented disk */ + const float3 lightN = normalize(ray_P - center); + /* we set the light normal to the outgoing direction to support texturing*/ + ls->Ng = -ls->D; float invarea = klight->spot.invarea; ls->eval_fac = (0.25f * M_1_PI_F) * invarea; @@ -435,7 +452,7 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, /* spot light attenuation */ ls->eval_fac *= spot_light_attenuation( - ls->Ng, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D); + dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D); if (ls->eval_fac == 0.0f) { return false; @@ -447,23 +464,32 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, /* compute pdf */ if (ls->t != FLT_MAX) - ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t); + ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t); + else + ls->pdf = 0.f; } else if (type == LIGHT_POINT) { - float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); + const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); + const float3 lighN = normalize(ray_P - center); + + /* we set the light normal to the outgoing direction to support texturing*/ + ls->Ng = -ls->D; - ls->Ng = normalize(ray_P - center); float invarea = klight->spot.invarea; ls->eval_fac = (0.25f * M_1_PI_F) * invarea; ls->pdf = invarea; + if (ls->eval_fac == 0.0f) { + return false; + } + float2 uv = map_to_sphere(ls->Ng); ls->u = uv.x; ls->v = uv.y; /* compute pdf */ if (ls->t != FLT_MAX) - ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t); + ls->pdf *= lamp_light_pdf(kg, lighN, -ls->D, ls->t); else ls->pdf = 0.f; } @@ -921,4 +947,4 @@ ccl_device_inline bool light_distribution_sample_new_position(KernelGlobals kg, } } -CCL_NAMESPACE_END +CCL_NAMESPACE_END
\ No newline at end of file diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 7dbc783b1bb..521ad2f7066 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -198,7 +198,7 @@ ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg, float NL = dot(sd->N, L); bool transmit = (NL < 0.0f); float3 Ng = (transmit ? -sd->Ng : sd->Ng); - float3 P = ray_offset(sd->P, Ng); + float3 P = sd->P; if ((sd->type & PRIMITIVE_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) { const float offset_cutoff = @@ -243,7 +243,7 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri } else { /* other lights, avoid self-intersection */ - ray->D = ray_offset(ls->P, ls->Ng) - P; + ray->D = ls->P - P; ray->D = normalize_len(ray->D, &ray->t); } } @@ -257,6 +257,12 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri ray->dP = differential_make_compact(sd->dP); ray->dD = differential_zero_compact(); ray->time = sd->time; + + /* Fill in intersection surface and light details. */ + ray->self.prim = sd->prim; + ray->self.object = sd->object; + ray->self.light_prim = ls->prim; + ray->self.light_object = ls->object; } /* Create shadow ray towards light sample. */ diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index a79fc323a13..d79e7dfa8a5 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -116,6 +116,8 @@ ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal"); ustring OSLRenderServices::u_curve_random("geom:curve_random"); ustring OSLRenderServices::u_is_point("geom:is_point"); ustring OSLRenderServices::u_point_radius("geom:point_radius"); +ustring OSLRenderServices::u_point_position("geom:point_position"); +ustring OSLRenderServices::u_point_random("geom:point_random"); ustring OSLRenderServices::u_normal_map_normal("geom:normal_map_normal"); ustring OSLRenderServices::u_path_ray_length("path:ray_length"); ustring OSLRenderServices::u_path_ray_depth("path:ray_depth"); @@ -999,6 +1001,10 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg float3 f = curve_tangent_normal(kg, sd); return set_attribute_float3(f, type, derivatives, val); } + else if (name == u_curve_random) { + float f = curve_random(kg, sd); + return set_attribute_float(f, type, derivatives, val); + } /* point attributes */ else if (name == u_is_point) { float f = (sd->type & PRIMITIVE_POINT) != 0; @@ -1008,6 +1014,14 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg float f = point_radius(kg, sd); return set_attribute_float(f, type, derivatives, val); } + else if (name == u_point_position) { + float3 f = point_position(kg, sd); + return set_attribute_float3(f, type, derivatives, val); + } + else if (name == u_point_random) { + float f = point_random(kg, sd); + return set_attribute_float(f, type, derivatives, val); + } else if (name == u_normal_map_normal) { if (sd->type & PRIMITIVE_TRIANGLE) { float3 f = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v); diff --git a/intern/cycles/kernel/osl/services.h b/intern/cycles/kernel/osl/services.h index 9526c92b8fb..96c71297186 100644 --- a/intern/cycles/kernel/osl/services.h +++ b/intern/cycles/kernel/osl/services.h @@ -298,7 +298,9 @@ class OSLRenderServices : public OSL::RendererServices { static ustring u_curve_tangent_normal; static ustring u_curve_random; static ustring u_is_point; + static ustring u_point_position; static ustring u_point_radius; + static ustring u_point_random; static ustring u_normal_map_normal; static ustring u_path_ray_length; static ustring u_path_ray_depth; diff --git a/intern/cycles/kernel/osl/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt index 4cafdb2a6d7..16a9b1cc012 100644 --- a/intern/cycles/kernel/osl/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt @@ -49,6 +49,7 @@ set(SRC_OSL node_glossy_bsdf.osl node_gradient_texture.osl node_hair_info.osl + node_point_info.osl node_scatter_volume.osl node_absorption_volume.osl node_principled_volume.osl diff --git a/intern/cycles/kernel/osl/shaders/node_point_info.osl b/intern/cycles/kernel/osl/shaders/node_point_info.osl new file mode 100644 index 00000000000..58d8acbf269 --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_point_info.osl @@ -0,0 +1,26 @@ +/* + * Copyright 2011-2022 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdcycles.h" + +shader node_point_info(output point Position = point(0.0, 0.0, 0.0), + output float Radius = 0.0, + output float Random = 0.0) +{ + getattribute("geom:point_position", Position); + getattribute("geom:point_radius", Radius); + getattribute("geom:point_random", Random); +} diff --git a/intern/cycles/kernel/svm/ao.h b/intern/cycles/kernel/svm/ao.h index 678f49c8ccd..dcb1a79717d 100644 --- a/intern/cycles/kernel/svm/ao.h +++ b/intern/cycles/kernel/svm/ao.h @@ -70,10 +70,14 @@ ccl_device float svm_ao( /* Create ray. */ Ray ray; - ray.P = ray_offset(sd->P, N); + ray.P = sd->P; ray.D = D.x * T + D.y * B + D.z * N; ray.t = max_dist; ray.time = sd->time; + ray.self.object = sd->object; + ray.self.prim = sd->prim; + ray.self.light_object = OBJECT_NONE; + ray.self.light_prim = PRIM_NONE; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); diff --git a/intern/cycles/kernel/svm/bevel.h b/intern/cycles/kernel/svm/bevel.h index 46dfb6631da..98b663299da 100644 --- a/intern/cycles/kernel/svm/bevel.h +++ b/intern/cycles/kernel/svm/bevel.h @@ -196,6 +196,10 @@ ccl_device float3 svm_bevel( ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); ray.time = sd->time; + ray.self.object = OBJECT_NONE; + ray.self.prim = PRIM_NONE; + ray.self.light_object = OBJECT_NONE; + ray.self.light_prim = PRIM_NONE; /* Intersect with the same object. if multiple intersections are found it * will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */ @@ -207,15 +211,24 @@ ccl_device float3 svm_bevel( /* Quickly retrieve P and Ng without setting up ShaderData. */ float3 hit_P; if (sd->type == PRIMITIVE_TRIANGLE) { - hit_P = triangle_refine_local( - kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim); + hit_P = triangle_point_from_uv(kg, + sd, + isect.hits[hit].object, + isect.hits[hit].prim, + isect.hits[hit].u, + isect.hits[hit].v); } # ifdef __OBJECT_MOTION__ else if (sd->type == PRIMITIVE_MOTION_TRIANGLE) { float3 verts[3]; motion_triangle_vertices(kg, sd->object, isect.hits[hit].prim, sd->time, verts); - hit_P = motion_triangle_refine_local( - kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim, verts); + hit_P = motion_triangle_point_from_uv(kg, + sd, + isect.hits[hit].object, + isect.hits[hit].prim, + isect.hits[hit].u, + isect.hits[hit].v, + verts); } # endif /* __OBJECT_MOTION__ */ diff --git a/intern/cycles/kernel/svm/geometry.h b/intern/cycles/kernel/svm/geometry.h index 2bac58b0aa2..225348b1ac2 100644 --- a/intern/cycles/kernel/svm/geometry.h +++ b/intern/cycles/kernel/svm/geometry.h @@ -242,13 +242,6 @@ ccl_device_noinline void svm_node_hair_info(KernelGlobals kg, stack_store_float(stack, out_offset, data); break; } -# if 0 - case NODE_INFO_CURVE_FADE: { - data = sd->curve_transparency; - stack_store_float(stack, out_offset, data); - break; - } -# endif case NODE_INFO_CURVE_TANGENT_NORMAL: { data3 = curve_tangent_normal(kg, sd); stack_store_float3(stack, out_offset, data3); @@ -258,4 +251,28 @@ ccl_device_noinline void svm_node_hair_info(KernelGlobals kg, } #endif +#ifdef __POINTCLOUD__ + +/* Point Info */ + +ccl_device_noinline void svm_node_point_info(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) +{ + switch (type) { + case NODE_INFO_POINT_POSITION: + stack_store_float3(stack, out_offset, point_position(kg, sd)); + break; + case NODE_INFO_POINT_RADIUS: + stack_store_float(stack, out_offset, point_radius(kg, sd)); + break; + case NODE_INFO_POINT_RANDOM: + break; /* handled as attribute */ + } +} + +#endif + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index b226bc66771..35d4c3f2055 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -454,13 +454,14 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, break; #if defined(__HAIR__) case NODE_HAIR_INFO: - IF_KERNEL_NODES_FEATURE(HAIR) - { - svm_node_hair_info(kg, sd, stack, node.y, node.z); - } + svm_node_hair_info(kg, sd, stack, node.y, node.z); + break; +#endif +#if defined(__POINTCLOUD__) + case NODE_POINT_INFO: + svm_node_point_info(kg, sd, stack, node.y, node.z); break; #endif - case NODE_TEXTURE_MAPPING: offset = svm_node_texture_mapping(kg, sd, stack, node.y, node.z, offset); break; diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index dd1b1f9bc28..16e9fd8862a 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -81,6 +81,7 @@ typedef enum ShaderNodeType { NODE_OBJECT_INFO, NODE_PARTICLE_INFO, NODE_HAIR_INFO, + NODE_POINT_INFO, NODE_TEXTURE_MAPPING, NODE_MAPPING, NODE_MIN_MAX, @@ -176,12 +177,16 @@ typedef enum NodeHairInfo { NODE_INFO_CURVE_INTERCEPT, NODE_INFO_CURVE_LENGTH, NODE_INFO_CURVE_THICKNESS, - /* Fade for minimum hair width transiency. */ - // NODE_INFO_CURVE_FADE, NODE_INFO_CURVE_TANGENT_NORMAL, NODE_INFO_CURVE_RANDOM, } NodeHairInfo; +typedef enum NodePointInfo { + NODE_INFO_POINT_POSITION, + NODE_INFO_POINT_RADIUS, + NODE_INFO_POINT_RANDOM, +} NodePointInfo; + typedef enum NodeLightPath { NODE_LP_camera = 0, NODE_LP_shadow, diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 5d41abb53c4..d4cb22d4af4 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -512,12 +512,21 @@ typedef struct differential { /* Ray */ +typedef struct RaySelfPrimitives { + int prim; /* Primitive the ray is starting from */ + int object; /* Instance prim is a part of */ + int light_prim; /* Light primitive */ + int light_object; /* Light object */ +} RaySelfPrimitives; + typedef struct Ray { float3 P; /* origin */ float3 D; /* direction */ float t; /* length of the ray */ float time; /* time (for motion blur) */ + RaySelfPrimitives self; + #ifdef __RAY_DIFFERENTIALS__ float dP; float dD; @@ -1565,21 +1574,21 @@ enum KernelFeatureFlag : uint32_t { KERNEL_FEATURE_NODE_BSDF = (1U << 0U), KERNEL_FEATURE_NODE_EMISSION = (1U << 1U), KERNEL_FEATURE_NODE_VOLUME = (1U << 2U), - KERNEL_FEATURE_NODE_HAIR = (1U << 3U), - KERNEL_FEATURE_NODE_BUMP = (1U << 4U), - KERNEL_FEATURE_NODE_BUMP_STATE = (1U << 5U), - KERNEL_FEATURE_NODE_VORONOI_EXTRA = (1U << 6U), - KERNEL_FEATURE_NODE_RAYTRACE = (1U << 7U), - KERNEL_FEATURE_NODE_AOV = (1U << 8U), - KERNEL_FEATURE_NODE_LIGHT_PATH = (1U << 9U), + KERNEL_FEATURE_NODE_BUMP = (1U << 3U), + KERNEL_FEATURE_NODE_BUMP_STATE = (1U << 4U), + KERNEL_FEATURE_NODE_VORONOI_EXTRA = (1U << 5U), + KERNEL_FEATURE_NODE_RAYTRACE = (1U << 6U), + KERNEL_FEATURE_NODE_AOV = (1U << 7U), + KERNEL_FEATURE_NODE_LIGHT_PATH = (1U << 8U), /* Use denoising kernels and output denoising passes. */ - KERNEL_FEATURE_DENOISING = (1U << 10U), + KERNEL_FEATURE_DENOISING = (1U << 9U), /* Use path tracing kernels. */ - KERNEL_FEATURE_PATH_TRACING = (1U << 11U), + KERNEL_FEATURE_PATH_TRACING = (1U << 10U), /* BVH/sampling kernel features. */ + KERNEL_FEATURE_POINTCLOUD = (1U << 11U), KERNEL_FEATURE_HAIR = (1U << 12U), KERNEL_FEATURE_HAIR_THICK = (1U << 13U), KERNEL_FEATURE_OBJECT_MOTION = (1U << 14U), @@ -1616,9 +1625,6 @@ enum KernelFeatureFlag : uint32_t { KERNEL_FEATURE_AO_PASS = (1U << 25U), KERNEL_FEATURE_AO_ADDITIVE = (1U << 26U), KERNEL_FEATURE_AO = (KERNEL_FEATURE_AO_PASS | KERNEL_FEATURE_AO_ADDITIVE), - - /* Point clouds. */ - KERNEL_FEATURE_POINTCLOUD = (1U << 27U), }; /* Shader node feature mask, to specialize shader evaluation for kernels. */ @@ -1628,7 +1634,7 @@ enum KernelFeatureFlag : uint32_t { KERNEL_FEATURE_NODE_LIGHT_PATH) #define KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW \ (KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \ - KERNEL_FEATURE_NODE_HAIR | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE | \ + KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE | \ KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_LIGHT_PATH) #define KERNEL_FEATURE_NODE_MASK_SURFACE \ (KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW | KERNEL_FEATURE_NODE_RAYTRACE | \ diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index 49d18d00dd7..90f1e1cb021 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -236,6 +236,7 @@ void Geometry::compute_bvh( BVHParams bparams; bparams.use_spatial_split = params->use_bvh_spatial_split; + bparams.use_compact_structure = params->use_bvh_compact_structure; bparams.bvh_layout = bvh_layout; bparams.use_unaligned_nodes = dscene->data.bvh.have_curves && params->use_bvh_unaligned_nodes; diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp index 1963ebbbb19..b5b8eee24a7 100644 --- a/intern/cycles/scene/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -570,7 +570,6 @@ static void log_kernel_features(const uint features) << "\n"; VLOG(2) << "Use Emission " << string_from_bool(features & KERNEL_FEATURE_NODE_EMISSION) << "\n"; VLOG(2) << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_NODE_VOLUME) << "\n"; - VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_NODE_HAIR) << "\n"; VLOG(2) << "Use Bump " << string_from_bool(features & KERNEL_FEATURE_NODE_BUMP) << "\n"; VLOG(2) << "Use Voronoi " << string_from_bool(features & KERNEL_FEATURE_NODE_VORONOI_EXTRA) << "\n"; diff --git a/intern/cycles/scene/scene.h b/intern/cycles/scene/scene.h index ec935b41be6..77268837070 100644 --- a/intern/cycles/scene/scene.h +++ b/intern/cycles/scene/scene.h @@ -160,6 +160,7 @@ class SceneParams { BVHType bvh_type; bool use_bvh_spatial_split; + bool use_bvh_compact_structure; bool use_bvh_unaligned_nodes; int num_bvh_time_steps; int hair_subdivisions; @@ -174,6 +175,7 @@ class SceneParams { bvh_layout = BVH_LAYOUT_BVH2; bvh_type = BVH_TYPE_DYNAMIC; use_bvh_spatial_split = false; + use_bvh_compact_structure = true; use_bvh_unaligned_nodes = true; num_bvh_time_steps = 0; hair_subdivisions = 3; @@ -187,6 +189,7 @@ class SceneParams { return !(shadingsystem == params.shadingsystem && bvh_layout == params.bvh_layout && bvh_type == params.bvh_type && use_bvh_spatial_split == params.use_bvh_spatial_split && + use_bvh_compact_structure == params.use_bvh_compact_structure && use_bvh_unaligned_nodes == params.use_bvh_unaligned_nodes && num_bvh_time_steps == params.num_bvh_time_steps && hair_subdivisions == params.hair_subdivisions && hair_shape == params.hair_shape && diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index e8316ad41b4..34675be8e80 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -32,6 +32,7 @@ #include "util/color.h" #include "util/foreach.h" #include "util/log.h" +#include "util/string.h" #include "util/transform.h" #include "kernel/tables.h" @@ -462,8 +463,12 @@ void ImageTextureNode::compile(OSLCompiler &compiler) const ustring known_colorspace = metadata.colorspace; if (handle.svm_slot() == -1) { + /* OIIO currently does not support <UVTILE> substitutions natively. Replace with a format they + * understand. */ + std::string osl_filename = filename.string(); + string_replace(osl_filename, "<UVTILE>", "<U>_<V>"); compiler.parameter_texture( - "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); + "filename", ustring(osl_filename), compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { compiler.parameter_texture("filename", handle.svm_slot()); @@ -472,7 +477,8 @@ void ImageTextureNode::compile(OSLCompiler &compiler) const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) || alpha_type == IMAGE_ALPHA_CHANNEL_PACKED || alpha_type == IMAGE_ALPHA_IGNORE); - const bool is_tiled = (filename.find("<UDIM>") != string::npos); + const bool is_tiled = (filename.find("<UDIM>") != string::npos || + filename.find("<UVTILE>") != string::npos); compiler.parameter(this, "projection"); compiler.parameter(this, "projection_blend"); @@ -4388,9 +4394,6 @@ NODE_DEFINE(HairInfoNode) SOCKET_OUT_FLOAT(size, "Length"); SOCKET_OUT_FLOAT(thickness, "Thickness"); SOCKET_OUT_NORMAL(tangent_normal, "Tangent Normal"); -#if 0 /* Output for minimum hair width transparency - deactivated. */ - SOCKET_OUT_FLOAT(fade, "Fade"); -#endif SOCKET_OUT_FLOAT(index, "Random"); return type; @@ -4448,12 +4451,7 @@ void HairInfoNode::compile(SVMCompiler &compiler) if (!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_TANGENT_NORMAL, compiler.stack_assign(out)); } -#if 0 - out = output("Fade"); - if(!out->links.empty()) { - compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_FADE, compiler.stack_assign(out)); - } -#endif + out = output("Random"); if (!out->links.empty()) { int attr = compiler.attribute(ATTR_STD_CURVE_RANDOM); @@ -4466,6 +4464,59 @@ void HairInfoNode::compile(OSLCompiler &compiler) compiler.add(this, "node_hair_info"); } +/* Point Info */ + +NODE_DEFINE(PointInfoNode) +{ + NodeType *type = NodeType::add("point_info", create, NodeType::SHADER); + + SOCKET_OUT_POINT(position, "Position"); + SOCKET_OUT_FLOAT(radius, "Radius"); + SOCKET_OUT_FLOAT(random, "Random"); + + return type; +} + +PointInfoNode::PointInfoNode() : ShaderNode(get_node_type()) +{ +} + +void PointInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) +{ + if (shader->has_surface_link()) { + if (!output("Random")->links.empty()) + attributes->add(ATTR_STD_POINT_RANDOM); + } + + ShaderNode::attributes(shader, attributes); +} + +void PointInfoNode::compile(SVMCompiler &compiler) +{ + ShaderOutput *out; + + out = output("Position"); + if (!out->links.empty()) { + compiler.add_node(NODE_POINT_INFO, NODE_INFO_POINT_POSITION, compiler.stack_assign(out)); + } + + out = output("Radius"); + if (!out->links.empty()) { + compiler.add_node(NODE_POINT_INFO, NODE_INFO_POINT_RADIUS, compiler.stack_assign(out)); + } + + out = output("Random"); + if (!out->links.empty()) { + int attr = compiler.attribute(ATTR_STD_POINT_RANDOM); + compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT); + } +} + +void PointInfoNode::compile(OSLCompiler &compiler) +{ + compiler.add(this, "node_point_info"); +} + /* Volume Info */ NODE_DEFINE(VolumeInfoNode) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index 0faefd3041f..a8d5bdcf157 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -1005,9 +1005,20 @@ class HairInfoNode : public ShaderNode { { return true; } - virtual int get_feature() +}; + +class PointInfoNode : public ShaderNode { + public: + SHADER_NODE_CLASS(PointInfoNode) + + void attributes(Shader *shader, AttributeRequestSet *attributes); + bool has_attribute_dependency() + { + return true; + } + bool has_spatial_varying() { - return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_HAIR; + return true; } }; diff --git a/intern/cycles/util/math.h b/intern/cycles/util/math.h index 18b60b70a4b..605a19aaef0 100644 --- a/intern/cycles/util/math.h +++ b/intern/cycles/util/math.h @@ -401,7 +401,7 @@ ccl_device_inline float fractf(float x) return x - floorf(x); } -/* Adapted from godot-engine math_funcs.h. */ +/* Adapted from `godot-engine` math_funcs.h. */ ccl_device_inline float wrapf(float value, float max, float min) { float range = max - min; diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 84f156949aa..34d1ab1150e 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -291,7 +291,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) include(CheckSymbolExists) set(CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") check_symbol_exists(memfd_create "sys/mman.h" HAVE_MEMFD_CREATE) - if (HAVE_MEMFD_CREATE) + if(HAVE_MEMFD_CREATE) add_definitions(-DHAVE_MEMFD_CREATE) endif() @@ -307,7 +307,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner) pkg_check_modules(wayland-protocols wayland-protocols>=1.15) - if (${wayland-protocols_FOUND}) + if(${wayland-protocols_FOUND}) pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) else() find_path(WAYLAND_PROTOCOLS_DIR @@ -316,7 +316,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ) endif() - if (NOT EXISTS ${WAYLAND_PROTOCOLS_DIR}) + if(NOT EXISTS ${WAYLAND_PROTOCOLS_DIR}) message(FATAL_ERROR "path to wayland-protocols not found") endif() @@ -518,11 +518,11 @@ if(WITH_XR_OPENXR) ) elseif(UNIX AND NOT APPLE) list(APPEND XR_PLATFORM_DEFINES -DXR_OS_LINUX) - if (WITH_GHOST_WAYLAND) + if(WITH_GHOST_WAYLAND) list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_WAYLAND) endif() - if (WITH_GHOST_X11) - if (WITH_GL_EGL) + if(WITH_GHOST_X11) + if(WITH_GL_EGL) list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_EGL) else() list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_XLIB) diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp index d1fc80adf56..2a1bfb633b3 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.cpp +++ b/intern/ghost/intern/GHOST_ImeWin32.cpp @@ -96,7 +96,7 @@ bool GHOST_ImeWin32::IsEnglishMode() !(conversion_modes_ & (IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE)); } -bool GHOST_ImeWin32::IsImeKeyEvent(char ascii) +bool GHOST_ImeWin32::IsImeKeyEvent(char ascii, GHOST_TKey key) { if (!(IsEnglishMode())) { /* In Chinese, Japanese, Korean, all alpha keys are processed by IME. */ @@ -106,7 +106,8 @@ bool GHOST_ImeWin32::IsImeKeyEvent(char ascii) if (IsLanguage(IMELANG_JAPANESE) && (ascii >= ' ' && ascii <= '~')) { return true; } - else if (IsLanguage(IMELANG_CHINESE) && ascii && strchr("!\"$'(),.:;<>?[\\]^_`/", ascii)) { + else if (IsLanguage(IMELANG_CHINESE) && ascii && strchr("!\"$'(),.:;<>?[\\]^_`/", ascii) && + !(key == GHOST_kKeyNumpadPeriod)) { return true; } } diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h index ce0e4d64d53..d17a6d79503 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.h +++ b/intern/ghost/intern/GHOST_ImeWin32.h @@ -161,7 +161,7 @@ class GHOST_ImeWin32 { bool IsEnglishMode(); /* Checks a key whether IME has to do handling. */ - bool IsImeKeyEvent(char ascii); + bool IsImeKeyEvent(char ascii, GHOST_TKey key); /** * Create the IME windows, and allocate required resources for them. diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index b92c3e73a88..756c4f876ed 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -890,7 +890,7 @@ bool GHOST_SystemCocoa::processEvents(bool waitForEvent) bool anyProcessed = false; NSEvent *event; - // TODO : implement timer ?? + /* TODO: implement timer? */ #if 0 do { GHOST_TimerManager* timerMgr = getTimerManager(); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 5251dd01b29..923453d6c6f 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -105,6 +105,8 @@ */ #define BROKEN_PEEK_TOUCHPAD +static bool isStartedFromCommandPrompt(); + static void initRawInput() { #ifdef WITH_INPUT_NDOF @@ -166,7 +168,10 @@ GHOST_SystemWin32::~GHOST_SystemWin32() { // Shutdown COM OleUninitialize(); - toggleConsole(1); + + if (isStartedFromCommandPrompt()) { + toggleConsole(1); + } } uint64_t GHOST_SystemWin32::performanceCounterToMillis(__int64 perf_ticks) const @@ -1220,7 +1225,7 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA } #ifdef WITH_INPUT_IME - if (window->getImeInput()->IsImeKeyEvent(ascii)) { + if (window->getImeInput()->IsImeKeyEvent(ascii, key)) { return NULL; } #endif /* WITH_INPUT_IME */ diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index b5d0fd8e6db..47d4ff77d17 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -71,6 +71,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_mousePresent(false), m_inLiveResize(false), m_system(system), + m_dropTarget(NULL), + m_hWnd(0), m_hDC(0), m_isDialog(dialog), m_hasMouseCaptured(false), @@ -78,6 +80,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_nPressedButtons(0), m_customCursor(0), m_wantAlphaBackground(alphaBackground), + m_Bar(NULL), m_wintab(NULL), m_lastPointerTabletData(GHOST_TABLET_DATA_NONE), m_normal_state(GHOST_kWindowStateNormal), @@ -129,8 +132,24 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_hDC = ::GetDC(m_hWnd); if (!setDrawingContextType(type)) { + const char *title = "Blender - Unsupported Graphics Card Configuration"; + const char *text = + "A graphics card and driver with support for OpenGL 3.3 or higher is " + "required.\n\nInstalling the latest driver for your graphics card might resolve the " + "issue."; + if (GetSystemMetrics(SM_CMONITORS) > 1) { + text = + "A graphics card and driver with support for OpenGL 3.3 or higher is " + "required.\n\nPlugging all monitors into your primary graphics card might resolve " + "this issue. Installing the latest driver for your graphics card could also help."; + } + MessageBox(m_hWnd, text, title, MB_OK | MB_ICONERROR); + ::ReleaseDC(m_hWnd, m_hDC); ::DestroyWindow(m_hWnd); m_hWnd = NULL; + if (!parentwindow) { + exit(0); + } return; } @@ -564,20 +583,13 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); - if (context->initializeDrawingContext()) { - return context; - } - else { - MessageBox(m_hWnd, - "A graphics card and driver with support for OpenGL 3.3 or higher is required.\n" - "Installing the latest driver for your graphics card may resolve the issue.\n\n" - "The program will now close.", - "Blender - Unsupported Graphics Card or Driver", - MB_OK | MB_ICONERROR); + if (context && !context->initializeDrawingContext()) { delete context; - exit(0); + context = nullptr; } + return context; + #elif defined(WITH_GL_PROFILE_COMPAT) // ask for 2.1 context, driver gives any GL version >= 2.1 // (hopefully the latest compatibility profile) diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index 15b40690d83..9fa93f0c4b4 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -86,6 +86,7 @@ void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info) initApiLayers(); initExtensions(); if (isDebugMode()) { + printSDKVersion(); printAvailableAPILayersAndExtensionsInfo(); } @@ -156,6 +157,16 @@ void GHOST_XrContext::storeInstanceProperties() /** \name Debug Printing * \{ */ +void GHOST_XrContext::printSDKVersion() +{ + const XrVersion sdk_version = XR_CURRENT_API_VERSION; + + printf("OpenXR SDK Version: %u.%u.%u\n", + XR_VERSION_MAJOR(sdk_version), + XR_VERSION_MINOR(sdk_version), + XR_VERSION_PATCH(sdk_version)); +} + void GHOST_XrContext::printInstanceInfo() { assert(m_oxr->instance != XR_NULL_HANDLE); diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h index 479b50e1537..b00f017ff59 100644 --- a/intern/ghost/intern/GHOST_XrContext.h +++ b/intern/ghost/intern/GHOST_XrContext.h @@ -126,6 +126,7 @@ class GHOST_XrContext : public GHOST_IXrContext { void storeInstanceProperties(); void initDebugMessenger(); + void printSDKVersion(); void printInstanceInfo(); void printAvailableAPILayersAndExtensionsInfo(); void printExtensionsAndAPILayersToEnable(); diff --git a/intern/ghost/test/CMakeLists.txt b/intern/ghost/test/CMakeLists.txt index acd0da8785e..c564085c774 100644 --- a/intern/ghost/test/CMakeLists.txt +++ b/intern/ghost/test/CMakeLists.txt @@ -292,7 +292,7 @@ target_link_libraries(multitest_c guardedalloc_lib wcwidth_lib ${OPENGL_gl_LIBRARY} - ${FREETYPE_LIBRARIES} + ${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES} ${ZLIB_LIBRARIES} ${CMAKE_DL_LIBS} ${PLATFORM_LINKLIBS} diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index 8a20323dcfc..dccb7a32139 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -39,8 +39,8 @@ * second intern/ module with MEM_ prefix, for use in c++. * * \subsection memdependencies Dependencies - * - stdlib - * - stdio + * - `stdlib` + * - `stdio` * * \subsection memdocs API Documentation * See \ref MEM_guardedalloc.h @@ -268,6 +268,12 @@ void MEM_use_guarded_allocator(void); * Allocate new memory for and constructs an object of type #T. * #MEM_delete should be used to delete the object. Just calling #MEM_freeN is not enough when #T * is not a trivial type. + * + * Note that when no arguments are passed, C++ will do recursive member-wise value initialization. + * That is because C++ differentiates between creating an object with `T` (default initialization) + * and `T()` (value initialization), whereby this function does the latter. Value initialization + * rules are complex, but for C-style structs, memory will be zero-initialized. So this doesn't + * match a `malloc()`, but a `calloc()` call in this case. See https://stackoverflow.com/a/4982720. */ template<typename T, typename... Args> inline T *MEM_new(const char *allocation_name, Args &&...args) diff --git a/intern/locale/CMakeLists.txt b/intern/locale/CMakeLists.txt index 732fa1e4d11..3e467302d8e 100644 --- a/intern/locale/CMakeLists.txt +++ b/intern/locale/CMakeLists.txt @@ -56,7 +56,6 @@ if(WITH_INTERNATIONAL) list(APPEND LIB ${BOOST_LIBRARIES} ) - add_definitions(-DWITH_INTERNATIONAL) add_definitions(${BOOST_DEFINITIONS}) endif() @@ -13,6 +13,9 @@ if errorlevel 1 goto EOF call "%BLENDER_DIR%\build_files\windows\parse_arguments.cmd" %* if errorlevel 1 goto EOF +call "%BLENDER_DIR%\build_files\windows\find_dependencies.cmd" +if errorlevel 1 goto EOF + REM if it is one of the convenience targets and BLENDER_BIN is set REM skip compiler detection if "%ICONS%%ICONS_GEOM%%DOC_PY%" == "1" ( @@ -21,9 +24,6 @@ if "%ICONS%%ICONS_GEOM%%DOC_PY%" == "1" ( ) ) -call "%BLENDER_DIR%\build_files\windows\find_dependencies.cmd" -if errorlevel 1 goto EOF - if "%BUILD_SHOW_HASHES%" == "1" ( call "%BLENDER_DIR%\build_files\windows\show_hashes.cmd" goto EOF @@ -88,6 +88,11 @@ if "%DOC_PY%" == "1" ( goto EOF ) +if "%CMAKE%" == "" ( + echo Cmake not found in path, required for building, exiting... + exit /b 1 +) + echo Building blender with VS%BUILD_VS_YEAR% for %BUILD_ARCH% in %BUILD_DIR% call "%BLENDER_DIR%\build_files\windows\check_libraries.cmd" diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 68f8874599d7fbac10fa332535450d8a78fafda +Subproject 050058417452bfba0cc9ae8692173eb02ac1ef3 diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 0574f418a03e2dddfac8b402ce1fb8c33b6b25d +Subproject faa9fc7f98e19be54a715c24061185b04dff5b6 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject 7936dde9ece881d531b1a2ee6c45ddb56d30038 +Subproject 61e45814503f51963c91c51aaf764612e7c5dc7 diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index bdc345ee50a..d41db4e45b3 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -356,6 +356,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "y", "y = (Ax + B)", # Sub-strings. + "and AMD Radeon Pro 21.Q4 driver or newer", "available with", "brown fox", "can't save image while rendering", @@ -378,6 +379,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "image path can't be written to", "in memory to enable editing!", "insufficient content", + "into", "jumps over", "left", "local", @@ -387,6 +389,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "performance impact!", "right", "the lazy dog", + "to the top level of the tree", "unable to load movie clip", "unable to load text", "unable to open the file", diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 62186655326..6a74c27b9c4 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -76,10 +76,12 @@ class SpellChecker: "tangency", "vertices", "wasn", # wasn't + "zig", "zag", # Brands etc. "htc", "huawei", + "radeon", "vive", "xbox", @@ -136,6 +138,7 @@ class SpellChecker: "filename", "filenames", "filepath", "filepaths", "forcefield", "forcefields", + "framerange", "fulldome", "fulldomes", "fullscreen", "gamepad", @@ -498,6 +501,7 @@ class SpellChecker: "framerate", "gimbal", "grayscale", + "icosahedron", "icosphere", "inpaint", "kerning", @@ -556,6 +560,7 @@ class SpellChecker: "bspline", "bweight", "colorband", + "crazyspace", "datablock", "datablocks", "despeckle", "depsgraph", @@ -730,6 +735,7 @@ class SpellChecker: "precisa", "px", "qmc", + "rdna", "rdp", "rgb", "rgba", "rhs", diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 2983a326358..f0da4bac974 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -677,7 +677,6 @@ url_manual_mapping = ( ("bpy.ops.gpencil.stroke_merge_by_distance*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-merge-by-distance"), ("bpy.ops.node.collapse_hide_unused_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-collapse-hide-unused-toggle"), ("bpy.ops.object.anim_transforms_to_deltas*", "scene_layout/object/editing/apply.html#bpy-ops-object-anim-transforms-to-deltas"), - ("bpy.ops.object.convert_proxy_to_override*", "files/linked_libraries/library_overrides.html#bpy-ops-object-convert-proxy-to-override"), ("bpy.ops.object.modifier_copy_to_selected*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy-to-selected"), ("bpy.ops.preferences.app_template_install*", "advanced/app_templates.html#bpy-ops-preferences-app-template-install"), ("bpy.types.actionposemarkers.active_index*", "animation/armatures/properties/pose_library.html#bpy-types-actionposemarkers-active-index"), @@ -1860,6 +1859,7 @@ url_manual_mapping = ( ("bpy.types.shadernodeemission*", "render/shader_nodes/shader/emission.html#bpy-types-shadernodeemission"), ("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.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"), ("bpy.types.shadernodemaprange*", "render/shader_nodes/converter/map_range.html#bpy-types-shadernodemaprange"), ("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"), @@ -2139,7 +2139,6 @@ url_manual_mapping = ( ("bpy.ops.object.origin_set*", "scene_layout/object/origin.html#bpy-ops-object-origin-set"), ("bpy.ops.object.parent_set*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-set"), ("bpy.ops.object.pointcloud*", "modeling/point_cloud.html#bpy-ops-object-pointcloud"), - ("bpy.ops.object.proxy_make*", "files/linked_libraries/library_proxies.html#bpy-ops-object-proxy-make"), ("bpy.ops.object.select_all*", "scene_layout/object/selecting.html#bpy-ops-object-select-all"), ("bpy.ops.object.shade_flat*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-flat"), ("bpy.ops.pose.group_assign*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-assign"), diff --git a/release/scripts/modules/rna_xml.py b/release/scripts/modules/rna_xml.py index 7f7b273c42b..aa8841c5efe 100644 --- a/release/scripts/modules/rna_xml.py +++ b/release/scripts/modules/rna_xml.py @@ -298,7 +298,7 @@ def xml2rna( del value_xml_split tp_name = 'ARRAY' -# print(" %s.%s (%s) --- %s" % (type(value).__name__, attr, tp_name, subvalue_type)) + # print(" %s.%s (%s) --- %s" % (type(value).__name__, attr, tp_name, subvalue_type)) try: setattr(value, attr, value_xml_coerce) except ValueError: @@ -340,7 +340,6 @@ def xml2rna( else: # print(elems) - if len(elems) == 1: # sub node named by its type child_xml_real, = elems @@ -376,7 +375,6 @@ def _get_context_val(context, path): def xml_file_run(context, filepath, rna_map): - import xml.dom.minidom xml_nodes = xml.dom.minidom.parse(filepath) @@ -391,27 +389,25 @@ def xml_file_run(context, filepath, rna_map): value = _get_context_val(context, rna_path) if value is not Ellipsis and value is not None: - print(" loading XML: %r -> %r" % (filepath, rna_path)) + # print(" loading XML: %r -> %r" % (filepath, rna_path)) xml2rna(xml_node, root_rna=value) def xml_file_write(context, filepath, rna_map, *, skip_typemap=None): - - file = open(filepath, "w", encoding="utf-8") - fw = file.write - - fw("<bpy>\n") - - for rna_path, _xml_tag in rna_map: - # xml_tag is ignored, we get this from the rna - value = _get_context_val(context, rna_path) - rna2xml(fw, + with open(filepath, "w", encoding="utf-8") as file: + fw = file.write + fw("<bpy>\n") + + for rna_path, _xml_tag in rna_map: + # xml_tag is ignored, we get this from the rna + value = _get_context_val(context, rna_path) + rna2xml( + fw=fw, root_rna=value, method='ATTR', root_ident=" ", ident_val=" ", skip_typemap=skip_typemap, - ) + ) - fw("</bpy>\n") - file.close() + fw("</bpy>\n") diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index 1ac7626f926..417a3c39310 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -318,7 +318,8 @@ def load(): use_v3d_tab_menu=kc_prefs.use_v3d_tab_menu, use_v3d_shade_ex_pie=kc_prefs.use_v3d_shade_ex_pie, use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'), - use_fallback_tool=(True if is_select_left else (kc_prefs.rmb_action == 'FALLBACK_TOOL')), + use_fallback_tool=True, + use_fallback_tool_rmb=(False if is_select_left else kc_prefs.rmb_action == 'FALLBACK_TOOL'), use_alt_tool_or_cursor=( (not use_mouse_emulate_3_button) and (kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index a006cd787ea..5367d9b33f9 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -16,6 +16,16 @@ # # ##### END GPL LICENSE BLOCK ##### +# ------------------------------------------------------------------------------ +# Developer Notes +# +# - This script should run without Blender (no references to the `bpy` module for example). +# - All configuration must be passed into the `generate_keymaps` function (via `Params`). +# - Supporting some combinations of options is becoming increasingly complex, +# especially `Params.select_mouse` & `Params.use_fallback_tool_rmb`. +# To ensure changes don't unintentionally break other configurations, see: +# `source/tools/utils/blender_keyconfig_export_permutations.py --help` +# # ------------------------------------------------------------------------------ # Configurable Parameters @@ -48,6 +58,8 @@ class Params: "use_gizmo_drag", # Use the fallback tool instead of tweak for RMB select. "use_fallback_tool", + # Only set for RMB select. + "use_fallback_tool_rmb", # Use pie menu for tab by default (swap 'Tab/Ctrl-Tab'). "use_v3d_tab_menu", # Use extended pie menu for shading. @@ -65,15 +77,16 @@ class Params: "v3d_tilde_action", # Alt-MMB axis switching 'RELATIVE' or 'ABSOLUTE' axis switching. "v3d_alt_mmb_drag_action", - + # File selector actions on single click. "use_file_single_click", + # Convenience variables: # (derived from other settings). # - # This case needs to be checked often, - # Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHTMOUSE' else False)`. - "use_fallback_tool_rmb", - # Shorthand for: `('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value)`. + # The fallback tool is activated on the same button as selection. + # Shorthand for: `(True if (select_mouse == 'LEFT') else self.use_fallback_tool_rmb)` + "use_fallback_tool_select_mouse", + # Shorthand for: `('CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value)`. "select_mouse_value_fallback", # Shorthand for: `{"type": params.select_tweak, "value": 'ANY'}`. "select_tweak_event", @@ -103,6 +116,7 @@ class Params: use_select_all_toggle=False, use_gizmo_drag=True, use_fallback_tool=False, + use_fallback_tool_rmb=False, use_v3d_tab_menu=False, use_v3d_shade_ex_pie=False, use_v3d_mmb_pan=False, @@ -146,7 +160,6 @@ class Params: self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'} self.cursor_tweak_event = None - self.use_fallback_tool = use_fallback_tool self.tool_modifier = {} else: # Left mouse select uses Click event for selection. This is a little @@ -169,7 +182,6 @@ class Params: self.cursor_set_event = {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True} self.cursor_tweak_event = {"type": 'EVT_TWEAK_R', "value": 'ANY', "shift": True} - self.use_fallback_tool = True # Use the "tool" functionality for LMB select. if use_alt_tool_or_cursor: @@ -197,8 +209,11 @@ class Params: self.use_file_single_click = use_file_single_click + self.use_fallback_tool = use_fallback_tool + self.use_fallback_tool_rmb = use_fallback_tool_rmb + # Convenience variables: - self.use_fallback_tool_rmb = self.use_fallback_tool if select_mouse == 'RIGHT' else False + self.use_fallback_tool_select_mouse = True if (select_mouse == 'LEFT') else self.use_fallback_tool_rmb self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value self.select_tweak_event = {"type": self.select_tweak, "value": 'ANY'} self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS' @@ -1149,11 +1164,7 @@ def km_uv_editor(params): items.extend([ # Selection modes. *_template_items_uv_select_mode(params), - *_template_uv_select( - type=params.select_mouse, - value=('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value), - legacy=params.legacy, - ), + *_template_uv_select(type=params.select_mouse, value=params.select_mouse_value_fallback, legacy=params.legacy), ("uv.mark_seam", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), ("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None), @@ -6283,7 +6294,8 @@ def km_image_editor_tool_uv_select_box(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. - **(params.select_tweak_event if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event))), ]}, ) @@ -6295,7 +6307,8 @@ def km_image_editor_tool_uv_select_circle(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_circle", - **(params.select_tweak_event if fallback else {"type": params.tool_mouse, "value": 'PRESS'}), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + {"type": params.tool_mouse, "value": 'PRESS'}), properties=[("wait_for_input", False)])), # No selection fallback since this operates on press. ]}, @@ -6310,7 +6323,8 @@ def km_image_editor_tool_uv_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_lasso", - **(params.select_tweak_event if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event))), ]}, ) @@ -6402,7 +6416,8 @@ def km_node_editor_tool_select_box(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "node.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. - **(params.select_tweak_event if fallback else params.tool_tweak_event), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event), properties=[("tweak", True)], )), ]}, @@ -6415,7 +6430,9 @@ def km_node_editor_tool_select_lasso(params, *, fallback): {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( - "node.select_lasso", **(params.select_tweak_event if fallback else params.tool_tweak_event), + "node.select_lasso", + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event), properties=[("tweak", True)])) ]}, ) @@ -6430,7 +6447,7 @@ def km_node_editor_tool_select_circle(params, *, fallback): "node.select_circle", # Why circle select should be used on tweak? # So that RMB or Shift-RMB is still able to set an element as active. - type=params.select_tweak if fallback else params.tool_mouse, + type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, value='ANY' if fallback else 'PRESS', properties=[("wait_for_input", False)])), ]}, @@ -6484,7 +6501,8 @@ def km_3d_view_tool_select_box(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. - **(params.select_tweak_event if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event))), ]}, ) @@ -6498,7 +6516,7 @@ def km_3d_view_tool_select_circle(params, *, fallback): "view3d.select_circle", # Why circle select should be used on tweak? # So that RMB or Shift-RMB is still able to set an element as active. - type=params.select_tweak if fallback else params.tool_mouse, + type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, value='ANY' if fallback else 'PRESS', properties=[("wait_for_input", False)])), ]}, @@ -6512,7 +6530,8 @@ def km_3d_view_tool_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_lasso", - **(params.select_tweak_event if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event))), ]} ) @@ -7394,7 +7413,8 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "gpencil.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. - **(params.select_tweak_event if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event))), ]}, ) @@ -7408,7 +7428,7 @@ def km_3d_view_tool_edit_gpencil_select_circle(params, *, fallback): "gpencil.select_circle", # Why circle select should be used on tweak? # So that RMB or Shift-RMB is still able to set an element as active. - type=params.select_tweak if fallback else params.tool_mouse, + type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, value='ANY' if fallback else 'PRESS', properties=[("wait_for_input", False)])), ]}, @@ -7422,7 +7442,8 @@ def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "gpencil.select_lasso", - **(params.select_tweak_event if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event))), ]} ) @@ -7573,7 +7594,8 @@ def km_sequencer_editor_tool_generic_select_box(params, *, fallback): # Don't use `tool_maybe_tweak_event`, see comment for this slot. *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "sequencer.select_box", - **(params.select_tweak_event if fallback else params.tool_tweak_event), + **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else + params.tool_tweak_event), properties=[("tweak", params.select_mouse == 'LEFTMOUSE')])), # RMB select can already set the frame, match the tweak tool. diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index ce8bfa3b058..bb85ad8ca50 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -2961,93 +2961,75 @@ class WM_MT_splash_quick_setup(Menu): bl_label = "Quick Setup" def draw(self, context): - wm = context.window_manager - # prefs = context.preferences - layout = self.layout - layout.operator_context = 'EXEC_DEFAULT' layout.label(text="Quick Setup") - split = layout.split(factor=0.25) + split = layout.split(factor=0.14) # Left margin. split.label() - split = split.split(factor=2.0 / 3.0) + split = split.split(factor=0.73) # Content width. col = split.column() + col.use_property_split = True + col.use_property_decorate = False + + # Languages. if bpy.app.build_options.international: - sub = col.split(factor=0.35) - row = sub.row() - row.alignment = 'RIGHT' - row.label(text="Language") prefs = context.preferences - sub.prop(prefs.view, "language", text="") + col.prop(prefs.view, "language") + col.separator() - col.separator() + # Shortcuts. + wm = context.window_manager + kc = wm.keyconfigs.active + kc_prefs = kc.preferences - sub = col.split(factor=0.35) - row = sub.row() - row.alignment = 'RIGHT' - row.label(text="Shortcuts") - text = bpy.path.display_name(wm.keyconfigs.active.name) + sub = col.column(heading="Shortcuts") + text = bpy.path.display_name(kc.name) if not text: text = "Blender" sub.menu("USERPREF_MT_keyconfigs", text=text) - kc = wm.keyconfigs.active - kc_prefs = kc.preferences has_select_mouse = hasattr(kc_prefs, "select_mouse") if has_select_mouse: - sub = col.split(factor=0.35) - row = sub.row() - row.alignment = 'RIGHT' - row.label(text="Select With") - sub.row().prop(kc_prefs, "select_mouse", expand=True) - has_select_mouse = True + col.row().prop(kc_prefs, "select_mouse", text="Select With", expand=True) has_spacebar_action = hasattr(kc_prefs, "spacebar_action") if has_spacebar_action: - sub = col.split(factor=0.35) - row = sub.row() - row.alignment = 'RIGHT' - row.label(text="Spacebar") - sub.row().prop(kc_prefs, "spacebar_action", expand=True) - has_select_mouse = True + col.row().prop(kc_prefs, "spacebar_action", text="Spacebar") col.separator() - sub = col.split(factor=0.35) - row = sub.row() - row.alignment = 'RIGHT' - row.label(text="Theme") + # Themes. + sub = col.column(heading="Theme") label = bpy.types.USERPREF_MT_interface_theme_presets.bl_label if label == "Presets": label = "Blender Dark" sub.menu("USERPREF_MT_interface_theme_presets", text=label) - # Keep height constant + # Keep height constant. if not has_select_mouse: col.label() if not has_spacebar_action: col.label() - layout.label() + layout.separator(factor=2.0) - row = layout.row() + # Save settings buttons. + sub = layout.row() - sub = row.row() old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version() if bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version: - sub.operator("preferences.copy_prev", text=iface_("Load %d.%d Settings", "Operator") % old_version) + sub.operator("preferences.copy_prev", text="Load %d.%d Settings" % old_version) sub.operator("wm.save_userpref", text="Save New Settings") else: sub.label() sub.label() sub.operator("wm.save_userpref", text="Next") - layout.separator() - layout.separator() + layout.separator(factor=2.4) class WM_MT_splash(Menu): diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 1fb40ad8bc8..7c254596683 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -33,9 +33,9 @@ _modules = [ "properties_data_bone", "properties_data_camera", "properties_data_curve", + "properties_data_curves", "properties_data_empty", "properties_data_gpencil", - "properties_data_hair", "properties_data_light", "properties_data_lattice", "properties_data_mesh", diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index 22f3d1a9c50..2a6bd53bff5 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -149,7 +149,7 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel): col.operator("pose.group_move", icon='TRIA_DOWN', text="").direction = 'DOWN' split = layout.split() - split.active = (ob.proxy is None) + split.active = True col = split.column() col.prop(group, "color_set") diff --git a/release/scripts/startup/bl_ui/properties_data_hair.py b/release/scripts/startup/bl_ui/properties_data_curves.py index 7f95fad9a9e..0b4bf0283ed 100644 --- a/release/scripts/startup/bl_ui/properties_data_hair.py +++ b/release/scripts/startup/bl_ui/properties_data_curves.py @@ -30,10 +30,10 @@ class DataButtonsPanel: @classmethod def poll(cls, context): engine = context.scene.render.engine - return hasattr(context, 'hair') and context.hair and (engine in cls.COMPAT_ENGINES) + return hasattr(context, 'curves') and context.curves and (engine in cls.COMPAT_ENGINES) -class DATA_PT_context_hair(DataButtonsPanel, Panel): +class DATA_PT_context_curves(DataButtonsPanel, Panel): bl_label = "" bl_options = {'HIDE_HEADER'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -42,21 +42,21 @@ class DATA_PT_context_hair(DataButtonsPanel, Panel): layout = self.layout ob = context.object - hair = context.hair + curves = context.curves space = context.space_data if ob: layout.template_ID(ob, "data") - elif hair: + elif curves: layout.template_ID(space, "pin_id") -class HAIR_MT_add_attribute(Menu): +class CURVES_MT_add_attribute(Menu): bl_label = "Add Attribute" @staticmethod - def add_standard_attribute(layout, hair, name, data_type, domain): - exists = hair.attributes.get(name) is not None + def add_standard_attribute(layout, curves, name, data_type, domain): + exists = curves.attributes.get(name) is not None col = layout.column() col.enabled = not exists @@ -69,10 +69,10 @@ class HAIR_MT_add_attribute(Menu): def draw(self, context): layout = self.layout - hair = context.hair + curves = context.curves - self.add_standard_attribute(layout, hair, 'Radius', 'FLOAT', 'POINT') - self.add_standard_attribute(layout, hair, 'Color', 'FLOAT_COLOR', 'POINT') + self.add_standard_attribute(layout, curves, 'radius', 'FLOAT', 'POINT') + self.add_standard_attribute(layout, curves, 'color', 'FLOAT_COLOR', 'POINT') layout.separator() @@ -80,7 +80,7 @@ class HAIR_MT_add_attribute(Menu): layout.operator("geometry.attribute_add", text="Custom...") -class HAIR_UL_attributes(UIList): +class CURVES_UL_attributes(UIList): def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index): data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type] domain = attribute.bl_rna.properties['domain'].enum_items[attribute.domain] @@ -96,44 +96,44 @@ class HAIR_UL_attributes(UIList): sub.label(text=data_type.name) -class DATA_PT_hair_attributes(DataButtonsPanel, Panel): +class DATA_PT_CURVES_attributes(DataButtonsPanel, Panel): bl_label = "Attributes" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} def draw(self, context): - hair = context.hair + curves = context.curves layout = self.layout row = layout.row() col = row.column() col.template_list( - "HAIR_UL_attributes", + "CURVES_UL_attributes", "attributes", - hair, + curves, "attributes", - hair.attributes, + curves.attributes, "active_index", rows=3, ) col = row.column(align=True) - col.menu("HAIR_MT_add_attribute", icon='ADD', text="") + col.menu("CURVES_MT_add_attribute", icon='ADD', text="") col.operator("geometry.attribute_remove", icon='REMOVE', text="") -class DATA_PT_custom_props_hair(DataButtonsPanel, PropertyPanel, Panel): +class DATA_PT_custom_props_curves(DataButtonsPanel, PropertyPanel, Panel): COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} _context_path = "object.data" - _property_type = bpy.types.Hair if hasattr(bpy.types, "Hair") else None + _property_type = bpy.types.Curves if hasattr(bpy.types, "Curves") else None classes = ( - DATA_PT_context_hair, - DATA_PT_hair_attributes, - DATA_PT_custom_props_hair, - HAIR_MT_add_attribute, - HAIR_UL_attributes, + DATA_PT_context_curves, + DATA_PT_CURVES_attributes, + DATA_PT_custom_props_curves, + CURVES_MT_add_attribute, + CURVES_UL_attributes, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 18dfa4da6c6..99edbe647e2 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -127,8 +127,8 @@ class DopesheetFilterPopoverBase: flow.prop(dopesheet, "show_lattices", text="Lattices") if bpy.data.metaballs: flow.prop(dopesheet, "show_metaballs", text="Metaballs") - if hasattr(bpy.data, "hairs") and bpy.data.hairs: - flow.prop(dopesheet, "show_hairs", text="Hairs") + if hasattr(bpy.data, "hair_curves") and bpy.data.hair_curves: + flow.prop(dopesheet, "show_hair_curves", text="Hair Curves") if hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds: flow.prop(dopesheet, "show_pointclouds", text="Point Clouds") if bpy.data.volumes: diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 731a220848e..50005a8f7f0 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -761,6 +761,15 @@ class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="") col.separator() col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="") + col.menu("ASSETBROWSER_MT_metadata_preview_menu", icon='DOWNARROW_HLT', text="") + + +class ASSETBROWSER_MT_metadata_preview_menu(bpy.types.Menu): + bl_label = "Preview" + + def draw(self, context): + layout = self.layout + layout.operator("ed.lib_id_generate_preview_from_object", text="Render Active Object") class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel): @@ -840,6 +849,7 @@ classes = ( ASSETBROWSER_MT_view, ASSETBROWSER_MT_select, ASSETBROWSER_MT_edit, + ASSETBROWSER_MT_metadata_preview_menu, ASSETBROWSER_PT_metadata, ASSETBROWSER_PT_metadata_preview, ASSETBROWSER_PT_metadata_tags, diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index a9f2b9e9a36..db0020b7846 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -335,6 +335,7 @@ class GRAPH_MT_key_snap(Menu): layout.operator("graph.snap", text="Selection to Nearest Second").type = 'NEAREST_SECOND' layout.operator("graph.snap", text="Selection to Nearest Marker").type = 'NEAREST_MARKER' layout.operator("graph.snap", text="Flatten Handles").type = 'HORIZONTAL' + layout.operator("graph.equalize_handles", text="Equalize Handles").side = 'BOTH' layout.separator() layout.operator("graph.frame_jump", text="Cursor to Selection") layout.operator("graph.snap_cursor_value", text="Cursor Value to Selection") diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index 2db2b11a5f9..78c312fd03d 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -291,6 +291,9 @@ class NLA_MT_context_menu(Menu): layout.separator() + props = layout.operator("wm.call_panel", text="Rename...") + props.name = "TOPBAR_PT_name" + props.keep_open = False layout.operator("nla.duplicate", text="Duplicate").linked = False layout.operator("nla.duplicate", text="Linked Duplicate").linked = True diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index d85538a37e0..6cc80c088e0 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -448,7 +448,7 @@ class OUTLINER_PT_filter(Panel): if ( bpy.data.curves or bpy.data.metaballs or - (hasattr(bpy.data, "hairs") and bpy.data.hairs) or + (hasattr(bpy.data, "hair_curves") and bpy.data.hair_curves) or (hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds) or bpy.data.volumes or bpy.data.lightprobes or diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 6035170f9df..9c77529229b 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -2028,6 +2028,9 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): layout.active = not strip.mute col = layout.column(align=True) + col.prop(strip.transform, "filter", text="Filter") + + col = layout.column(align=True) col.prop(strip.transform, "offset_x", text="Position X") col.prop(strip.transform, "offset_y", text="Y") diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 99abc60db6f..ce854155b88 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -481,7 +481,7 @@ class TOPBAR_MT_file_export(Menu): bl_owner_use_filter = False def draw(self, _context): - self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj) - New") + self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj)") if bpy.app.build_options.collada: self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)") @@ -832,6 +832,14 @@ class TOPBAR_PT_name(Panel): row = row_with_icon(layout, 'NODE') row.prop(item, "label", text="") found = True + elif space_type == 'NLA_EDITOR': + layout.label(text="NLA Strip Name") + item = next( + (strip for strip in context.selected_nla_strips if strip.active), None) + if item: + row = row_with_icon(layout, 'NLA') + row.prop(item, "name", text="") + found = True else: if mode == 'POSE' or (mode == 'WEIGHT_PAINT' and context.pose_object): layout.label(text="Bone Name") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 0548486c786..26b4229690f 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2295,7 +2295,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): def draw(self, context): self._draw_items( context, ( - ({"property": "use_new_hair_type"}, "T68981"), + ({"property": "use_new_curves_type"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), ({"property": "use_full_frame_compositor"}, "T88150"), ), @@ -2316,7 +2316,6 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): context, ( ({"property": "use_undo_legacy"}, "T60695"), ({"property": "override_auto_resync"}, "T83811"), - ({"property": "proxy_to_override_auto_conversion"}, "T91671"), ({"property": "use_cycles_debug"}, None), ({"property": "use_geometry_nodes_legacy"}, "T91274"), ({"property": "show_asset_debug_info"}, None), diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 5eca606216e..ea7a1885369 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2148,8 +2148,8 @@ class VIEW3D_MT_add(Menu): layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE') layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META') layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT') - if context.preferences.experimental.use_new_hair_type: - layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR') + if context.preferences.experimental.use_new_curves_type: + layout.operator("object.hair_curves_add", text="Hair Curves", icon='OUTLINER_OB_CURVES') if context.preferences.experimental.use_new_point_cloud_type: layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD') layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME') @@ -2228,8 +2228,6 @@ class VIEW3D_MT_object_relations(Menu): layout.operator("object.make_override_library", text="Make Library Override...") - layout.operator("object.convert_proxy_to_override") - layout.operator("object.make_dupli_face") layout.separator() diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 92e5eb91da6..4b48f5f0680 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -181,6 +181,7 @@ def geometry_node_items(context): yield NodeItem("GeometryNodeConvexHull") yield NodeItem("GeometryNodeDeleteGeometry") yield NodeItem("GeometryNodeGeometryToInstance") + yield NodeItem("GeometryNodeMergeByDistance") yield NodeItem("GeometryNodeProximity") yield NodeItem("GeometryNodeJoinGeometry") yield NodeItem("GeometryNodeRaycast") @@ -390,6 +391,7 @@ shader_node_categories = [ NodeItem("ShaderNodeAmbientOcclusion"), NodeItem("ShaderNodeObjectInfo"), NodeItem("ShaderNodeHairInfo"), + NodeItem("ShaderNodePointInfo"), NodeItem("ShaderNodeVolumeInfo"), NodeItem("ShaderNodeParticleInfo"), NodeItem("ShaderNodeCameraData"), @@ -545,6 +547,8 @@ compositor_node_categories = [ NodeItem("CompositorNodeCombYUVA"), NodeItem("CompositorNodeSepYCCA"), NodeItem("CompositorNodeCombYCCA"), + NodeItem("CompositorNodeSeparateXYZ"), + NodeItem("CompositorNodeCombineXYZ"), NodeItem("CompositorNodeSwitchView"), NodeItem("CompositorNodeConvertColorSpace"), ]), diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 6c2cbb5df33..73e16c38cb7 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -27,7 +27,7 @@ if(WITH_CLANG_TIDY AND NOT MSVC) message(WARNING "Currently Clang-Tidy might fail with GCC toolchain, switch to Clang toolchain if that happens") if(COMMAND target_precompile_headers) message(STATUS "Clang-Tidy and GCC precompiled headers are incompatible, disabling precompiled headers") - set(CMAKE_DISABLE_PRECOMPILE_HEADERS On) + set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON) endif() endif() diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index c6112344208..1fcde431d2d 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -48,7 +48,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_modifier_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h - ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_hair_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curves_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt index e2d278255ba..d64e942069d 100644 --- a/source/blender/blendthumb/CMakeLists.txt +++ b/source/blender/blendthumb/CMakeLists.txt @@ -65,6 +65,7 @@ else() ) add_executable(blender-thumbnailer ${SRC} ${SRC_CMD}) + setup_platform_linker_flags(blender-thumbnailer) target_link_libraries(blender-thumbnailer bf_blenlib) target_link_libraries(blender-thumbnailer ${PTHREADS_LIBRARIES}) endif() diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 169107b19cb..638c5b727a6 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -309,7 +309,7 @@ void BLF_thumb_preview(const char *filename, /* blf_default.c */ void BLF_default_dpi(int dpi); -void BLF_default_size(int size); +void BLF_default_size(float size); void BLF_default_set(int fontid); /** * Get default font ID so we can pass it to other functions. diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt index b1453b243c0..5d04823cd0a 100644 --- a/source/blender/blenfont/CMakeLists.txt +++ b/source/blender/blenfont/CMakeLists.txt @@ -54,7 +54,7 @@ set(LIB bf_gpu bf_intern_guardedalloc - ${FREETYPE_LIBRARIES} + ${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES} ) if(WIN32) @@ -63,10 +63,6 @@ if(WIN32) ) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) list(APPEND INC diff --git a/source/blender/blenfont/intern/blf_default.c b/source/blender/blenfont/intern/blf_default.c index 57eeaa6768d..d5a0d514b5f 100644 --- a/source/blender/blenfont/intern/blf_default.c +++ b/source/blender/blenfont/intern/blf_default.c @@ -37,15 +37,15 @@ /* Default size and dpi, for BLF_draw_default. */ static int global_font_default = -1; static int global_font_dpi = 72; -/* Keep in sync with `UI_style_get()->widgetlabel.points` */ -static int global_font_size = 11; +/* Keep in sync with `UI_DEFAULT_TEXT_POINTS` */ +static float global_font_size = 11.0f; void BLF_default_dpi(int dpi) { global_font_dpi = dpi; } -void BLF_default_size(int size) +void BLF_default_size(float size) { global_font_size = size; } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 14d3a208f69..c1410447de6 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -34,7 +34,6 @@ #include FT_FREETYPE_H #include FT_GLYPH_H -#include FT_ADVANCES_H /* For FT_Get_Advance */ #include "MEM_guardedalloc.h" @@ -826,7 +825,10 @@ float blf_font_height(FontBLF *font, float blf_font_fixed_width(FontBLF *font) { - return (float)font->fixed_width; + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + float width = (gc) ? (float)gc->fixed_width : font->size / 2.0f; + blf_glyph_cache_release(font); + return width; } static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, @@ -1318,12 +1320,7 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m void blf_font_free(FontBLF *font) { - BLI_spin_lock(&blf_glyph_cache_mutex); - GlyphCacheBLF *gc; - - while ((gc = BLI_pophead(&font->cache))) { - blf_glyph_cache_free(gc); - } + blf_glyph_cache_clear(font); if (font->kerning_cache) { MEM_freeN(font->kerning_cache); @@ -1337,8 +1334,6 @@ void blf_font_free(FontBLF *font) MEM_freeN(font->name); } MEM_freeN(font); - - BLI_spin_unlock(&blf_glyph_cache_mutex); } /** \} */ @@ -1347,51 +1342,25 @@ void blf_font_free(FontBLF *font) /** \name Font Configure * \{ */ -void blf_font_size(FontBLF *font, float size, unsigned int dpi) +bool blf_font_size(FontBLF *font, float size, unsigned int dpi) { - blf_glyph_cache_acquire(font); - /* FreeType uses fixed-point integers in 64ths. */ FT_F26Dot6 ft_size = lroundf(size * 64.0f); - /* Adjust our size to be on even 64ths. */ + /* Adjust our new size to be on even 64ths. */ size = (float)ft_size / 64.0f; - GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi); - if (gc && (font->size == size && font->dpi == dpi)) { - /* Optimization: do not call FT_Set_Char_Size if size did not change. */ - } - else { - const FT_Error err = FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi); - if (err) { - /* FIXME: here we can go through the fixed size and choice a close one */ - printf("The current font don't support the size, %f and dpi, %u\n", size, dpi); - } - else { + if (font->size != size || font->dpi != dpi) { + if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == 0) { font->size = size; font->dpi = dpi; - if (gc == NULL) { - blf_glyph_cache_new(font); - } + } + else { + printf("The current font does not support the size, %f and dpi, %u\n", size, dpi); + return false; } } - blf_glyph_cache_release(font); - - /* Set fixed-width size for monospaced output. */ - FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); - if (gindex) { - FT_Fixed advance = 0; - FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); - /* Use CSS 'ch unit' width, advance of zero character. */ - font->fixed_width = (int)(advance >> 16); - } - else { - /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ - font->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); - } - if (font->fixed_width < 1) { - font->fixed_width = 1; - } + return true; } /** \} */ diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 4f25f99b65c..bcd9e1fb3a2 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -34,6 +34,7 @@ #include FT_GLYPH_H #include FT_OUTLINE_H #include FT_BITMAP_H +#include FT_ADVANCES_H /* For FT_Get_Advance. */ #include "MEM_guardedalloc.h" @@ -73,7 +74,7 @@ static FT_Fixed to_16dot16(double val) /** \name Glyph Cache * \{ */ -GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi) +static GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi) { GlyphCacheBLF *gc = (GlyphCacheBLF *)font->cache.first; while (gc) { @@ -86,7 +87,7 @@ GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi) return NULL; } -GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) +static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) { GlyphCacheBLF *gc = (GlyphCacheBLF *)MEM_callocN(sizeof(GlyphCacheBLF), "blf_glyph_cache_new"); @@ -100,6 +101,22 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table)); memset(gc->bucket, 0, sizeof(gc->bucket)); + /* Determine ideal fixed-width size for monospaced output. */ + FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); + if (gindex) { + FT_Fixed advance = 0; + FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); + /* Use CSS 'ch unit' width, advance of zero character. */ + gc->fixed_width = (int)(advance >> 16); + } + else { + /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ + gc->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); + } + if (gc->fixed_width < 1) { + gc->fixed_width = 1; + } + BLI_addhead(&font->cache, gc); return gc; } @@ -122,20 +139,7 @@ void blf_glyph_cache_release(FontBLF *font) BLI_spin_unlock(font->glyph_cache_mutex); } -void blf_glyph_cache_clear(FontBLF *font) -{ - GlyphCacheBLF *gc; - - BLI_spin_lock(font->glyph_cache_mutex); - - while ((gc = BLI_pophead(&font->cache))) { - blf_glyph_cache_free(gc); - } - - BLI_spin_unlock(font->glyph_cache_mutex); -} - -void blf_glyph_cache_free(GlyphCacheBLF *gc) +static void blf_glyph_cache_free(GlyphCacheBLF *gc) { GlyphBLF *g; for (uint i = 0; i < ARRAY_SIZE(gc->bucket); i++) { @@ -152,6 +156,19 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc) MEM_freeN(gc); } +void blf_glyph_cache_clear(FontBLF *font) +{ + GlyphCacheBLF *gc; + + BLI_spin_lock(font->glyph_cache_mutex); + + while ((gc = BLI_pophead(&font->cache))) { + blf_glyph_cache_free(gc); + } + + BLI_spin_unlock(font->glyph_cache_mutex); +} + /** * Try to find a glyph in cache. * diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 4e36f522981..d0bb3385e2c 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -56,7 +56,11 @@ struct FontBLF *blf_font_new(const char *name, const char *filename); struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size); void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size); -void blf_font_size(struct FontBLF *font, float size, unsigned int dpi); +/** + * Change font's output size. Returns true if successful in changing the size. + */ +bool blf_font_size(struct FontBLF *font, float size, unsigned int dpi); + void blf_font_draw(struct FontBLF *font, const char *str, size_t str_len, @@ -65,10 +69,7 @@ void blf_font_draw__wrap(struct FontBLF *font, const char *str, size_t str_len, struct ResultBLF *r_info); -void blf_font_draw_ascii(struct FontBLF *font, - const char *str, - size_t str_len, - struct ResultBLF *r_info); + /** * Use fixed column width, but an utf8 character may occupy multiple columns. */ @@ -137,18 +138,9 @@ int blf_font_count_missing_chars(struct FontBLF *font, void blf_font_free(struct FontBLF *font); -/** - * Find a glyph cache that matches a size, DPI & styles. - */ -struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font, float size, unsigned int dpi); -/** - * Create a new glyph cache for the current size, DPI & styles. - */ -struct GlyphCacheBLF *blf_glyph_cache_new(struct FontBLF *font); struct GlyphCacheBLF *blf_glyph_cache_acquire(struct FontBLF *font); void blf_glyph_cache_release(struct FontBLF *font); void blf_glyph_cache_clear(struct FontBLF *font); -void blf_glyph_cache_free(struct GlyphCacheBLF *gc); /** * Create (or load from cache) a fully-rendered bitmap glyph. diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 46156edbb1f..c96febd71ae 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -73,6 +73,9 @@ typedef struct GlyphCacheBLF { bool bold; bool italic; + /* Column width when printing monospaced. */ + int fixed_width; + /* and the glyphs. */ ListBase bucket[257]; @@ -207,9 +210,6 @@ typedef struct FontBLF { /* font size. */ float size; - /* Column width when printing monospaced. */ - int fixed_width; - /* max texture size. */ int tex_size_max; diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index 06bbd0cf521..f666976547e 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -61,7 +61,6 @@ void BLF_thumb_preview(const char *filename, int font_shrink = 4; FontBLF *font; - GlyphCacheBLF *gc; /* Create a new blender font obj and fill it with default values */ font = blf_font_new("thumb_font", filename); @@ -90,10 +89,8 @@ void BLF_thumb_preview(const char *filename, const size_t draw_str_i18n_len = strlen(draw_str_i18n); int draw_str_i18n_nbr = 0; - blf_font_size(font, (float)MAX2(font_size_min, font_size_curr), dpi); - gc = blf_glyph_cache_find(font, font->size, font->dpi); - /* There will be no matching glyph cache if blf_font_size() failed to set font size. */ - if (!gc) { + CLAMP_MIN(font_size_curr, font_size_min); + if (!blf_font_size(font, (float)font_size_curr, dpi)) { break; } diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 269d90b868e..4526bc38a70 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -104,14 +104,6 @@ typedef enum DerivedMeshType { DM_TYPE_CCGDM, } DerivedMeshType; -typedef enum DMDirtyFlag { - /* dm has valid tessellated faces, but tessellated CDDATA need to be updated. */ - DM_DIRTY_TESS_CDLAYERS = 1 << 0, - - /* check this with modifier dependsOnNormals callback to see if normals need recalculation */ - DM_DIRTY_NORMALS = 1 << 1, -} DMDirtyFlag; - typedef struct DerivedMesh DerivedMesh; struct DerivedMesh { /** Private DerivedMesh data, only for internal DerivedMesh use */ @@ -120,7 +112,6 @@ struct DerivedMesh { int needsFree; /* checked on ->release, is set to 0 for cached results */ int deformedOnly; /* set by modifier stack if only deformed from original */ DerivedMeshType type; - DMDirtyFlag dirty; /** * \warning Typical access is done via #getLoopTriArray, #getNumLoopTri. @@ -139,9 +130,6 @@ struct DerivedMesh { short tangent_mask; /* which tangent layers are calculated */ - /** Calculate vert and face normals */ - void (*calcNormals)(DerivedMesh *dm); - /** Loop tessellation cache (WARNING! Only call inside threading-protected code!) */ void (*recalcLoopTri)(DerivedMesh *dm); /** accessor functions */ @@ -164,7 +152,6 @@ struct DerivedMesh { */ struct MVert *(*getVertArray)(DerivedMesh *dm); struct MEdge *(*getEdgeArray)(DerivedMesh *dm); - struct MFace *(*getTessFaceArray)(DerivedMesh *dm); struct MLoop *(*getLoopArray)(DerivedMesh *dm); struct MPoly *(*getPolyArray)(DerivedMesh *dm); @@ -173,7 +160,6 @@ struct DerivedMesh { */ void (*copyVertArray)(DerivedMesh *dm, struct MVert *r_vert); void (*copyEdgeArray)(DerivedMesh *dm, struct MEdge *r_edge); - void (*copyTessFaceArray)(DerivedMesh *dm, struct MFace *r_face); void (*copyLoopArray)(DerivedMesh *dm, struct MLoop *r_loop); void (*copyPolyArray)(DerivedMesh *dm, struct MPoly *r_poly); @@ -182,37 +168,18 @@ struct DerivedMesh { */ struct MVert *(*dupVertArray)(DerivedMesh *dm); struct MEdge *(*dupEdgeArray)(DerivedMesh *dm); - struct MFace *(*dupTessFaceArray)(DerivedMesh *dm); struct MLoop *(*dupLoopArray)(DerivedMesh *dm); struct MPoly *(*dupPolyArray)(DerivedMesh *dm); - /** Return a pointer to a single element of vert/edge/face custom data - * from the derived mesh (this gives a pointer to the actual data, not - * a copy) - */ - void *(*getVertData)(DerivedMesh *dm, int index, int type); - void *(*getEdgeData)(DerivedMesh *dm, int index, int type); - void *(*getTessFaceData)(DerivedMesh *dm, int index, int type); - void *(*getPolyData)(DerivedMesh *dm, int index, int type); - /** Return a pointer to the entire array of vert/edge/face custom data * from the derived mesh (this gives a pointer to the actual data, not * a copy) */ void *(*getVertDataArray)(DerivedMesh *dm, int type); void *(*getEdgeDataArray)(DerivedMesh *dm, int type); - void *(*getTessFaceDataArray)(DerivedMesh *dm, int type); void *(*getLoopDataArray)(DerivedMesh *dm, int type); void *(*getPolyDataArray)(DerivedMesh *dm, int type); - /** Retrieves the base CustomData structures for - * verts/edges/tessfaces/loops/faces. */ - CustomData *(*getVertDataLayout)(DerivedMesh *dm); - CustomData *(*getEdgeDataLayout)(DerivedMesh *dm); - CustomData *(*getTessFaceDataLayout)(DerivedMesh *dm); - CustomData *(*getLoopDataLayout)(DerivedMesh *dm); - CustomData *(*getPolyDataLayout)(DerivedMesh *dm); - /** Optional grid access for subsurf */ int (*getNumGrids)(DerivedMesh *dm); int (*getGridSize)(DerivedMesh *dm); @@ -231,11 +198,6 @@ struct DerivedMesh { /** Get smooth vertex normal, undefined if index is not valid */ void (*getVertNo)(DerivedMesh *dm, int index, float r_no[3]); - void (*getPolyNo)(DerivedMesh *dm, int index, float r_no[3]); - - /** Get a map of vertices to faces - */ - const struct MeshElemMap *(*getPolyMap)(struct Object *ob, DerivedMesh *dm); /** Release reference to the DerivedMesh. This function decides internally * if the DerivedMesh will be freed, or cached for later use. */ @@ -265,15 +227,6 @@ void DM_init(DerivedMesh *dm, * Utility function to initialize a DerivedMesh for the desired number * of vertices, edges and faces, with a layer setup copied from source */ -void DM_from_template_ex(DerivedMesh *dm, - DerivedMesh *source, - DerivedMeshType type, - int numVerts, - int numEdges, - int numTessFaces, - int numLoops, - int numPolys, - const struct CustomData_MeshMasks *mask); void DM_from_template(DerivedMesh *dm, DerivedMesh *source, DerivedMeshType type, @@ -303,26 +256,9 @@ void DM_set_only_copy(DerivedMesh *dm, const struct CustomData_MeshMasks *mask); void DM_add_vert_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); void DM_add_edge_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); -void DM_add_tessface_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); -void DM_add_loop_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); void DM_add_poly_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); /* -------------------------------------------------------------------- */ -/** \name Custom Data Access Functions - * - * \return pointer to data from first layer which matches type - * if they return NULL for valid indices, data doesn't exist. - * \note these return pointers - any change modifies the internals of the mesh. - * \{ */ - -void *DM_get_vert_data(struct DerivedMesh *dm, int index, int type); -void *DM_get_edge_data(struct DerivedMesh *dm, int index, int type); -void *DM_get_tessface_data(struct DerivedMesh *dm, int index, int type); -void *DM_get_poly_data(struct DerivedMesh *dm, int index, int type); - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Custom Data Layer Access Functions * * \return pointer to first data layer which matches type (a flat array) @@ -332,7 +268,6 @@ void *DM_get_poly_data(struct DerivedMesh *dm, int index, int type); void *DM_get_vert_data_layer(struct DerivedMesh *dm, int type); void *DM_get_edge_data_layer(struct DerivedMesh *dm, int type); -void *DM_get_tessface_data_layer(struct DerivedMesh *dm, int type); void *DM_get_poly_data_layer(struct DerivedMesh *dm, int type); void *DM_get_loop_data_layer(struct DerivedMesh *dm, int type); @@ -354,8 +289,6 @@ void DM_copy_vert_data(struct DerivedMesh *source, */ void DM_DupPolys(DerivedMesh *source, DerivedMesh *target); -void DM_ensure_normals(DerivedMesh *dm); - /** * Ensure the array is large enough. * @@ -399,14 +332,8 @@ bool editbmesh_modifier_is_enabled(struct Scene *scene, void makeDerivedMesh(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, - struct BMEditMesh *em, const struct CustomData_MeshMasks *dataMask); -void DM_calc_loop_tangents(DerivedMesh *dm, - bool calc_active_tangent, - const char (*tangent_names)[MAX_NAME], - int tangent_names_len); - #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index 0b09bfd8730..5d1b4baedfd 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -365,7 +365,6 @@ void what_does_obaction(struct Object *ob, char groupname[], const struct AnimationEvalContext *anim_eval_context); -/* for proxy */ void BKE_pose_copy_pchan_result(struct bPoseChannel *pchanto, const struct bPoseChannel *pchanfrom); /** diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 8584ce6f508..12d8135ba55 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -619,14 +619,6 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *object); -void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, struct Object *object); -void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, struct Object *object); -void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, struct Object *object); - -void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph, - struct Object *object, - int pchan_index); - /* -------------------------------------------------------------------- */ /** \name Deform 3D Coordinates by Armature (armature_deform.c) * \{ */ diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 6020da08f51..ff207997e79 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -40,11 +40,11 @@ struct ReportList; /* Attribute.domain */ typedef enum AttributeDomain { ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */ - ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */ + ATTR_DOMAIN_POINT = 0, /* Mesh, Curve or Point Cloud Point */ ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */ ATTR_DOMAIN_FACE = 2, /* Mesh Face */ ATTR_DOMAIN_CORNER = 3, /* Mesh Corner */ - ATTR_DOMAIN_CURVE = 4, /* Hair Curve */ + ATTR_DOMAIN_CURVE = 4, /* A single curve in a larger curve data-block */ ATTR_DOMAIN_INSTANCE = 5, /* Instance */ ATTR_DOMAIN_NUM diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 90f349125c9..bf773cd6d75 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -50,6 +50,9 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f case CD_PROP_BOOL: func(bool()); break; + case CD_PROP_INT8: + func(int8_t()); + break; case CD_PROP_COLOR: func(ColorGeometry4f()); break; @@ -77,6 +80,9 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func else if (cpp_type.is<bool>()) { func(bool()); } + else if (cpp_type.is<int8_t>()) { + func(int8_t()); + } else if (cpp_type.is<ColorGeometry4f>()) { func(ColorGeometry4f()); } @@ -93,6 +99,12 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func template<typename T> T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2); +template<> +inline int8_t mix3(const float3 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2) +{ + return static_cast<int8_t>(weights.x * v0 + weights.y * v1 + weights.z * v2); +} + template<> inline bool mix3(const float3 &weights, const bool &v0, const bool &v1, const bool &v2) { return (weights.x * v0 + weights.y * v1 + weights.z * v2) >= 0.5f; @@ -147,6 +159,11 @@ template<> inline bool mix2(const float factor, const bool &a, const bool &b) return ((1.0f - factor) * a + factor * b) >= 0.5f; } +template<> inline int8_t mix2(const float factor, const int8_t &a, const int8_t &b) +{ + return static_cast<int8_t>((1.0f - factor) * a + factor * b); +} + template<> inline int mix2(const float factor, const int &a, const int &b) { return static_cast<int>((1.0f - factor) * a + factor * b); @@ -364,6 +381,15 @@ template<> struct DefaultMixerStruct<bool> { using type = SimpleMixerWithAccumulationType<bool, float, float_to_bool>; }; +template<> struct DefaultMixerStruct<int8_t> { + static int8_t float_to_int8_t(const float &value) + { + return static_cast<int8_t>(value); + } + /* Store interpolated 8 bit integers in a float temporarily to increase accuracy. */ + using type = SimpleMixerWithAccumulationType<int8_t, float, float_to_int8_t>; +}; + template<typename T> struct DefaultPropatationMixerStruct { /* Use void by default. This can be checked for in `if constexpr` statements. */ using type = typename DefaultMixerStruct<T>::type; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index d0ab8be9a29..fe656166ada 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 301 +#define BLENDER_VERSION 302 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 5 +#define BLENDER_FILE_SUBVERSION 2 /* 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_camera.h b/source/blender/blenkernel/BKE_camera.h index ee78621c11f..550ce4eb601 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -46,7 +46,7 @@ void *BKE_camera_add(struct Main *bmain, const char *name); /** * Get the camera's DOF value, takes the DOF object into account. */ -float BKE_camera_object_dof_distance(struct Object *ob); +float BKE_camera_object_dof_distance(const struct Object *ob); int BKE_camera_sensor_fit(int sensor_fit, float sizex, float sizey); float BKE_camera_sensor_size(int sensor_fit, float sensor_x, float sensor_y); diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 402bffea91d..467d74b17da 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -123,12 +123,21 @@ struct Collection *BKE_collection_object_find(struct Main *bmain, bool BKE_collection_is_empty(const struct Collection *collection); /** - * Add object to collection + * Add object to given collection, ensuring this collection is 'editable' (i.e. local and not a + * liboverride), and finding a suitable parent one otherwise. */ bool BKE_collection_object_add(struct Main *bmain, struct Collection *collection, struct Object *ob); /** + * Same as #BKE_collection_object_add, but unconditionally adds the object to the given collection. + * + * NOTE: required in certain cases, like do-versioning or complex ID management tasks. + */ +bool BKE_collection_object_add_notest(struct Main *bmain, + struct Collection *collection, + struct Object *ob); +/** * Add \a ob_dst to all scene collections that reference object \a ob_src is in. * Used for copying objects. * diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index 55e5cd0a149..2da79e27576 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -278,18 +278,6 @@ bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph, void BKE_constraint_panel_expand(struct bConstraint *con); -/* Constraints + Proxies function prototypes */ - -/** - * Rescue all constraints tagged as being #CONSTRAINT_PROXY_LOCAL - * (i.e. added to bone that's proxy-synced in this file). - */ -void BKE_constraints_proxylocal_extract(struct ListBase *dst, struct ListBase *src); -/** - * Returns if the owner of the constraint is proxy-protected. - */ -bool BKE_constraints_proxylocked_owner(struct Object *ob, struct bPoseChannel *pchan); - /* Constraint Evaluation function prototypes */ /** diff --git a/source/blender/blenkernel/BKE_crazyspace.h b/source/blender/blenkernel/BKE_crazyspace.h index c00e397f552..42d85c70bc1 100644 --- a/source/blender/blenkernel/BKE_crazyspace.h +++ b/source/blender/blenkernel/BKE_crazyspace.h @@ -26,10 +26,12 @@ #ifdef __cplusplus extern "C" { #endif + struct BMEditMesh; struct Depsgraph; struct Mesh; struct Object; +struct ReportList; struct Scene; /* crazyspace.c */ @@ -69,6 +71,31 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, float (**deformmats)[3][3], float (**deformcos)[3]); +/* -------------------------------------------------------------------- */ +/** \name Crazy-Space API + * \{ */ + +void BKE_crazyspace_api_eval(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *object, + struct ReportList *reports); + +void BKE_crazyspace_api_displacement_to_deformed(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement[3], + float r_displacement_deformed[3]); + +void BKE_crazyspace_api_displacement_to_original(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement_deformed[3], + float r_displacement[3]); + +void BKE_crazyspace_api_eval_clear(struct Object *object); + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_curves.h b/source/blender/blenkernel/BKE_curves.h new file mode 100644 index 00000000000..99839b20121 --- /dev/null +++ b/source/blender/blenkernel/BKE_curves.h @@ -0,0 +1,68 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct BoundBox; +struct CustomDataLayer; +struct Depsgraph; +struct Curves; +struct Main; +struct Object; +struct Scene; + +void *BKE_curves_add(struct Main *bmain, const char *name); + +struct BoundBox *BKE_curves_boundbox_get(struct Object *ob); + +void BKE_curves_update_customdata_pointers(struct Curves *curves); +bool BKE_curves_customdata_required(struct Curves *curves, struct CustomDataLayer *layer); + +/* Depsgraph */ + +struct Curves *BKE_curves_new_for_eval(const struct Curves *curves_src, + int totpoint, + int totcurve); +struct Curves *BKE_curves_copy_for_eval(struct Curves *curves_src, bool reference); + +void BKE_curves_data_update(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *object); + +/* Draw Cache */ + +enum { + BKE_CURVES_BATCH_DIRTY_ALL = 0, +}; + +void BKE_curves_batch_cache_dirty_tag(struct Curves *curves, int mode); +void BKE_curves_batch_cache_free(struct Curves *curves); + +extern void (*BKE_curves_batch_cache_dirty_tag_cb)(struct Curves *curves, int mode); +extern void (*BKE_curves_batch_cache_free_cb)(struct Curves *curves); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 00eae2e8e6e..38b43e36feb 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -254,6 +254,11 @@ bool CustomData_free_layer_active(struct CustomData *data, int type, int totelem void CustomData_free_layers(struct CustomData *data, int type, int totelem); /** + * Free all anonymous attributes. + */ +void CustomData_free_layers_anonymous(struct CustomData *data, int totelem); + +/** * Returns true if a layer with the specified type exists. */ bool CustomData_has_layer(const struct CustomData *data, int type); @@ -437,6 +442,12 @@ int CustomData_get_clone_layer(const struct CustomData *data, int type); int CustomData_get_stencil_layer(const struct CustomData *data, int type); /** + * Returns name of the active layer of the given type or NULL + * if no such active layer is defined. + */ +const char *CustomData_get_active_layer_name(const struct CustomData *data, int type); + +/** * Copies the data from source to the data element at index in the first layer of type * no effect if there is no layer of type. */ diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 5be06dcc5c3..1da7ae3da8a 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -62,14 +62,6 @@ typedef struct BMEditMesh { struct BMLoop *(*looptris)[3]; int tottri; - struct Mesh *mesh_eval_final, *mesh_eval_cage; - - /** Cached cage bounding box of `mesh_eval_cage` for selection. */ - struct BoundBox *bb_cage; - - /** Evaluated mesh data-mask. */ - CustomData_MeshMasks lastDataMask; - /** Selection mode (#SCE_SELECT_VERTEX, #SCE_SELECT_EDGE & #SCE_SELECT_FACE). */ short selectmode; /** The active material (assigned to newly created faces). */ @@ -121,7 +113,6 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em); * don't add NULL data check here. caller must do that */ BMEditMesh *BKE_editmesh_from_object(struct Object *ob); -void BKE_editmesh_free_derived_caches(BMEditMesh *em); /** * \note Does not free the #BMEditMesh struct itself. */ @@ -145,7 +136,7 @@ void BKE_editmesh_lnorspace_update(BMEditMesh *em, struct Mesh *me); * If auto-smooth not already set, set it. */ void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, struct Mesh *me); -struct BoundBox *BKE_editmesh_cage_boundbox_get(BMEditMesh *em); +struct BoundBox *BKE_editmesh_cage_boundbox_get(struct Object *object, BMEditMesh *em); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_hair.h b/source/blender/blenkernel/BKE_hair.h deleted file mode 100644 index 403e461a9dc..00000000000 --- a/source/blender/blenkernel/BKE_hair.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -/** \file - * \ingroup bke - * \brief General operations for hairs. - */ -#ifdef __cplusplus -extern "C" { -#endif - -struct BoundBox; -struct CustomDataLayer; -struct Depsgraph; -struct Hair; -struct Main; -struct Object; -struct Scene; - -void *BKE_hair_add(struct Main *bmain, const char *name); - -struct BoundBox *BKE_hair_boundbox_get(struct Object *ob); - -void BKE_hair_update_customdata_pointers(struct Hair *hair); -bool BKE_hair_customdata_required(struct Hair *hair, struct CustomDataLayer *layer); - -/* Depsgraph */ - -struct Hair *BKE_hair_new_for_eval(const struct Hair *hair_src, int totpoint, int totcurve); -struct Hair *BKE_hair_copy_for_eval(struct Hair *hair_src, bool reference); - -void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *object); - -/* Draw Cache */ - -enum { - BKE_HAIR_BATCH_DIRTY_ALL = 0, -}; - -void BKE_hair_batch_cache_dirty_tag(struct Hair *hair, int mode); -void BKE_hair_batch_cache_free(struct Hair *hair); - -extern void (*BKE_hair_batch_cache_dirty_tag_cb)(struct Hair *hair, int mode); -extern void (*BKE_hair_batch_cache_free_cb)(struct Hair *hair); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index df50f773a46..e9e5b183e4a 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -278,7 +278,7 @@ extern IDTypeInfo IDType_ID_PC; extern IDTypeInfo IDType_ID_CF; extern IDTypeInfo IDType_ID_WS; extern IDTypeInfo IDType_ID_LP; -extern IDTypeInfo IDType_ID_HA; +extern IDTypeInfo IDType_ID_CV; extern IDTypeInfo IDType_ID_PT; extern IDTypeInfo IDType_ID_VO; extern IDTypeInfo IDType_ID_SIM; diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 80c6b155be0..598818ba3c0 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -24,6 +24,8 @@ #include "BLI_utildefines.h" +#include "BLI_rect.h" + #ifdef __cplusplus extern "C" { #endif @@ -561,19 +563,27 @@ struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image, * Is the alpha of the `GPUTexture` for a given image/ibuf premultiplied. */ bool BKE_image_has_gpu_texture_premultiplied_alpha(struct Image *image, struct ImBuf *ibuf); + /** * Partial update of texture for texture painting. * This is often much quicker than fully updating the texture for high resolution images. */ void BKE_image_update_gputexture( struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); + /** * Mark areas on the #GPUTexture that needs to be updated. The areas are marked in chunks. * The next time the #GPUTexture is used these tiles will be refreshes. This saves time * when writing to the same place multiple times This happens for during foreground rendering. */ -void BKE_image_update_gputexture_delayed( - struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h); +void BKE_image_update_gputexture_delayed(struct Image *ima, + struct ImageTile *image_tile, + struct ImBuf *ibuf, + int x, + int y, + int w, + int h); + /** * Called on entering and exiting texture paint mode, * temporary disabling/enabling mipmapping on all images for quick texture @@ -591,6 +601,32 @@ bool BKE_image_remove_renderslot(struct Image *ima, struct ImageUser *iuser, int struct RenderSlot *BKE_image_get_renderslot(struct Image *ima, int index); bool BKE_image_clear_renderslot(struct Image *ima, struct ImageUser *iuser, int slot); +/* --- image_partial_update.cc --- */ +/** Image partial updates. */ +struct PartialUpdateUser; + +/** + * \brief Create a new PartialUpdateUser. An Object that contains data to use partial updates. + */ +struct PartialUpdateUser *BKE_image_partial_update_create(const struct Image *image); + +/** + * \brief free a partial update user. + */ +void BKE_image_partial_update_free(struct PartialUpdateUser *user); + +/* --- partial updater (image side) --- */ +struct PartialUpdateRegister; + +void BKE_image_partial_update_register_free(struct Image *image); +/** \brief Mark a region of the image to update. */ +void BKE_image_partial_update_mark_region(struct Image *image, + const struct ImageTile *image_tile, + const struct ImBuf *image_buffer, + const rcti *updated_region); +/** \brief Mark the whole image to be updated. */ +void BKE_image_partial_update_mark_full_update(struct Image *image); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_image_partial_update.hh b/source/blender/blenkernel/BKE_image_partial_update.hh new file mode 100644 index 00000000000..0fc05809bbd --- /dev/null +++ b/source/blender/blenkernel/BKE_image_partial_update.hh @@ -0,0 +1,298 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup bke + * + * To reduce the overhead of image processing this file contains a mechanism to detect areas of the + * image that are changed. These areas are organized in chunks. Changes that happen over time are + * organized in changesets. + * + * A common use case is to update #GPUTexture for drawing where only that part is uploaded that + * only changed. + */ + +#pragma once + +#include "BLI_utildefines.h" + +#include "BLI_rect.h" + +#include "DNA_image_types.h" + +extern "C" { +struct PartialUpdateUser; +struct PartialUpdateRegister; +} + +namespace blender::bke::image { + +using TileNumber = int; + +namespace partial_update { + +/* --- image_partial_update.cc --- */ +/** Image partial updates. */ + +/** + * \brief Result codes of #BKE_image_partial_update_collect_changes. + */ +enum class ePartialUpdateCollectResult { + /** \brief Unable to construct partial updates. Caller should perform a full update. */ + FullUpdateNeeded, + + /** \brief No changes detected since the last time requested. */ + NoChangesDetected, + + /** \brief Changes detected since the last time requested. */ + PartialChangesDetected, +}; + +/** + * \brief A region to update. + * + * Data is organized in tiles. These tiles are in texel space (1 unit is a single texel). When + * tiles are requested they are merged with neighboring tiles. + */ +struct PartialUpdateRegion { + /** \brief region of the image that has been updated. Region can be bigger than actual changes. + */ + struct rcti region; + + /** + * \brief Tile number (UDIM) that this region belongs to. + */ + TileNumber tile_number; +}; + +/** + * \brief Return codes of #BKE_image_partial_update_get_next_change. + */ +enum class ePartialUpdateIterResult { + /** \brief no tiles left when iterating over tiles. */ + Finished = 0, + + /** \brief a chunk was available and has been loaded. */ + ChangeAvailable = 1, +}; + +/** + * \brief collect the partial update since the last request. + * + * Invoke #BKE_image_partial_update_get_next_change to iterate over the collected tiles. + * + * \returns ePartialUpdateCollectResult::FullUpdateNeeded: called should not use partial updates + * but recalculate the full image. This result can be expected when called for the first time for a + * user and when it isn't possible to reconstruct the changes as the internal state doesn't have + * enough data stored. ePartialUpdateCollectResult::NoChangesDetected: The have been no changes + * detected since last invoke for the same user. + * ePartialUpdateCollectResult::PartialChangesDetected: Parts of the image has been updated since + * last invoke for the same user. The changes can be read by using + * #BKE_image_partial_update_get_next_change. + */ +ePartialUpdateCollectResult BKE_image_partial_update_collect_changes( + struct Image *image, struct PartialUpdateUser *user); + +ePartialUpdateIterResult BKE_image_partial_update_get_next_change( + struct PartialUpdateUser *user, struct PartialUpdateRegion *r_region); + +/** \brief Abstract class to load tile data when using the PartialUpdateChecker. */ +class AbstractTileData { + protected: + virtual ~AbstractTileData() = default; + + public: + /** + * \brief Load the data for the given tile_number. + * + * Invoked when changes are on a different tile compared to the previous tile.. + */ + virtual void init_data(TileNumber tile_number) = 0; + /** + * \brief Unload the data that has been loaded. + * + * Invoked when changes are on a different tile compared to the previous tile or when finished + * iterating over the changes. + */ + virtual void free_data() = 0; +}; + +/** + * \brief Class to not load any tile specific data when iterating over changes. + */ +class NoTileData : AbstractTileData { + public: + NoTileData(Image *UNUSED(image), ImageUser *UNUSED(image_user)) + { + } + + void init_data(TileNumber UNUSED(new_tile_number)) override + { + } + + void free_data() override + { + } +}; + +/** + * \brief Load the ImageTile and ImBuf associated with the partial change. + */ +class ImageTileData : AbstractTileData { + public: + /** + * \brief Not owned Image that is being iterated over. + */ + Image *image; + + /** + * \brief Local copy of the image user. + * + * The local copy is required so we don't change the image user of the caller. + * We need to change it in order to request data for a specific tile. + */ + ImageUser image_user = {0}; + + /** + * \brief ImageTile associated with the loaded tile. + * Data is not owned by this instance but by the `image`. + */ + ImageTile *tile = nullptr; + + /** + * \brief ImBuf of the loaded tile. + * + * Can be nullptr when the file doesn't exist or when the tile hasn't been initialized. + */ + ImBuf *tile_buffer = nullptr; + + ImageTileData(Image *image, ImageUser *image_user) : image(image) + { + if (image_user != nullptr) { + this->image_user = *image_user; + } + } + + void init_data(TileNumber new_tile_number) override + { + image_user.tile = new_tile_number; + tile = BKE_image_get_tile(image, new_tile_number); + tile_buffer = BKE_image_acquire_ibuf(image, &image_user, NULL); + } + + void free_data() override + { + BKE_image_release_ibuf(image, tile_buffer, nullptr); + tile = nullptr; + tile_buffer = nullptr; + } +}; + +template<typename TileData = NoTileData> struct PartialUpdateChecker { + + /** + * \brief Not owned Image that is being iterated over. + */ + Image *image; + ImageUser *image_user; + + /** + * \brief the collected changes are stored inside the PartialUpdateUser. + */ + PartialUpdateUser *user; + + struct CollectResult { + PartialUpdateChecker<TileData> *checker; + + /** + * \brief Tile specific data. + */ + TileData tile_data; + PartialUpdateRegion changed_region; + ePartialUpdateCollectResult result_code; + + private: + TileNumber last_tile_number; + + public: + CollectResult(PartialUpdateChecker<TileData> *checker, ePartialUpdateCollectResult result_code) + : checker(checker), + tile_data(checker->image, checker->image_user), + result_code(result_code) + { + } + + const ePartialUpdateCollectResult get_result_code() const + { + return result_code; + } + + /** + * \brief Load the next changed region. + * + * This member function can only be called when partial changes are detected. + * (`get_result_code()` returns `ePartialUpdateCollectResult::PartialChangesDetected`). + * + * When changes for another tile than the previous tile is loaded the #tile_data will be + * updated. + */ + ePartialUpdateIterResult get_next_change() + { + BLI_assert(result_code == ePartialUpdateCollectResult::PartialChangesDetected); + ePartialUpdateIterResult result = BKE_image_partial_update_get_next_change(checker->user, + &changed_region); + switch (result) { + case ePartialUpdateIterResult::Finished: + tile_data.free_data(); + return result; + + case ePartialUpdateIterResult::ChangeAvailable: + if (last_tile_number == changed_region.tile_number) { + return result; + } + tile_data.free_data(); + tile_data.init_data(changed_region.tile_number); + last_tile_number = changed_region.tile_number; + return result; + + default: + BLI_assert_unreachable(); + return result; + } + } + }; + + public: + PartialUpdateChecker(Image *image, ImageUser *image_user, PartialUpdateUser *user) + : image(image), image_user(image_user), user(user) + { + } + + /** + * \brief Check for new changes since the last time this method was invoked for this #user. + */ + CollectResult collect_changes() + { + ePartialUpdateCollectResult collect_result = BKE_image_partial_update_collect_changes(image, + user); + return CollectResult(this, collect_result); + } +}; + +} // namespace partial_update +} // namespace blender::bke::image diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index bc2249b93b9..accdfe1ca25 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -157,6 +157,14 @@ int BKE_layer_collection_findindex(struct ViewLayer *view_layer, const struct La void BKE_layer_collection_resync_forbid(void); void BKE_layer_collection_resync_allow(void); +/** + * Helper to fix older pre-2.80 blend-files. + * + * Ensures the given `view_layer` as a valid first-level layer collection, i.e. a single one + * matching the scene's master collection. This is a requirement for `BKE_layer_collection_sync`. + */ +void BKE_layer_collection_doversion_2_80(const struct Scene *scene, struct ViewLayer *view_layer); + void BKE_main_collection_sync(const struct Main *bmain); void BKE_scene_collection_sync(const struct Scene *scene); /** diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 1d905ad85b1..daf13590ca2 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -376,13 +376,19 @@ enum { /** Clear asset data (in case the ID can actually be made local, in copy case asset data is never * copied over). */ LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR = 1 << 3, - - /* Special type-specific options. */ - /** For Objects, do not clear the proxy pointers while making the data-block local. */ - LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING = 1 << 16, }; /** + * Helper to decide whether given `id` can be directly made local, or needs to be copied. + * `r_force_local` and `r_force_copy` cannot be true together. But both can be false, in case no + * action should be performed. + * + * \note low-level helper to de-duplicate logic between `BKE_lib_id_make_local_generic` and the + * specific corner-cases implementations needed for objects and brushes. + */ +void BKE_lib_id_make_local_generic_action_define( + struct Main *bmain, struct ID *id, int flags, bool *r_force_local, bool *r_force_copy); +/** * Generic 'make local' function, works for most of data-block types. */ void BKE_lib_id_make_local_generic(struct Main *bmain, struct ID *id, int flags); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 30e75259967..e8065566c97 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -100,6 +100,9 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, * main. You can add more local IDs to be remapped to use new overriding ones by setting their * LIB_TAG_DOIT tag. * + * \param owner_library: the library in which the overrides should be created. Besides versioning + * and resync code path, this should always be NULL (i.e. the local .blend file). + * * \param reference_library: the library from which the linked data being overridden come from * (i.e. the library of the linked reference ID). * @@ -109,6 +112,7 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, * \return \a true on success, \a false otherwise. */ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, + struct Library *owner_library, const struct Library *reference_library, bool do_no_main); /** @@ -122,16 +126,24 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, * * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in * which case \a scene's master collection children hierarchy is used instead). + * + * \param owner_library: the library in which the overrides should be created. Besides versioning + * and resync code path, this should always be NULL (i.e. the local .blend file). + * * \param id_root: The root ID to create an override from. + * * \param id_reference: Some reference ID used to do some post-processing after overrides have been * created, may be NULL. Typically, the Empty object instantiating the linked collection we * override, currently. + * * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. + * * \return true if override was successfully created. */ bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, + struct Library *owner_library, struct ID *id_root, struct ID *id_reference, struct ID **r_id_root_override); diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index d853cb16b13..dd2e2a2f8e5 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -63,7 +63,7 @@ enum { /** * That ID is not really used by its owner, it's just an internal hint/helper. - * This addresses Their Highest Ugliness the 'from' pointers: Object->from_proxy and Key->from. + * This marks the 'from' pointers issue, like Key->from. * How to handle that kind of cases totally depends on what caller code is doing... */ IDWALK_CB_LOOPBACK = (1 << 4), @@ -135,7 +135,6 @@ enum { /** Do not process ID pointers inside embedded IDs. Needed by depsgraph processing e.g. */ IDWALK_IGNORE_EMBEDDED_ID = (1 << 3), - IDWALK_NO_INDIRECT_PROXY_DATA_USAGE = (1 << 8), /* Ugly special case :(((( */ /** Also process internal ID pointers like `ID.newid` or `ID.orig_id`. * WARNING: Dangerous, use with caution. */ IDWALK_DO_INTERNAL_RUNTIME_POINTERS = (1 << 9), diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index d8842dbce7f..f14cd75c5c6 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -38,6 +38,9 @@ extern "C" { #endif +struct ID; +struct IDRemapper; + /* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */ /* Also IDRemap->flag. */ @@ -65,15 +68,6 @@ enum { * and can cause crashes very easily! */ ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3, - /** - * Do not consider proxy/_group pointers of local objects as indirect usages... - * Our oh-so-beloved proxies again... - * Do not consider data used by local proxy object as indirect usage. - * This is needed e.g. in reload scenario, - * since we have to ensure remapping of Armature data of local proxy - * is also performed. Usual nightmare... - */ - ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4, /** Do not remap library override pointers. */ ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5, /** Don't touch the user count (use for low level actions such as swapping pointers). */ @@ -98,6 +92,19 @@ enum { }; /** + * Replace all references in given Main using the given \a mappings + * + * \note Is preferred over BKE_libblock_remap_locked due to performance. + */ +void BKE_libblock_remap_multiple_locked(struct Main *bmain, + const struct IDRemapper *mappings, + const short remap_flags); + +void BKE_libblock_remap_multiple(struct Main *bmain, + const struct IDRemapper *mappings, + const short remap_flags); + +/** * Replace all references in given Main to \a old_id by \a new_id * (if \a new_id is NULL, it unlinks \a old_id). * @@ -146,12 +153,61 @@ void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id, int remap_f ATTR_NONNULL(); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); -typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); +typedef void (*BKE_library_remap_editor_id_reference_cb)(const struct IDRemapper *mappings); void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func); void BKE_library_callback_remap_editor_id_reference_set( BKE_library_remap_editor_id_reference_cb func); +/* IDRemapper */ +struct IDRemapper; +typedef enum IDRemapperApplyResult { + /** No remapping rules available for the source. */ + ID_REMAP_RESULT_SOURCE_UNAVAILABLE, + /** Source isn't mappable (e.g. NULL). */ + ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE, + /** Source has been remapped to a new pointer. */ + ID_REMAP_RESULT_SOURCE_REMAPPED, + /** Source has been set to NULL. */ + ID_REMAP_RESULT_SOURCE_UNASSIGNED, +} IDRemapperApplyResult; + +typedef enum IDRemapperApplyOptions { + ID_REMAP_APPLY_UPDATE_REFCOUNT = (1 << 0), + ID_REMAP_APPLY_ENSURE_REAL = (1 << 1), + + ID_REMAP_APPLY_DEFAULT = 0, +} IDRemapperApplyOptions; + +typedef void (*IDRemapperIterFunction)(struct ID *old_id, struct ID *new_id, void *user_data); + +/** + * Create a new ID Remapper. + * + * An ID remapper stores multiple remapping rules. + */ +struct IDRemapper *BKE_id_remapper_create(void); + +void BKE_id_remapper_clear(struct IDRemapper *id_remapper); +bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper); +/** Free the given ID Remapper. */ +void BKE_id_remapper_free(struct IDRemapper *id_remapper); +/** Add a new remapping. */ +void BKE_id_remapper_add(struct IDRemapper *id_remapper, struct ID *old_id, struct ID *new_id); + +/** + * Apply a remapping. + * + * Update the id pointer stored in the given r_id_ptr if a remapping rule exists. + */ +IDRemapperApplyResult BKE_id_remapper_apply(const struct IDRemapper *id_remapper, + struct ID **r_id_ptr, + IDRemapperApplyOptions options); +bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter); +void BKE_id_remapper_iter(const struct IDRemapper *id_remapper, + IDRemapperIterFunction func, + void *user_data); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 4c6eb31db4b..e4f94110eb1 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -182,7 +182,11 @@ typedef struct Main { ListBase linestyles; ListBase cachefiles; ListBase workspaces; - ListBase hairs; + /** + * \note The name `hair_curves` is chosen to be different than `curves`, + * but they are generic curve data-blocks, not just for hair. + */ + ListBase hair_curves; ListBase pointclouds; ListBase volumes; ListBase simulations; diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 6554a9c72aa..e1c706a82dc 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -119,6 +119,9 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh, void BKE_mesh_free_data_for_undo(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_free_editmesh(struct Mesh *mesh); + /** * A version of #BKE_mesh_copy_parameters that is intended for evaluated output * (the modifier stack for example). diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 80889813b34..a05ed67063a 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -245,10 +245,6 @@ typedef struct ModifierTypeInfo { const struct ModifierEvalContext *ctx, struct Mesh *mesh); - struct Hair *(*modifyHair)(struct ModifierData *md, - const struct ModifierEvalContext *ctx, - struct Hair *hair); - /** * The modifier has to change the geometry set in-place. The geometry set can contain zero or * more geometry components. This callback can be used by modifiers that don't work on any @@ -470,6 +466,8 @@ void BKE_modifiers_foreach_tex_link(struct Object *ob, TexWalkFunc walk, void *u struct ModifierData *BKE_modifiers_findby_type(const struct Object *ob, ModifierType type); struct ModifierData *BKE_modifiers_findby_name(const struct Object *ob, const char *name); +struct ModifierData *BKE_modifiers_findby_session_uuid(const struct Object *ob, + const SessionUUID *session_uuid); void BKE_modifiers_clear_errors(struct Object *ob); /** * used for buttons, to find out if the 'draw deformed in edit-mode option is there. @@ -568,7 +566,8 @@ const char *BKE_modifier_path_relbase_from_global(struct Object *ob); * For a given modifier data, get corresponding original one. * If the modifier data is already original, return it as-is. */ -struct ModifierData *BKE_modifier_get_original(struct ModifierData *md); +struct ModifierData *BKE_modifier_get_original(const struct Object *object, + struct ModifierData *md); struct ModifierData *BKE_modifier_get_evaluated(struct Depsgraph *depsgraph, struct Object *object, struct ModifierData *md); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2f9034f6438..7ffa180b523 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -274,6 +274,9 @@ typedef struct bNodeType { char *label, int maxlen); + /** Optional override for node class, used for drawing node header. */ + int (*ui_class)(const struct bNode *node); + /** Called when the node is updated in the editor. */ void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node); /** Check and update if internal ID data has changed. */ @@ -1178,30 +1181,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_OUTPUT_AOV 707 #define SH_NODE_VECTOR_ROTATE 708 #define SH_NODE_CURVE_FLOAT 709 - -/* API */ - -struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree); -void ntreeShaderEndExecTree(struct bNodeTreeExec *exec); -/** - Find an output node of the shader tree. - * - * \note it will only return output which is NOT in the group, which isn't how - * render engines works but it's how the GPU shader compilation works. This we - * can change in the future and make it a generic function, but for now it stays - * private here. - */ -struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target); -/** - * This one needs to work on a local tree. - * - * TODO: This is *not* part of `blenkernel`, it's defined under "source/blender/nodes/". - * This declaration should be moved out of BKE. - */ -void ntreeGPUMaterialNodes(struct bNodeTree *localtree, - struct GPUMaterial *mat, - bool *has_surface_output, - bool *has_volume_output); +#define SH_NODE_POINT_INFO 710 /** \} */ @@ -1314,6 +1294,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree, #define CMP_NODE_POSTERIZE 327 #define CMP_NODE_CONVERT_COLOR_SPACE 328 #define CMP_NODE_SCENE_TIME 329 +#define CMP_NODE_SEPARATE_XYZ 330 +#define CMP_NODE_COMBINE_XYZ 331 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -1352,75 +1334,6 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree, #define CMP_DEFAULT_SMAA_CONTRAST_LIMIT 0.2f #define CMP_DEFAULT_SMAA_CORNER_ROUNDING 0.25f -/* API */ -void ntreeCompositExecTree(struct Scene *scene, - struct bNodeTree *ntree, - struct RenderData *rd, - int rendering, - int do_previews, - const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings, - const char *view_name); - -/** - * Called from render pipeline, to tag render input and output. - * need to do all scenes, to prevent errors when you re-render 1 scene. - */ -void ntreeCompositTagRender(struct Scene *scene); -/** - * Update the outputs of the render layer nodes. - * Since the outputs depend on the render engine, this part is a bit complex: - * - #ntreeCompositUpdateRLayers is called and loops over all render layer nodes. - * - Each render layer node calls the update function of the - * render engine that's used for its scene. - * - The render engine calls RE_engine_register_pass for each pass. - * - #RE_engine_register_pass calls #node_cmp_rlayers_register_pass. - * - * TODO: This is *not* part of `blenkernel`, it's defined under "source/blender/nodes/". - * This declaration should be moved out of BKE. - */ -void ntreeCompositUpdateRLayers(struct bNodeTree *ntree); -void ntreeCompositClearTags(struct bNodeTree *ntree); - -struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree, - struct bNode *node, - const char *name, - struct ImageFormatData *im_format); -int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node); -void ntreeCompositOutputFileSetPath(struct bNode *node, - struct bNodeSocket *sock, - const char *name); -void ntreeCompositOutputFileSetLayer(struct bNode *node, - struct bNodeSocket *sock, - const char *name); -/* needed in do_versions */ -void ntreeCompositOutputFileUniquePath(struct ListBase *list, - struct bNodeSocket *sock, - const char defname[], - char delim); -void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, - struct bNodeSocket *sock, - const char defname[], - char delim); - -void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); -void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); - -void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); -void ntreeCompositCryptomatteSyncFromRemove(bNode *node); -bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); -int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, - const bNode *node, - char *r_prefix, - size_t prefix_len); -/** - * Update the runtime layer names with the crypto-matte layer names of the references render layer - * or image. - */ -void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); -struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); - /** \} */ /* -------------------------------------------------------------------- */ @@ -1457,24 +1370,6 @@ struct TexResult; #define TEX_NODE_PROC 500 #define TEX_NODE_PROC_MAX 600 -/* API */ -void ntreeTexCheckCyclics(struct bNodeTree *ntree); - -struct bNodeTreeExec *ntreeTexBeginExecTree(struct bNodeTree *ntree); -void ntreeTexEndExecTree(struct bNodeTreeExec *exec); -int ntreeTexExecTree(struct bNodeTree *ntree, - struct TexResult *target, - const float co[3], - float dxt[3], - float dyt[3], - int osatex, - short thread, - const struct Tex *tex, - short which_output, - int cfra, - int preview, - struct MTex *mtex); - /** \} */ /* -------------------------------------------------------------------- */ @@ -1633,6 +1528,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_FLIP_FACES 1150 #define GEO_NODE_SCALE_ELEMENTS 1151 #define GEO_NODE_EXTRUDE_MESH 1152 +#define GEO_NODE_MERGE_BY_DISTANCE 1153 /** \} */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index da8dba0c86b..96ed7942067 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -144,18 +144,6 @@ void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_sr void BKE_object_free_modifiers(struct Object *ob, int flag); void BKE_object_free_shaderfx(struct Object *ob, int flag); -/** - * Proxy rule: - * - `lib_object->proxy_from` == the one we borrow from, set temporally while object_update. - * - `local_object->proxy` == pointer to library object, saved in files and read. - * - `local_object->proxy_group` == pointer to collection dupli-object, saved in files and read. - */ -void BKE_object_make_proxy(struct Main *bmain, - struct Object *ob, - struct Object *target, - struct Object *cob); -void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target); - bool BKE_object_exists_check(struct Main *bmain, const struct Object *obtest); /** * Actual check for internal data, not context or flags. @@ -444,7 +432,6 @@ void BKE_object_eval_constraints(struct Depsgraph *depsgraph, struct Object *ob); void BKE_object_eval_transform_final(struct Depsgraph *depsgraph, struct Object *ob); -bool BKE_object_eval_proxy_copy(struct Depsgraph *depsgraph, struct Object *object); void BKE_object_eval_uber_transform(struct Depsgraph *depsgraph, struct Object *ob); void BKE_object_eval_uber_data(struct Depsgraph *depsgraph, struct Scene *scene, @@ -486,12 +473,6 @@ void BKE_object_handle_data_update(struct Depsgraph *depsgraph, */ void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); /** - * Proxy rule: - * - lib_object->proxy_from == the one we borrow from, only set temporal and cleared here. - * - local_object->proxy == pointer to library object, saved in files and read. - * - * Function below is polluted with proxy exceptions, cleanup will follow! - * * The main object update call, for object matrix, constraints, keys and #DispList (modifiers) * requires flags to be set! * @@ -501,8 +482,7 @@ void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene, void BKE_object_handle_update_ex(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, - struct RigidBodyWorld *rbw, - bool do_proxy_update); + struct RigidBodyWorld *rbw); void BKE_object_sculpt_data_create(struct Object *ob); @@ -530,6 +510,9 @@ struct Mesh *BKE_object_get_pre_modified_mesh(const struct Object *object); */ struct Mesh *BKE_object_get_original_mesh(const struct Object *object); +struct Mesh *BKE_object_get_editmesh_eval_final(const struct Object *object); +struct Mesh *BKE_object_get_editmesh_eval_cage(const struct Object *object); + /* Lattice accessors. * These functions return either the regular lattice, or the edit-mode lattice, * whichever is currently in use. */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 4019c4d62c4..8291be9d7e8 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -499,7 +499,6 @@ typedef struct SculptSession { /* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */ struct MVert *mvert; - const float (*vert_normals)[3]; struct MPoly *mpoly; struct MLoop *mloop; diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 63f6fca2a9d..c85ae04a492 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -38,6 +38,7 @@ struct BlendLibReader; struct BlendWriter; struct Header; struct ID; +struct IDRemapper; struct LibraryForeachIDData; struct ListBase; struct Menu; @@ -117,10 +118,7 @@ typedef struct SpaceType { bContextDataCallback context; /* Used when we want to replace an ID by another (or NULL). */ - void (*id_remap)(struct ScrArea *area, - struct SpaceLink *sl, - struct ID *old_id, - struct ID *new_id); + void (*id_remap)(struct ScrArea *area, struct SpaceLink *sl, const struct IDRemapper *mappings); int (*space_subtype_get)(struct ScrArea *area); void (*space_subtype_set)(struct ScrArea *area, int value); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index d89588b649a..11dab0ecaad 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -119,6 +119,7 @@ set(SRC intern/crazyspace.c intern/cryptomatte.cc intern/curve.cc + intern/curves.cc intern/curve_bevel.c intern/curve_convert.c intern/curve_decimate.c @@ -156,7 +157,6 @@ set(SRC intern/gpencil_curve.c intern/gpencil_geom.cc intern/gpencil_modifier.c - intern/hair.cc intern/icons.cc intern/icons_rasterize.c intern/idprop.c @@ -165,6 +165,7 @@ set(SRC intern/idprop_utils.c intern/idtype.c intern/image.c + intern/image_partial_update.cc intern/image_gen.c intern/image_gpu.cc intern/image_save.c @@ -179,7 +180,9 @@ set(SRC intern/lib_id.c intern/lib_id_delete.c intern/lib_id_eval.c + intern/lib_id_remapper.cc intern/lib_override.c + intern/lib_override_proxy_conversion.c intern/lib_query.c intern/lib_remap.c intern/library.c @@ -353,6 +356,7 @@ set(SRC BKE_cryptomatte.h BKE_cryptomatte.hh BKE_curve.h + BKE_curves.h BKE_curve_to_mesh.hh BKE_curveprofile.h BKE_customdata.h @@ -381,7 +385,6 @@ set(SRC BKE_gpencil_curve.h BKE_gpencil_geom.h BKE_gpencil_modifier.h - BKE_hair.h BKE_icons.h BKE_idprop.h BKE_idprop.hh @@ -522,7 +525,7 @@ set(LIB bf_simulation # For `vfontdata_freetype.c`. - ${FREETYPE_LIBRARIES} + ${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES} ) if(WITH_BINRELOC) @@ -721,10 +724,6 @@ if(WITH_FFTW3) add_definitions(-DFFTW3=1) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() @@ -825,8 +824,10 @@ if(WITH_GTESTS) intern/cryptomatte_test.cc intern/fcurve_test.cc intern/idprop_serialize_test.cc + intern/image_partial_update_test.cc intern/lattice_deform_test.cc intern/layer_test.cc + intern/lib_id_remapper_test.cc intern/lib_id_test.cc intern/lib_remap_test.cc intern/tracking_test.cc diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 13131c24eda..2e779d6fad7 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -127,30 +127,6 @@ static MEdge *dm_getEdgeArray(DerivedMesh *dm) return medge; } -static MFace *dm_getTessFaceArray(DerivedMesh *dm) -{ - MFace *mface = (MFace *)CustomData_get_layer(&dm->faceData, CD_MFACE); - - if (!mface) { - int numTessFaces = dm->getNumTessFaces(dm); - - if (!numTessFaces) { - /* Do not add layer if there's no elements in it, this leads to issues later when - * this layer is needed with non-zero size, but currently CD stuff does not check - * for requested layer size on creation and just returns layer which was previously - * added (sergey) */ - return nullptr; - } - - mface = (MFace *)CustomData_add_layer( - &dm->faceData, CD_MFACE, CD_CALLOC, nullptr, numTessFaces); - CustomData_set_layer_flag(&dm->faceData, CD_MFACE, CD_FLAG_TEMPORARY); - dm->copyTessFaceArray(dm, mface); - } - - return mface; -} - static MLoop *dm_getLoopArray(DerivedMesh *dm) { MLoop *mloop = (MLoop *)CustomData_get_layer(&dm->loopData, CD_MLOOP); @@ -203,18 +179,6 @@ static MEdge *dm_dupEdgeArray(DerivedMesh *dm) return tmp; } -static MFace *dm_dupFaceArray(DerivedMesh *dm) -{ - MFace *tmp = (MFace *)MEM_malloc_arrayN( - dm->getNumTessFaces(dm), sizeof(*tmp), "dm_dupFaceArray tmp"); - - if (tmp) { - dm->copyTessFaceArray(dm, tmp); - } - - return tmp; -} - static MLoop *dm_dupLoopArray(DerivedMesh *dm) { MLoop *tmp = (MLoop *)MEM_malloc_arrayN( @@ -270,42 +234,15 @@ static const MLoopTri *dm_getLoopTriArray(DerivedMesh *dm) return looptri; } -static CustomData *dm_getVertCData(DerivedMesh *dm) -{ - return &dm->vertData; -} - -static CustomData *dm_getEdgeCData(DerivedMesh *dm) -{ - return &dm->edgeData; -} - -static CustomData *dm_getTessFaceCData(DerivedMesh *dm) -{ - return &dm->faceData; -} - -static CustomData *dm_getLoopCData(DerivedMesh *dm) -{ - return &dm->loopData; -} - -static CustomData *dm_getPolyCData(DerivedMesh *dm) -{ - return &dm->polyData; -} - void DM_init_funcs(DerivedMesh *dm) { /* default function implementations */ dm->getVertArray = dm_getVertArray; dm->getEdgeArray = dm_getEdgeArray; - dm->getTessFaceArray = dm_getTessFaceArray; dm->getLoopArray = dm_getLoopArray; dm->getPolyArray = dm_getPolyArray; dm->dupVertArray = dm_dupVertArray; dm->dupEdgeArray = dm_dupEdgeArray; - dm->dupTessFaceArray = dm_dupFaceArray; dm->dupLoopArray = dm_dupLoopArray; dm->dupPolyArray = dm_dupPolyArray; @@ -314,19 +251,8 @@ void DM_init_funcs(DerivedMesh *dm) /* subtypes handle getting actual data */ dm->getNumLoopTri = dm_getNumLoopTri; - dm->getVertDataLayout = dm_getVertCData; - dm->getEdgeDataLayout = dm_getEdgeCData; - dm->getTessFaceDataLayout = dm_getTessFaceCData; - dm->getLoopDataLayout = dm_getLoopCData; - dm->getPolyDataLayout = dm_getPolyCData; - - dm->getVertData = DM_get_vert_data; - dm->getEdgeData = DM_get_edge_data; - dm->getTessFaceData = DM_get_tessface_data; - dm->getPolyData = DM_get_poly_data; dm->getVertDataArray = DM_get_vert_data_layer; dm->getEdgeDataArray = DM_get_edge_data_layer; - dm->getTessFaceDataArray = DM_get_tessface_data_layer; dm->getPolyDataArray = DM_get_poly_data_layer; dm->getLoopDataArray = DM_get_loop_data_layer; } @@ -349,7 +275,6 @@ void DM_init(DerivedMesh *dm, DM_init_funcs(dm); dm->needsFree = 1; - dm->dirty = (DMDirtyFlag)0; /* Don't use #CustomData_reset because we don't want to touch custom-data. */ copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1); @@ -359,16 +284,16 @@ void DM_init(DerivedMesh *dm, copy_vn_i(dm->polyData.typemap, CD_NUMTYPES, -1); } -void DM_from_template_ex(DerivedMesh *dm, - DerivedMesh *source, - DerivedMeshType type, - int numVerts, - int numEdges, - int numTessFaces, - int numLoops, - int numPolys, - const CustomData_MeshMasks *mask) +void DM_from_template(DerivedMesh *dm, + DerivedMesh *source, + DerivedMeshType type, + int numVerts, + int numEdges, + int numTessFaces, + int numLoops, + int numPolys) { + const CustomData_MeshMasks *mask = &CD_MASK_DERIVEDMESH; CustomData_copy(&source->vertData, &dm->vertData, mask->vmask, CD_CALLOC, numVerts); CustomData_copy(&source->edgeData, &dm->edgeData, mask->emask, CD_CALLOC, numEdges); CustomData_copy(&source->faceData, &dm->faceData, mask->fmask, CD_CALLOC, numTessFaces); @@ -387,26 +312,6 @@ void DM_from_template_ex(DerivedMesh *dm, DM_init_funcs(dm); dm->needsFree = 1; - dm->dirty = (DMDirtyFlag)0; -} -void DM_from_template(DerivedMesh *dm, - DerivedMesh *source, - DerivedMeshType type, - int numVerts, - int numEdges, - int numTessFaces, - int numLoops, - int numPolys) -{ - DM_from_template_ex(dm, - source, - type, - numVerts, - numEdges, - numTessFaces, - numLoops, - numPolys, - &CD_MASK_DERIVEDMESH); } bool DM_release(DerivedMesh *dm) @@ -464,14 +369,6 @@ void DM_DupPolys(DerivedMesh *source, DerivedMesh *target) } } -void DM_ensure_normals(DerivedMesh *dm) -{ - if (dm->dirty & DM_DIRTY_NORMALS) { - dm->calcNormals(dm); - } - BLI_assert((dm->dirty & DM_DIRTY_NORMALS) == 0); -} - void DM_ensure_looptri_data(DerivedMesh *dm) { const unsigned int totpoly = dm->numPolyData; @@ -524,7 +421,7 @@ void DM_set_only_copy(DerivedMesh *dm, const CustomData_MeshMasks *mask) * see replies to r50969, Campbell */ #if 0 CustomData_set_only_copy(&dm->loopData, mask->lmask); - CustomData_set_only_copy(&dm->polyData, mask->pmask); + Custom(&dm->polyData, mask->pmask); #endif } @@ -552,45 +449,11 @@ void DM_add_edge_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void * CustomData_add_layer(&dm->edgeData, type, alloctype, layer, dm->numEdgeData); } -void DM_add_tessface_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->faceData, type, alloctype, layer, dm->numTessFaceData); -} - -void DM_add_loop_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->loopData, type, alloctype, layer, dm->numLoopData); -} - void DM_add_poly_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) { CustomData_add_layer(&dm->polyData, type, alloctype, layer, dm->numPolyData); } -void *DM_get_vert_data(DerivedMesh *dm, int index, int type) -{ - BLI_assert(index >= 0 && index < dm->getNumVerts(dm)); - return CustomData_get(&dm->vertData, index, type); -} - -void *DM_get_edge_data(DerivedMesh *dm, int index, int type) -{ - BLI_assert(index >= 0 && index < dm->getNumEdges(dm)); - return CustomData_get(&dm->edgeData, index, type); -} - -void *DM_get_tessface_data(DerivedMesh *dm, int index, int type) -{ - BLI_assert(index >= 0 && index < dm->getNumTessFaces(dm)); - return CustomData_get(&dm->faceData, index, type); -} - -void *DM_get_poly_data(DerivedMesh *dm, int index, int type) -{ - BLI_assert(index >= 0 && index < dm->getNumPolys(dm)); - return CustomData_get(&dm->polyData, index, type); -} - void *DM_get_vert_data_layer(DerivedMesh *dm, int type) { if (type == CD_MVERT) { @@ -609,15 +472,6 @@ void *DM_get_edge_data_layer(DerivedMesh *dm, int type) return CustomData_get_layer(&dm->edgeData, type); } -void *DM_get_tessface_data_layer(DerivedMesh *dm, int type) -{ - if (type == CD_MFACE) { - return dm->getTessFaceArray(dm); - } - - return CustomData_get_layer(&dm->faceData, type); -} - void *DM_get_poly_data_layer(DerivedMesh *dm, int type) { return CustomData_get_layer(&dm->polyData, type); @@ -1731,13 +1585,6 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, BKE_id_free(nullptr, mesh_orco); } - /* Ensure normals calculation below is correct (normal settings have transferred properly). - * However, nodes modifiers might create meshes from scratch or transfer meshes from other - * objects with different settings, and in general it doesn't make sense to guarantee that - * the settings are the same as the original mesh. If necessary, this could become a modifier - * type flag. */ - BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh); - /* Compute normals. */ editbmesh_calc_modifier_final_normals(mesh_final, &final_datamask); if (mesh_cage && (mesh_cage != mesh_final)) { @@ -1769,17 +1616,6 @@ static void mesh_build_data(struct Depsgraph *depsgraph, const CustomData_MeshMasks *dataMask, const bool need_mapping) { - BLI_assert(ob->type == OB_MESH); - - /* Evaluated meshes aren't supposed to be created on original instances. If you do, - * they aren't cleaned up properly on mode switch, causing crashes, e.g T58150. */ - BLI_assert(ob->id.tag & LIB_TAG_COPIED_ON_WRITE); - - BKE_object_free_derived_caches(ob); - if (DEG_is_active(depsgraph)) { - BKE_sculpt_update_object_before_eval(ob); - } - #if 0 /* XXX This is already taken care of in mesh_calc_modifiers()... */ if (need_mapping) { /* Also add the flag so that it is recorded in lastDataMask. */ @@ -1846,15 +1682,7 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph, BMEditMesh *em, CustomData_MeshMasks *dataMask) { - BLI_assert(obedit->id.tag & LIB_TAG_COPIED_ON_WRITE); - - BKE_object_free_derived_caches(obedit); - if (DEG_is_active(depsgraph)) { - BKE_sculpt_update_object_before_eval(obedit); - } - - BKE_editmesh_free_derived_caches(em); - + Mesh *mesh = static_cast<Mesh *>(obedit->data); Mesh *me_cage; Mesh *me_final; GeometrySet *non_mesh_components; @@ -1862,13 +1690,33 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph, editbmesh_calc_modifiers( depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final, &non_mesh_components); - em->mesh_eval_final = me_final; - em->mesh_eval_cage = me_cage; + /* The modifier stack result is expected to share edit mesh pointer with the input. + * This is similar `mesh_calc_finalize()`. */ + BKE_mesh_free_editmesh(me_final); + BKE_mesh_free_editmesh(me_cage); + me_final->edit_mesh = me_cage->edit_mesh = em; + + /* Object has edit_mesh but is not in edit mode (object shares mesh datablock with another object + * with is in edit mode). + * Convert edit mesh to mesh until the draw manager can draw mesh wrapper which is not in the + * edit mode. */ + if (!(obedit->mode & OB_MODE_EDIT)) { + BKE_mesh_wrapper_ensure_mdata(me_final); + if (me_final != me_cage) { + BKE_mesh_wrapper_ensure_mdata(me_cage); + } + } + + const bool is_mesh_eval_owned = (me_final != mesh->runtime.mesh_eval); + BKE_object_eval_assign_data(obedit, &me_final->id, is_mesh_eval_owned); + + obedit->runtime.editmesh_eval_cage = me_cage; + obedit->runtime.geometry_set_eval = non_mesh_components; - BKE_object_boundbox_calc_from_mesh(obedit, em->mesh_eval_final); + BKE_object_boundbox_calc_from_mesh(obedit, me_final); - em->lastDataMask = *dataMask; + obedit->runtime.last_data_mask = *dataMask; } static void object_get_datamask(const Depsgraph *depsgraph, @@ -1924,9 +1772,25 @@ static void object_get_datamask(const Depsgraph *depsgraph, void makeDerivedMesh(struct Depsgraph *depsgraph, Scene *scene, Object *ob, - BMEditMesh *em, const CustomData_MeshMasks *dataMask) { + BLI_assert(ob->type == OB_MESH); + + /* Evaluated meshes aren't supposed to be created on original instances. If you do, + * they aren't cleaned up properly on mode switch, causing crashes, e.g T58150. */ + BLI_assert(ob->id.tag & LIB_TAG_COPIED_ON_WRITE); + + BKE_object_free_derived_caches(ob); + if (DEG_is_active(depsgraph)) { + BKE_sculpt_update_object_before_eval(ob); + } + + /* NOTE: Access the `edit_mesh` after freeing the derived caches, so that `ob->data` is restored + * to the pre-evaluated state. This is because the evaluated state is not necessarily sharing the + * `edit_mesh` pointer with the input. For example, if the object is first evaluated in the + * object mode, and then user in another scene moves object to edit mode. */ + BMEditMesh *em = ((Mesh *)ob->data)->edit_mesh; + bool need_mapping; CustomData_MeshMasks cddata_masks = *dataMask; object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping); @@ -1965,8 +1829,9 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph, !CustomData_MeshMasks_are_matching(&(ob->runtime.last_data_mask), &cddata_masks) || (need_mapping && !ob->runtime.last_need_mapping)) { CustomData_MeshMasks_update(&cddata_masks, &ob->runtime.last_data_mask); - mesh_build_data( - depsgraph, scene, ob, &cddata_masks, need_mapping || ob->runtime.last_need_mapping); + + makeDerivedMesh(depsgraph, scene, ob, dataMask); + mesh_eval = BKE_object_get_evaluated_mesh(ob); } @@ -1981,6 +1846,15 @@ Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph, Object *ob, const CustomData_MeshMasks *dataMask) { + BMEditMesh *em = ((Mesh *)ob->data)->edit_mesh; + if (em != nullptr) { + /* There is no such a concept as deformed mesh in edit mode. + * Explicitly disallow this request so that the evaluated result is not modified with evaluated + * result from the wrong mode. */ + BLI_assert_msg(0, "Request of derformed mesh of object which is in edit mode"); + return nullptr; + } + /* This function isn't thread-safe and can't be used during evaluation. */ BLI_assert(DEG_is_evaluating(depsgraph) == false); @@ -2055,12 +1929,12 @@ Mesh *editbmesh_get_eval_cage(struct Depsgraph *depsgraph, */ object_get_datamask(depsgraph, obedit, &cddata_masks, nullptr); - if (!em->mesh_eval_cage || - !CustomData_MeshMasks_are_matching(&(em->lastDataMask), &cddata_masks)) { + if (!obedit->runtime.editmesh_eval_cage || + !CustomData_MeshMasks_are_matching(&(obedit->runtime.last_data_mask), &cddata_masks)) { editbmesh_build_data(depsgraph, scene, obedit, em, &cddata_masks); } - return em->mesh_eval_cage; + return obedit->runtime.editmesh_eval_cage; } Mesh *editbmesh_get_eval_cage_from_orig(struct Depsgraph *depsgraph, @@ -2117,32 +1991,6 @@ void mesh_get_mapped_verts_coords(Mesh *me_eval, float (*r_cos)[3], const int to } } -void DM_calc_loop_tangents(DerivedMesh *dm, - bool calc_active_tangent, - const char (*tangent_names)[MAX_NAME], - int tangent_names_len) -{ - BKE_mesh_calc_loop_tangent_ex( - dm->getVertArray(dm), - dm->getPolyArray(dm), - dm->getNumPolys(dm), - dm->getLoopArray(dm), - dm->getLoopTriArray(dm), - dm->getNumLoopTri(dm), - &dm->loopData, - calc_active_tangent, - tangent_names, - tangent_names_len, - (const float(*)[3])CustomData_get_layer(&dm->vertData, CD_NORMAL), - (const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL), - (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), - (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ - /* result */ - &dm->loopData, - dm->getNumLoops(dm), - &dm->tangent_mask); -} - static void mesh_init_origspace(Mesh *mesh) { const float default_osf[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index fde42304185..8426f6f4676 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1956,30 +1956,15 @@ void BKE_pose_blend_read_lib(BlendLibReader *reader, Object *ob, bPose *pose) return; } - /* always rebuild to match proxy or lib changes, but on Undo */ + /* Always rebuild to match library changes, except on Undo. */ bool rebuild = false; if (!BLO_read_lib_is_undo(reader)) { - if (ob->proxy || ob->id.lib != arm->id.lib) { + if (ob->id.lib != arm->id.lib) { rebuild = true; } } - if (ob->proxy) { - /* sync proxy layer */ - if (pose->proxy_layer) { - arm->layer = pose->proxy_layer; - } - - /* sync proxy active bone */ - if (pose->proxy_act_bone[0]) { - Bone *bone = BKE_armature_find_bone_name(arm, pose->proxy_act_bone); - if (bone) { - arm->act_bone = bone; - } - } - } - LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { BKE_constraint_blend_read_lib(reader, (ID *)ob, &pchan->constraints); diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 42b72a7cd66..1c0b465d202 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -1284,8 +1284,8 @@ void BKE_animdata_main_cb(Main *bmain, ID_AnimData_Edit_Callback func, void *use /* cache files */ ANIMDATA_IDS_CB(bmain->cachefiles.first); - /* hairs */ - ANIMDATA_IDS_CB(bmain->hairs.first); + /* Hair Curves. */ + ANIMDATA_IDS_CB(bmain->hair_curves.first); /* pointclouds */ ANIMDATA_IDS_CB(bmain->pointclouds.first); @@ -1413,8 +1413,8 @@ void BKE_animdata_fix_paths_rename_all_ex(Main *bmain, /* cache files */ RENAMEFIX_ANIM_IDS(bmain->cachefiles.first); - /* hairs */ - RENAMEFIX_ANIM_IDS(bmain->hairs.first); + /* Hair Curves. */ + RENAMEFIX_ANIM_IDS(bmain->hair_curves.first); /* pointclouds */ RENAMEFIX_ANIM_IDS(bmain->pointclouds.first); diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index b5ea68aaadc..c45856adbda 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -3382,8 +3382,8 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, float /* cache files */ EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM); - /* hairs */ - EVAL_ANIM_IDS(main->hairs.first, ADT_RECALC_ANIM); + /* Hair Curves. */ + EVAL_ANIM_IDS(main->hair_curves.first, ADT_RECALC_ANIM); /* pointclouds */ EVAL_ANIM_IDS(main->pointclouds.first, ADT_RECALC_ANIM); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 5704ef6e42f..7feb9d08915 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -69,8 +69,6 @@ #include "CLG_log.h" -static CLG_LogRef LOG = {"bke.armature"}; - /* -------------------------------------------------------------------- */ /** \name Prototypes * \{ */ @@ -2296,161 +2294,6 @@ void BKE_armature_where_is(bArmature *arm) /** \name Pose Rebuild * \{ */ -/* if bone layer is protected, copy the data from from->pose - * when used with linked libraries this copies from the linked pose into the local pose */ -static void pose_proxy_sync(Object *ob, Object *from, int layer_protected) -{ - bPose *pose = ob->pose, *frompose = from->pose; - bPoseChannel *pchan, *pchanp; - bConstraint *con; - int error = 0; - - if (frompose == NULL) { - return; - } - - /* 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) { - if (BKE_pose_channel_find_name(frompose, pchan->name) == NULL) { - CLOG_ERROR(&LOG, - "failed to sync proxy armature because '%s' is missing pose channel '%s'", - from->id.name, - pchan->name); - error = 1; - } - } - } - - if (error) { - return; - } - - /* clear all transformation values from library */ - BKE_pose_rest(frompose, false); - - /* copy over all of the proxy's bone groups */ - /* TODO: for later - * - implement 'local' bone groups as for constraints - * NOTE: this isn't trivial, as bones reference groups by index not by pointer, - * so syncing things correctly needs careful attention */ - BLI_freelistN(&pose->agroups); - BLI_duplicatelist(&pose->agroups, &frompose->agroups); - pose->active_group = frompose->active_group; - - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - pchanp = BKE_pose_channel_find_name(frompose, pchan->name); - - if (UNLIKELY(pchanp == NULL)) { - /* happens for proxies that become invalid because of a missing link - * for regular cases it shouldn't happen at all */ - } - else if (pchan->bone->layer & layer_protected) { - ListBase proxylocal_constraints = {NULL, NULL}; - bPoseChannel pchanw; - - /* copy posechannel to temp, but restore important pointers */ - pchanw = *pchanp; - pchanw.bone = pchan->bone; - pchanw.prev = pchan->prev; - pchanw.next = pchan->next; - pchanw.parent = pchan->parent; - pchanw.child = pchan->child; - pchanw.custom_tx = pchan->custom_tx; - pchanw.bbone_prev = pchan->bbone_prev; - pchanw.bbone_next = pchan->bbone_next; - - pchanw.mpath = pchan->mpath; - pchan->mpath = NULL; - - /* Reset runtime data, we don't want to share that with the proxy. */ - BKE_pose_channel_runtime_reset_on_copy(&pchanw.runtime); - - /* this is freed so copy a copy, else undo crashes */ - if (pchanw.prop) { - pchanw.prop = IDP_CopyProperty(pchanw.prop); - - /* use the values from the existing props */ - if (pchan->prop) { - IDP_SyncGroupValues(pchanw.prop, pchan->prop); - } - } - - /* Constraints - proxy constraints are flushed... local ones are added after - * 1: extract constraints not from proxy (CONSTRAINT_PROXY_LOCAL) from pchan's constraints. - * 2: copy proxy-pchan's constraints on-to new. - * 3: add extracted local constraints back on top. - * - * Note for BKE_constraints_copy: - * When copying constraints, disable 'do_extern' otherwise - * we get the libs direct linked in this blend. - */ - BKE_constraints_proxylocal_extract(&proxylocal_constraints, &pchan->constraints); - BKE_constraints_copy(&pchanw.constraints, &pchanp->constraints, false); - BLI_movelisttolist(&pchanw.constraints, &proxylocal_constraints); - - /* constraints - set target ob pointer to own object */ - for (con = pchanw.constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == from) { - ct->tar = ob; - } - } - - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - - /* free stuff from current channel */ - BKE_pose_channel_free(pchan); - - /* copy data in temp back over to the cleaned-out (but still allocated) original channel */ - *pchan = pchanw; - if (pchan->custom) { - id_us_plus(&pchan->custom->id); - } - } - else { - /* always copy custom shape */ - pchan->custom = pchanp->custom; - if (pchan->custom) { - id_us_plus(&pchan->custom->id); - } - if (pchanp->custom_tx) { - pchan->custom_tx = BKE_pose_channel_find_name(pose, pchanp->custom_tx->name); - } - - /* ID-Property Syncing */ - { - IDProperty *prop_orig = pchan->prop; - if (pchanp->prop) { - pchan->prop = IDP_CopyProperty(pchanp->prop); - if (prop_orig) { - /* copy existing values across when types match */ - IDP_SyncGroupValues(pchan->prop, prop_orig); - } - } - else { - pchan->prop = NULL; - } - if (prop_orig) { - IDP_FreeProperty(prop_orig); - } - } - } - } -} - /** * \param r_last_visited_bone_p: The last bone handled by the last call to this function. */ @@ -2579,16 +2422,6 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_ // printf("rebuild pose %s, %d bones\n", ob->id.name, counter); - /* synchronize protected layers with proxy */ - /* HACK! To preserve 2.7x behavior that you always can pose even locked bones, - * do not do any restoration if this is a COW temp copy! */ - /* Switched back to just NO_MAIN tag, for some reasons (c) - * using COW tag was working this morning, but not anymore... */ - if (ob->proxy != NULL && (ob->id.tag & LIB_TAG_NO_MAIN) == 0) { - BKE_object_copy_proxy_drivers(ob, ob->proxy); - pose_proxy_sync(ob, ob->proxy, arm->layer_protected); - } - BKE_pose_update_constraint_flags(pose); /* for IK detection for example */ pose->flag &= ~POSE_RECALC; diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 05c318663e9..73a396b2cdd 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -850,10 +850,6 @@ void BKE_pose_eval_init(struct Depsgraph *depsgraph, Scene *UNUSED(scene), Objec } BLI_assert(pose->chan_array != NULL || BLI_listbase_is_empty(&pose->chanbase)); - - if (object->proxy != NULL) { - object->proxy->proxy_from = object; - } } void BKE_pose_eval_init_ik(struct Depsgraph *depsgraph, Scene *scene, Object *object) @@ -1070,57 +1066,3 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, Scene *scene, Object *ob BIK_release_tree(scene, object, ctime); pose_eval_cleanup_common(object); } - -void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, Object *object) -{ - BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); - DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); - - BLI_assert(object->pose->chan_array != NULL || BLI_listbase_is_empty(&object->pose->chanbase)); -} - -void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, Object *object) -{ - BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); - DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); -} - -void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, Object *object) -{ - BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); - DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); - pose_eval_cleanup_common(object); -} - -void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph, Object *object, int pchan_index) -{ - const bArmature *armature = (bArmature *)object->data; - if (armature->edbo != NULL) { - return; - } - BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); - bPoseChannel *pchan = pose_pchan_get_indexed(object, pchan_index); - BLI_assert(pchan != NULL); - DEG_debug_print_eval_subdata( - depsgraph, __func__, object->id.name, object, "pchan", pchan->name, pchan); - /* TODO(sergey): Use indexed lookup, once it's guaranteed to be kept - * around for the time while proxies are evaluating. - */ -#if 0 - bPoseChannel *pchan_from = pose_pchan_get_indexed(object->proxy_from, pchan_index); -#else - bPoseChannel *pchan_from = BKE_pose_channel_find_name(object->proxy_from->pose, pchan->name); -#endif - if (pchan_from == NULL) { - printf( - "WARNING: Could not find bone %s in linked ID anymore... " - "You should delete and re-generate your proxy.\n", - pchan->name); - return; - } - BKE_pose_copy_pchan_result(pchan, pchan_from); - copy_dq_dq(&pchan->runtime.deform_dual_quat, &pchan_from->runtime.deform_dual_quat); - BKE_pchan_bbone_segments_cache_copy(pchan, pchan_from); - - pose_channel_flush_to_orig_if_needed(depsgraph, object, pchan); -} diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index ee8ef5e97f7..74eb95add51 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -29,8 +29,8 @@ #include "MEM_guardedalloc.h" #include "DNA_ID.h" +#include "DNA_curves_types.h" #include "DNA_customdata_types.h" -#include "DNA_hair_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" @@ -38,9 +38,9 @@ #include "BLI_string_utf8.h" #include "BKE_attribute.h" +#include "BKE_curves.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" -#include "BKE_hair.h" #include "BKE_pointcloud.h" #include "BKE_report.h" @@ -88,12 +88,12 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) } break; } - case ID_HA: { - Hair *hair = (Hair *)id; - info[ATTR_DOMAIN_POINT].customdata = &hair->pdata; - info[ATTR_DOMAIN_POINT].length = hair->totpoint; - info[ATTR_DOMAIN_CURVE].customdata = &hair->cdata; - info[ATTR_DOMAIN_CURVE].length = hair->totcurve; + case ID_CV: { + Curves *curves = (Curves *)id; + info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data; + info[ATTR_DOMAIN_POINT].length = curves->geometry.point_size; + info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data; + info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_size; break; } default: @@ -301,8 +301,8 @@ bool BKE_id_attribute_required(ID *id, CustomDataLayer *layer) case ID_PT: { return BKE_pointcloud_customdata_required((PointCloud *)id, layer); } - case ID_HA: { - return BKE_hair_customdata_required((Hair *)id, layer); + case ID_CV: { + return BKE_curves_customdata_required((Curves *)id, layer); } default: return false; @@ -372,8 +372,8 @@ int *BKE_id_attributes_active_index_p(ID *id) case ID_ME: { return &((Mesh *)id)->attributes_active_index; } - case ID_HA: { - return &((Hair *)id)->attributes_active_index; + case ID_CV: { + return &((Curves *)id)->attributes_active_index; } default: return NULL; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index cc43a3e26a8..68ab11a013b 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -83,6 +83,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty return &CPPType::get<ColorGeometry4f>(); case CD_PROP_BOOL: return &CPPType::get<bool>(); + case CD_PROP_INT8: + return &CPPType::get<int8_t>(); default: return nullptr; } @@ -109,6 +111,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is<bool>()) { return CD_PROP_BOOL; } + if (type.is<int8_t>()) { + return CD_PROP_INT8; + } return static_cast<CustomDataType>(-1); } @@ -117,16 +122,18 @@ static int attribute_data_type_complexity(const CustomDataType data_type) switch (data_type) { case CD_PROP_BOOL: return 0; - case CD_PROP_INT32: + case CD_PROP_INT8: return 1; - case CD_PROP_FLOAT: + case CD_PROP_INT32: return 2; - case CD_PROP_FLOAT2: + case CD_PROP_FLOAT: return 3; - case CD_PROP_FLOAT3: + case CD_PROP_FLOAT2: return 4; - case CD_PROP_COLOR: + case CD_PROP_FLOAT3: return 5; + case CD_PROP_COLOR: + return 6; #if 0 /* These attribute types are not supported yet. */ case CD_MLOOPCOL: return 3; diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 2cd128081eb..5341266e182 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -140,7 +140,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { private: static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | - CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL; + CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL | + CD_MASK_PROP_INT8; const AttributeDomain domain_; const CustomDataAccessInfo custom_data_access_; diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 6ae19c8036f..86c2593e2e6 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -78,6 +78,23 @@ /** \name High Level `.blend` file read/write. * \{ */ +static bool blendfile_or_libraries_versions_atleast(Main *bmain, + const short versionfile, + const short subversionfile) +{ + if (!MAIN_VERSION_ATLEAST(bmain, versionfile, subversionfile)) { + return false; + } + + LISTBASE_FOREACH (Library *, library, &bmain->libraries) { + if (!MAIN_VERSION_ATLEAST(library, versionfile, subversionfile)) { + return false; + } + } + + return true; +} + static bool foreach_path_clean_cb(BPathForeachPathData *UNUSED(bpath_data), char *path_dst, const char *path_src) @@ -349,10 +366,11 @@ static void setup_app_data(bContext *C, do_versions_ipos_to_animato(bmain); } - /* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */ - /* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a - * version bump and check here. */ - if (mode != LOAD_UNDO && !USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) { + /* NOTE: readfile's `do_version` does not allow to create new IDs, and only operates on a single + * library at a time. This code needs to operate on the whole Main at once. */ + /* NOTE: Check bmain version (i.e. current blend file version), AND the versions of all the + * linked libraries. */ + if (mode != LOAD_UNDO && !blendfile_or_libraries_versions_atleast(bmain, 302, 1)) { BKE_lib_override_library_main_proxy_convert(bmain, reports); } @@ -603,12 +621,12 @@ UserDef *BKE_blendfile_userdef_from_defaults(void) const char *addons[] = { "io_anim_bvh", "io_curve_svg", + "io_import_obj", "io_mesh_ply", "io_mesh_stl", "io_mesh_uv_layout", "io_scene_fbx", "io_scene_gltf2", - "io_scene_obj", "io_scene_x3d", "cycles", "pose_library", diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index 9b3f4c2fae8..025d16007d8 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -993,6 +993,27 @@ static int foreach_libblock_link_append_callback(LibraryIDLinkCallbackData *cb_d /** \name Library link/append code. * \{ */ +static void blendfile_link_append_proxies_convert(Main *bmain, ReportList *reports) +{ + /* NOTE: Do not bother checking file versions here, if there are no proxies to convert this code + * is quite fast anyway. */ + + BlendFileReadReport bf_reports = {.reports = reports}; + BKE_lib_override_library_main_proxy_convert(bmain, &bf_reports); + + if (bf_reports.count.proxies_to_lib_overrides_success != 0 || + bf_reports.count.proxies_to_lib_overrides_failures != 0) { + BKE_reportf( + bf_reports.reports, + RPT_WARNING, + "Proxies have been removed from Blender (%d proxies were automatically converted " + "to library overrides, %d proxies could not be converted and were cleared). " + "Please consider re-saving any library .blend file with the newest Blender version.", + bf_reports.count.proxies_to_lib_overrides_success, + bf_reports.count.proxies_to_lib_overrides_failures); + } +} + void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *reports) { if (lapp_context->num_items == 0) { @@ -1040,10 +1061,6 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * if (item->action != LINK_APPEND_ACT_UNSET) { /* Already set, pass. */ } - if (GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) { - CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); - item->action = LINK_APPEND_ACT_KEEP_LINKED; - } else if (do_reuse_local_id && existing_local_id != NULL) { CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name); item->action = LINK_APPEND_ACT_REUSE_LOCAL; @@ -1098,10 +1115,7 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * local_appended_new_id = id->newid; break; case LINK_APPEND_ACT_MAKE_LOCAL: - BKE_lib_id_make_local(bmain, - id, - make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL | - LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BKE_lib_id_make_local(bmain, id, make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL); BLI_assert(id->newid == NULL); local_appended_new_id = id; break; @@ -1210,55 +1224,11 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * continue; } BLI_assert(ID_IS_LINKED(id)); - - /* Attempt to re-link copied proxy objects. This allows appending of an entire scene - * from another blend file into this one, even when that blend file contains proxified - * armatures that have local references. Since the proxified object needs to be linked - * (not local), this will only work when the "Localize all" checkbox is disabled. - * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ - if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { - Object *ob = (Object *)id; - Object *ob_new = (Object *)id->newid; - bool is_local = false, is_lib = false; - - /* Proxies only work when the proxified object is linked-in from a library. */ - if (!ID_IS_LINKED(ob->proxy)) { - CLOG_WARN(&LOG, - "Proxy object %s will lose its link to %s, because the " - "proxified object is local", - id->newid->name, - ob->proxy->id.name); - continue; - } - - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - - /* We can only switch the proxy'ing to a made-local proxy if it is no longer - * referred to from a library. Not checking for local use; if new local proxy - * was not used locally would be a nasty bug! */ - if (is_local || is_lib) { - CLOG_WARN(&LOG, - "Made-local proxy object %s will lose its link to %s, " - "because the linked-in proxy is referenced (is_local=%i, is_lib=%i)", - id->newid->name, - ob->proxy->id.name, - is_local, - is_lib); - } - else { - /* we can switch the proxy'ing from the linked-in to the made-local proxy. - * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that - * was already allocated by object_make_local() (which called BKE_object_copy). */ - ob_new->proxy = ob->proxy; - ob_new->proxy_group = ob->proxy_group; - ob_new->proxy_from = ob->proxy_from; - ob_new->proxy->proxy_from = ob_new; - ob->proxy = ob->proxy_from = ob->proxy_group = NULL; - } - } } BKE_main_id_newptr_and_tag_clear(bmain); + + blendfile_link_append_proxies_convert(bmain, reports); } void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *reports) @@ -1361,6 +1331,10 @@ void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *re .active_collection = NULL}; loose_data_instantiate(&instantiate_context); } + + if ((lapp_context->params->flag & FILE_LINK) != 0) { + blendfile_link_append_proxies_convert(lapp_context->params->bmain, reports); + } } /** \} */ @@ -1541,7 +1515,6 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, /* Note that in reload case, we also want to replace indirect usages. */ const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | - ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE | (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE); for (item_idx = 0, itemlink = lapp_context->items.list; itemlink; item_idx++, itemlink = itemlink->next) { diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 153a65d67db..c86d4658cc9 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -149,16 +149,9 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) Brush *brush = (Brush *)id; const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; - bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; - bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; - BLI_assert(force_copy == false || force_copy != force_local); - bool is_local = false, is_lib = false; - - /* - only lib users: do nothing (unless force_local is set) - * - only local users: set flag - * - mixed: make copy - */ + bool force_local, force_copy; + BKE_lib_id_make_local_generic_action_define(bmain, id, flags, &force_local, &force_copy); if (brush->clone.image) { /* Special case: ima always local immediately. Clone image should only have one user anyway. */ @@ -171,18 +164,6 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL); } - if (!force_local && !force_copy) { - BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib); - if (lib_local || is_local) { - if (!is_lib) { - force_local = true; - } - else { - force_copy = true; - } - } - } - if (force_local) { BKE_lib_id_clear_library_data(bmain, &brush->id, flags); BKE_lib_id_expand_local(bmain, &brush->id, flags); diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 7940936b64a..d9c637eb177 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -218,7 +218,7 @@ void *BKE_camera_add(Main *bmain, const char *name) return cam; } -float BKE_camera_object_dof_distance(Object *ob) +float BKE_camera_object_dof_distance(const Object *ob) { Camera *cam = (Camera *)ob->data; if (ob->type != OB_CAMERA) { diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index a4f3e84a2bf..275500ba2f6 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -116,12 +116,6 @@ static void cdDM_copyEdgeArray(DerivedMesh *dm, MEdge *r_edge) memcpy(r_edge, cddm->medge, sizeof(*r_edge) * dm->numEdgeData); } -static void cdDM_copyTessFaceArray(DerivedMesh *dm, MFace *r_face) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - memcpy(r_face, cddm->mface, sizeof(*r_face) * dm->numTessFaceData); -} - static void cdDM_copyLoopArray(DerivedMesh *dm, MLoop *r_loop) { CDDerivedMesh *cddm = (CDDerivedMesh *)dm; @@ -147,20 +141,6 @@ static void cdDM_getVertNo(DerivedMesh *dm, int index, float r_no[3]) copy_v3_v3(r_no, cddm->vert_normals[index]); } -static const MeshElemMap *cdDM_getPolyMap(Object *ob, DerivedMesh *dm) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - - if (!cddm->pmap && ob->type == OB_MESH) { - Mesh *me = ob->data; - - BKE_mesh_vert_poly_map_create( - &cddm->pmap, &cddm->pmap_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop); - } - - return cddm->pmap; -} - static void cdDM_recalc_looptri(DerivedMesh *dm) { CDDerivedMesh *cddm = (CDDerivedMesh *)dm; @@ -216,24 +196,17 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->copyVertArray = cdDM_copyVertArray; dm->copyEdgeArray = cdDM_copyEdgeArray; - dm->copyTessFaceArray = cdDM_copyTessFaceArray; dm->copyLoopArray = cdDM_copyLoopArray; dm->copyPolyArray = cdDM_copyPolyArray; - dm->getVertData = DM_get_vert_data; - dm->getEdgeData = DM_get_edge_data; - dm->getTessFaceData = DM_get_tessface_data; dm->getVertDataArray = DM_get_vert_data_layer; dm->getEdgeDataArray = DM_get_edge_data_layer; - dm->getTessFaceDataArray = DM_get_tessface_data_layer; dm->recalcLoopTri = cdDM_recalc_looptri; dm->getVertCo = cdDM_getVertCo; dm->getVertNo = cdDM_getVertNo; - dm->getPolyMap = cdDM_getPolyMap; - dm->release = cdDM_release; return cddm; @@ -265,12 +238,6 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh, dm->deformedOnly = 1; dm->cd_flag = mesh->cd_flag; - if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { - dm->dirty |= DM_DIRTY_NORMALS; - } - /* TODO: DM_DIRTY_TESS_CDLAYERS ? Maybe not though, - * since we probably want to switch to looptris? */ - CustomData_merge(&mesh->vdata, &dm->vertData, cddata_masks.vmask, alloctype, mesh->totvert); CustomData_merge(&mesh->edata, &dm->edgeData, cddata_masks.emask, alloctype, mesh->totedge); CustomData_merge(&mesh->fdata, @@ -282,7 +249,9 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh, CustomData_merge(&mesh->pdata, &dm->polyData, cddata_masks.pmask, alloctype, mesh->totpoly); cddm->mvert = CustomData_get_layer(&dm->vertData, CD_MVERT); - cddm->vert_normals = CustomData_get_layer(&dm->vertData, CD_NORMAL); + /* Though this may be an unnecessary calculation, simply retrieving the layer may return nothing + * or dirty normals. */ + cddm->vert_normals = BKE_mesh_vertex_normals_ensure(mesh); cddm->medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE); cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP); cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); @@ -327,12 +296,6 @@ DerivedMesh *CDDM_copy(DerivedMesh *source) DM_from_template(dm, source, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys); dm->deformedOnly = source->deformedOnly; dm->cd_flag = source->cd_flag; - dm->dirty = source->dirty; - - /* Tessellation data is never copied, so tag it here. - * Only tag dirty layers if we really ignored tessellation faces. - */ - dm->dirty |= DM_DIRTY_TESS_CDLAYERS; CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts); CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index e6ce4eb9440..79f40c1c888 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1094,14 +1094,12 @@ static bool collection_object_remove(Main *bmain, return true; } -bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) +bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Object *ob) { - if (ELEM(NULL, collection, ob)) { + if (ob == NULL) { return false; } - collection = collection_parent_editable_find_recursive(collection); - /* Only case where this pointer can be NULL is when scene itself is linked, this case should * never be reached. */ BLI_assert(collection != NULL); @@ -1122,6 +1120,17 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) return true; } +bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) +{ + if (collection == NULL) { + return false; + } + + collection = collection_parent_editable_find_recursive(collection); + + return BKE_collection_object_add_notest(bmain, collection, ob); +} + void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst) { bool is_instantiated = false; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index f013ef99dde..3b7f91b93c0 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -5850,14 +5850,6 @@ static void add_new_constraint_to_list(Object *ob, bPoseChannel *pchan, bConstra BLI_addtail(list, con); BKE_constraint_unique_name(con, list); - /* if the target list is a list on some PoseChannel belonging to a proxy-protected - * Armature layer, we must tag newly added constraints with a flag which allows them - * to persist after proxy syncing has been done - */ - if (BKE_constraints_proxylocked_owner(ob, pchan)) { - con->flag |= CONSTRAINT_PROXY_LOCAL; - } - /* make this constraint the active one */ BKE_constraints_active_set(list, con); } @@ -6213,45 +6205,6 @@ bool BKE_constraint_is_nonlocal_in_liboverride(const Object *ob, const bConstrai (con == NULL || (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) == 0)); } -/* -------- Constraints and Proxies ------- */ - -void BKE_constraints_proxylocal_extract(ListBase *dst, ListBase *src) -{ - bConstraint *con, *next; - - /* for each tagged constraint, remove from src and move to dst */ - for (con = src->first; con; con = next) { - next = con->next; - - /* check if tagged */ - if (con->flag & CONSTRAINT_PROXY_LOCAL) { - BLI_remlink(src, con); - BLI_addtail(dst, con); - } - } -} - -bool BKE_constraints_proxylocked_owner(Object *ob, bPoseChannel *pchan) -{ - /* Currently, constraints can only be on object or bone level */ - if (ob && ob->proxy) { - if (ob->pose && pchan) { - bArmature *arm = ob->data; - - /* On bone-level, check if bone is on proxy-protected layer */ - if ((pchan->bone) && (pchan->bone->layer & arm->layer_protected)) { - return true; - } - } - else { - /* FIXME: constraints on object-level are not handled well yet */ - return true; - } - } - - return false; -} - /* -------- Target-Matrix Stuff ------- */ void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph, diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c index 6bbb9957b03..0bf83ed5036 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.c @@ -41,6 +41,7 @@ #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_multires.h" +#include "BKE_report.h" #include "DEG_depsgraph_query.h" @@ -109,7 +110,7 @@ float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object /* disable subsurf temporal, get mapped cos, and enable it */ if (modifiers_disable_subsurf_temporary(scene_eval, obedit_eval)) { /* need to make new derivemesh */ - makeDerivedMesh(depsgraph, scene_eval, obedit_eval, editmesh_eval, &CD_MASK_BAREMESH); + makeDerivedMesh(depsgraph, scene_eval, obedit_eval, &CD_MASK_BAREMESH); } /* now get the cage */ @@ -193,13 +194,10 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me, float (*mappedcos)[3], float (*quats)[4]) { - MVert *mvert = me->mvert; - for (int i = 0; i < me->totvert; i++, mvert++) { - mvert->flag &= ~ME_VERT_TMP_TAG; - } + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(me->totvert, __func__); /* first store two sets of tangent vectors in vertices, we derive it just from the face-edges */ - mvert = me->mvert; + MVert *mvert = me->mvert; MPoly *mp = me->mpoly; MLoop *mloop = me->mloop; @@ -209,7 +207,7 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me, MLoop *ml_prev = &ml_next[mp->totloop - 2]; for (int j = 0; j < mp->totloop; j++) { - if ((mvert[ml_curr->v].flag & ME_VERT_TMP_TAG) == 0) { + if (!BLI_BITMAP_TEST(vert_tag, ml_curr->v)) { const float *co_prev, *co_curr, *co_next; /* orig */ const float *vd_prev, *vd_curr, *vd_next; /* deform */ @@ -232,7 +230,7 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me, set_crazy_vertex_quat( quats[ml_curr->v], co_curr, co_next, co_prev, vd_curr, vd_next, vd_prev); - mvert[ml_curr->v].flag |= ME_VERT_TMP_TAG; + BLI_BITMAP_ENABLE(vert_tag, ml_curr->v); } ml_prev = ml_curr; @@ -240,6 +238,8 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me, ml_next++; } } + + MEM_freeN(vert_tag); } int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgraph, @@ -516,3 +516,85 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, } } } + +/* -------------------------------------------------------------------- */ +/** \name Crazyspace API + * \{ */ + +void BKE_crazyspace_api_eval(Depsgraph *depsgraph, + Scene *scene, + Object *object, + struct ReportList *reports) +{ + if (object->runtime.crazyspace_deform_imats != NULL || + object->runtime.crazyspace_deform_cos != NULL) { + return; + } + + if (object->type != OB_MESH) { + BKE_report(reports, + RPT_ERROR, + "Crazyspace transformation is only available for Mesh type of objects"); + return; + } + + const Mesh *mesh = (const Mesh *)object->data; + object->runtime.crazyspace_num_verts = mesh->totvert; + BKE_crazyspace_build_sculpt(depsgraph, + scene, + object, + &object->runtime.crazyspace_deform_imats, + &object->runtime.crazyspace_deform_cos); +} + +void BKE_crazyspace_api_displacement_to_deformed(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement[3], + float r_displacement_deformed[3]) +{ + if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_num_verts) { + BKE_reportf(reports, + RPT_ERROR, + "Invalid vertex index %d (expected to be within 0 to %d range)", + vertex_index, + object->runtime.crazyspace_num_verts); + return; + } + + mul_v3_m3v3(r_displacement_deformed, + object->runtime.crazyspace_deform_imats[vertex_index], + displacement); +} + +void BKE_crazyspace_api_displacement_to_original(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement_deformed[3], + float r_displacement[3]) +{ + if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_num_verts) { + BKE_reportf(reports, + RPT_ERROR, + "Invalid vertex index %d (expected to be within 0 to %d range))", + vertex_index, + object->runtime.crazyspace_num_verts); + return; + } + + float mat[3][3]; + if (!invert_m3_m3(mat, object->runtime.crazyspace_deform_imats[vertex_index])) { + copy_v3_v3(r_displacement, displacement_deformed); + return; + } + + mul_v3_m3v3(r_displacement, mat, displacement_deformed); +} + +void BKE_crazyspace_api_eval_clear(Object *object) +{ + MEM_SAFE_FREE(object->runtime.crazyspace_deform_imats); + MEM_SAFE_FREE(object->runtime.crazyspace_deform_cos); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 70edaccb244..dda2b5076a8 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -899,7 +899,7 @@ float BKE_nurb_calc_length(const Nurb *nu, int resolution) pntsit = points + 3; } - while (--b) { + while (--b > 0) { length += len_v3v3(prevpntsit, pntsit); prevpntsit = pntsit; pntsit += 3; diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 073d9d18a04..833b2fe99ec 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -66,8 +66,8 @@ static void vert_extrude_to_mesh_data(const Spline &spline, if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1]; - edge.v1 = vert_offset; - edge.v2 = vert_offset + eval_size - 1; + edge.v1 = vert_offset + eval_size - 1; + edge.v2 = vert_offset; edge.flag = ME_LOOSEEDGE; } diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc new file mode 100644 index 00000000000..f5672e9b288 --- /dev/null +++ b/source/blender/blenkernel/intern/curves.cc @@ -0,0 +1,473 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#include <cmath> +#include <cstring> + +#include "MEM_guardedalloc.h" + +#include "DNA_curves_types.h" +#include "DNA_defaults.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" + +#include "BLI_index_range.hh" +#include "BLI_listbase.h" +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" +#include "BLI_rand.hh" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_anim_data.h" +#include "BKE_curves.h" +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_idtype.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_lib_remap.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLT_translation.h" + +#include "DEG_depsgraph_query.h" + +#include "BLO_read_write.h" + +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::RandomNumberGenerator; + +static const char *ATTR_POSITION = "position"; +static const char *ATTR_RADIUS = "radius"; + +static void curves_random(Curves *curves); + +static void curves_init_data(ID *id) +{ + Curves *curves = (Curves *)id; + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(curves, id)); + + MEMCPY_STRUCT_AFTER(curves, DNA_struct_default_get(Curves), id); + + CustomData_reset(&curves->geometry.point_data); + CustomData_reset(&curves->geometry.curve_data); + + CustomData_add_layer_named(&curves->geometry.point_data, + CD_PROP_FLOAT3, + CD_CALLOC, + nullptr, + curves->geometry.point_size, + ATTR_POSITION); + CustomData_add_layer_named(&curves->geometry.point_data, + CD_PROP_FLOAT, + CD_CALLOC, + nullptr, + curves->geometry.point_size, + ATTR_RADIUS); + + BKE_curves_update_customdata_pointers(curves); + + curves_random(curves); +} + +static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) +{ + Curves *curves_dst = (Curves *)id_dst; + const Curves *curves_src = (const Curves *)id_src; + curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat)); + + curves_dst->geometry.point_size = curves_src->geometry.point_size; + curves_dst->geometry.curve_size = curves_src->geometry.curve_size; + + const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; + CustomData_copy(&curves_src->geometry.point_data, + &curves_dst->geometry.point_data, + CD_MASK_ALL, + alloc_type, + curves_dst->geometry.point_size); + CustomData_copy(&curves_src->geometry.curve_data, + &curves_dst->geometry.curve_data, + CD_MASK_ALL, + alloc_type, + curves_dst->geometry.curve_size); + BKE_curves_update_customdata_pointers(curves_dst); + + curves_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(curves_src->geometry.offsets)); + + curves_dst->batch_cache = nullptr; +} + +static void curves_free_data(ID *id) +{ + Curves *curves = (Curves *)id; + BKE_animdata_free(&curves->id, false); + + BKE_curves_batch_cache_free(curves); + + CustomData_free(&curves->geometry.point_data, curves->geometry.point_size); + CustomData_free(&curves->geometry.curve_data, curves->geometry.curve_size); + + MEM_SAFE_FREE(curves->geometry.offsets); + + MEM_SAFE_FREE(curves->mat); +} + +static void curves_foreach_id(ID *id, LibraryForeachIDData *data) +{ + Curves *curves = (Curves *)id; + for (int i = 0; i < curves->totcol; i++) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->mat[i], IDWALK_CB_USER); + } +} + +static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Curves *curves = (Curves *)id; + + CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomData_blend_write_prepare( + &curves->geometry.point_data, &players, players_buff, ARRAY_SIZE(players_buff)); + CustomData_blend_write_prepare( + &curves->geometry.curve_data, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); + + /* Write LibData */ + BLO_write_id_struct(writer, Curves, id_address, &curves->id); + BKE_id_blend_write(writer, &curves->id); + + /* Direct data */ + CustomData_blend_write(writer, + &curves->geometry.point_data, + players, + curves->geometry.point_size, + CD_MASK_ALL, + &curves->id); + CustomData_blend_write(writer, + &curves->geometry.curve_data, + clayers, + curves->geometry.curve_size, + CD_MASK_ALL, + &curves->id); + + BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.offsets); + + BLO_write_pointer_array(writer, curves->totcol, curves->mat); + if (curves->adt) { + BKE_animdata_blend_write(writer, curves->adt); + } + + /* Remove temporary data. */ + if (players && players != players_buff) { + MEM_freeN(players); + } + if (clayers && clayers != clayers_buff) { + MEM_freeN(clayers); + } +} + +static void curves_blend_read_data(BlendDataReader *reader, ID *id) +{ + Curves *curves = (Curves *)id; + BLO_read_data_address(reader, &curves->adt); + BKE_animdata_blend_read_data(reader, curves->adt); + + /* Geometry */ + CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size); + CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.point_size); + BKE_curves_update_customdata_pointers(curves); + + BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.offsets); + + /* Materials */ + BLO_read_pointer_array(reader, (void **)&curves->mat); +} + +static void curves_blend_read_lib(BlendLibReader *reader, ID *id) +{ + Curves *curves = (Curves *)id; + for (int a = 0; a < curves->totcol; a++) { + BLO_read_id_address(reader, curves->id.lib, &curves->mat[a]); + } +} + +static void curves_blend_read_expand(BlendExpander *expander, ID *id) +{ + Curves *curves = (Curves *)id; + for (int a = 0; a < curves->totcol; a++) { + BLO_expand(expander, curves->mat[a]); + } +} + +IDTypeInfo IDType_ID_CV = { + /*id_code */ ID_CV, + /*id_filter */ FILTER_ID_CV, + /*main_listbase_index */ INDEX_ID_CV, + /*struct_size */ sizeof(Curves), + /*name */ "Hair Curves", + /*name_plural */ "Hair Curves", + /*translation_context */ BLT_I18NCONTEXT_ID_CURVES, + /*flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, + /*asset_type_info */ nullptr, + + /*init_data */ curves_init_data, + /*copy_data */ curves_copy_data, + /*free_data */ curves_free_data, + /*make_local */ nullptr, + /*foreach_id */ curves_foreach_id, + /*foreach_cache */ nullptr, + /*foreach_path */ nullptr, + /*owner_get */ nullptr, + + /*blend_write */ curves_blend_write, + /*blend_read_data */ curves_blend_read_data, + /*blend_read_lib */ curves_blend_read_lib, + /*blend_read_expand */ curves_blend_read_expand, + + /*blend_read_undo_preserve */ nullptr, + + /*lib_override_apply_post */ nullptr, +}; + +static void curves_random(Curves *curves) +{ + CurvesGeometry &geometry = curves->geometry; + const int numpoints = 8; + + geometry.curve_size = 500; + + geometry.curve_size = 500; + geometry.point_size = geometry.curve_size * numpoints; + + curves->geometry.offsets = (int *)MEM_calloc_arrayN( + curves->geometry.curve_size + 1, sizeof(int), __func__); + CustomData_realloc(&geometry.point_data, geometry.point_size); + CustomData_realloc(&geometry.curve_data, geometry.curve_size); + BKE_curves_update_customdata_pointers(curves); + + MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1}; + MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size}; + MutableSpan<float> radii{geometry.radius, geometry.point_size}; + + for (const int i : offsets.index_range()) { + geometry.offsets[i] = numpoints * i; + } + + RandomNumberGenerator rng; + + for (int i = 0; i < geometry.curve_size; i++) { + const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); + MutableSpan<float3> curve_positions = positions.slice(curve_range); + MutableSpan<float> curve_radii = radii.slice(curve_range); + + const float theta = 2.0f * M_PI * rng.get_float(); + const float phi = saacosf(2.0f * rng.get_float() - 1.0f); + + float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)}; + no = blender::math::normalize(no); + + float3 co = no; + for (int key = 0; key < numpoints; key++) { + float t = key / (float)(numpoints - 1); + curve_positions[key] = co; + curve_radii[key] = 0.02f * (1.0f - t); + + float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f; + co += (offset + no) / numpoints; + } + } +} + +void *BKE_curves_add(Main *bmain, const char *name) +{ + Curves *curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, name)); + + return curves; +} + +BoundBox *BKE_curves_boundbox_get(Object *ob) +{ + BLI_assert(ob->type == OB_CURVES); + Curves *curves = static_cast<Curves *>(ob->data); + + if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { + return ob->runtime.bb; + } + + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = MEM_cnew<BoundBox>(__func__); + + float min[3], max[3]; + INIT_MINMAX(min, max); + + float(*curves_co)[3] = curves->geometry.position; + float *curves_radius = curves->geometry.radius; + for (int a = 0; a < curves->geometry.point_size; a++) { + float *co = curves_co[a]; + float radius = (curves_radius) ? curves_radius[a] : 0.0f; + const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius}; + const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius}; + DO_MIN(co_min, min); + DO_MAX(co_max, max); + } + + BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); + } + + return ob->runtime.bb; +} + +void BKE_curves_update_customdata_pointers(Curves *curves) +{ + curves->geometry.position = (float(*)[3])CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT3, ATTR_POSITION); + curves->geometry.radius = (float *)CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT, ATTR_RADIUS); +} + +bool BKE_curves_customdata_required(Curves *UNUSED(curves), CustomDataLayer *layer) +{ + return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, ATTR_POSITION); +} + +/* Dependency Graph */ + +Curves *BKE_curves_new_for_eval(const Curves *curves_src, int totpoint, int totcurve) +{ + Curves *curves_dst = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + + STRNCPY(curves_dst->id.name, curves_src->id.name); + curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat)); + curves_dst->totcol = curves_src->totcol; + + curves_dst->geometry.point_size = totpoint; + curves_dst->geometry.curve_size = totcurve; + CustomData_copy(&curves_src->geometry.point_data, + &curves_dst->geometry.point_data, + CD_MASK_ALL, + CD_CALLOC, + totpoint); + CustomData_copy(&curves_src->geometry.curve_data, + &curves_dst->geometry.curve_data, + CD_MASK_ALL, + CD_CALLOC, + totcurve); + BKE_curves_update_customdata_pointers(curves_dst); + + return curves_dst; +} + +Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference) +{ + int flags = LIB_ID_COPY_LOCALIZE; + + if (reference) { + flags |= LIB_ID_COPY_CD_REFERENCE; + } + + Curves *result = (Curves *)BKE_id_copy_ex(nullptr, &curves_src->id, nullptr, flags); + return result; +} + +static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph, + struct Scene *scene, + Object *object, + Curves *curves_input) +{ + Curves *curves = curves_input; + + /* Modifier evaluation modes. */ + const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); + const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; + ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE; + const ModifierEvalContext mectx = {depsgraph, object, apply_flag}; + + /* Get effective list of modifiers to execute. Some effects like shape keys + * are added as virtual modifiers before the user created modifiers. */ + VirtualModifierData virtualModifierData; + ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData); + + /* Evaluate modifiers. */ + for (; md; md = md->next) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); + + if (!BKE_modifier_is_enabled(scene, md, required_mode)) { + continue; + } + + if ((mti->type == eModifierTypeType_OnlyDeform) && + (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) { + /* Ensure we are not modifying the input. */ + if (curves == curves_input) { + curves = BKE_curves_copy_for_eval(curves, true); + } + + /* Ensure we are not overwriting referenced data. */ + CustomData_duplicate_referenced_layer_named(&curves->geometry.point_data, + CD_PROP_FLOAT3, + ATTR_POSITION, + curves->geometry.point_size); + BKE_curves_update_customdata_pointers(curves); + + /* Created deformed coordinates array on demand. */ + mti->deformVerts( + md, &mectx, nullptr, curves->geometry.position, curves->geometry.point_size); + } + } + + return curves; +} + +void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) +{ + /* Free any evaluated data and restore original data. */ + BKE_object_free_derived_caches(object); + + /* Evaluate modifiers. */ + Curves *curves = static_cast<Curves *>(object->data); + Curves *curves_eval = curves_evaluate_modifiers(depsgraph, scene, object, curves); + + /* Assign evaluated object. */ + const bool is_owned = (curves != curves_eval); + BKE_object_eval_assign_data(object, &curves_eval->id, is_owned); +} + +/* Draw Cache */ + +void (*BKE_curves_batch_cache_dirty_tag_cb)(Curves *curves, int mode) = nullptr; +void (*BKE_curves_batch_cache_free_cb)(Curves *curves) = nullptr; + +void BKE_curves_batch_cache_dirty_tag(Curves *curves, int mode) +{ + if (curves->batch_cache) { + BKE_curves_batch_cache_dirty_tag_cb(curves, mode); + } +} + +void BKE_curves_batch_cache_free(Curves *curves) +{ + if (curves->batch_cache) { + BKE_curves_batch_cache_free_cb(curves); + } +} diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 5e3beab9b72..c5cc077c8ae 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -31,7 +31,6 @@ #include "DNA_ID.h" #include "DNA_customdata_types.h" -#include "DNA_hair_types.h" #include "DNA_meshdata_types.h" #include "BLI_bitmap.h" @@ -1793,10 +1792,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(float[3]), "vec3f", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 44: CD_RADIUS */ {sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, - /* 45: CD_HAIRCURVE */ - {sizeof(HairCurve), "HairCurve", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, - /* 46: CD_HAIRMAPPING */ - {sizeof(HairMapping), "HairMapping", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, + /* 45: CD_PROP_INT8 */ + {sizeof(int8_t), "MInt8Property", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, + /* 46: CD_HAIRMAPPING */ /* UNUSED */ + {-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 47: CD_PROP_COLOR */ {sizeof(MPropCol), "MPropCol", @@ -1914,7 +1913,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDCustomLoopNormal", "CDSculptFaceGroups", /* 43-46 */ "CDHairPoint", - "CDHairCurve", + "CDPropInt8", "CDHairMapping", "CDPoint", "CDPropCol", @@ -2427,6 +2426,13 @@ int CustomData_get_stencil_layer(const CustomData *data, int type) return (layer_index != -1) ? data->layers[layer_index].active_mask : -1; } +const char *CustomData_get_active_layer_name(const struct CustomData *data, const int type) +{ + /* Get the layer index of the active layer of this type. */ + const int layer_index = CustomData_get_active_layer_index(data, type); + return layer_index < 0 ? nullptr : data->layers[layer_index].name; +} + void CustomData_set_layer_active(CustomData *data, int type, int n) { for (int i = 0; i < data->totlayer; i++) { @@ -2773,6 +2779,24 @@ void CustomData_free_layers(CustomData *data, int type, int totelem) } } +void CustomData_free_layers_anonymous(struct CustomData *data, int totelem) +{ + while (true) { + bool found_anonymous_layer = false; + for (int i = 0; i < data->totlayer; i++) { + const CustomDataLayer *layer = &data->layers[i]; + if (layer->anonymous_id != nullptr) { + CustomData_free_layer(data, layer->type, totelem, i); + found_anonymous_layer = true; + break; + } + } + if (!found_anonymous_layer) { + break; + } + } +} + bool CustomData_has_layer(const CustomData *data, int type) { return (CustomData_get_layer_index(data, type) != -1); diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 64e0427a810..bc5a0ed1538 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -1518,7 +1518,7 @@ static void dynamic_paint_set_init_color_tex_to_vcol_cb( multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage, false); if (texres.tin > pPoint[vert].color[3]) { - copy_v3_v3(pPoint[vert].color, &texres.tr); + copy_v3_v3(pPoint[vert].color, texres.trgba); pPoint[vert].color[3] = texres.tin; } } @@ -1559,7 +1559,7 @@ static void dynamic_paint_set_init_color_tex_to_imseq_cb( multitex_ext_safe(tex, uv_final, &texres, NULL, scene_color_manage, false); /* apply color */ - copy_v3_v3(pPoint[i].color, &texres.tr); + copy_v3_v3(pPoint[i].color, texres.trgba); pPoint[i].color[3] = texres.tin; } @@ -1985,9 +1985,6 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * } MEM_freeN(fcolor); - - /* Mark tessellated CD layers as dirty. */ - // result->dirty |= DM_DIRTY_TESS_CDLAYERS; } /* vertex group paint */ else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 6ef811c46c7..0774a1a3d88 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -39,6 +39,8 @@ #include "BKE_mesh_wrapper.h" #include "BKE_object.h" +#include "DEG_depsgraph_query.h" + BMEditMesh *BKE_editmesh_create(BMesh *bm) { BMEditMesh *em = MEM_callocN(sizeof(BMEditMesh), __func__); @@ -51,9 +53,6 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em) BMEditMesh *em_copy = MEM_callocN(sizeof(BMEditMesh), __func__); *em_copy = *em; - em_copy->mesh_eval_cage = em_copy->mesh_eval_final = NULL; - em_copy->bb_cage = NULL; - em_copy->bm = BM_mesh_copy(em->bm); /* The tessellation is NOT calculated on the copy here, @@ -194,22 +193,8 @@ void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, }); } -void BKE_editmesh_free_derived_caches(BMEditMesh *em) -{ - if (em->mesh_eval_cage) { - BKE_id_free(NULL, em->mesh_eval_cage); - } - if (em->mesh_eval_final && em->mesh_eval_final != em->mesh_eval_cage) { - BKE_id_free(NULL, em->mesh_eval_final); - } - em->mesh_eval_cage = em->mesh_eval_final = NULL; - - MEM_SAFE_FREE(em->bb_cage); -} - void BKE_editmesh_free_data(BMEditMesh *em) { - BKE_editmesh_free_derived_caches(em); if (em->looptris) { MEM_freeN(em->looptris); @@ -283,13 +268,15 @@ const float (*BKE_editmesh_vert_coords_when_deformed(struct Depsgraph *depsgraph *r_is_alloc = false; Mesh *me = ob->data; + Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object_eval); if ((me->runtime.edit_data != NULL) && (me->runtime.edit_data->vertexCos != NULL)) { /* Deformed, and we have deformed coords already. */ coords = me->runtime.edit_data->vertexCos; } - else if ((em->mesh_eval_final != NULL) && - (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { + else if ((editmesh_eval_final != NULL) && + (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { /* If this is an edit-mesh type, leave NULL as we can use the vertex coords. */ } else { @@ -334,18 +321,18 @@ void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, Mesh *me) } } -BoundBox *BKE_editmesh_cage_boundbox_get(BMEditMesh *em) +BoundBox *BKE_editmesh_cage_boundbox_get(struct Object *object, BMEditMesh *UNUSED(em)) { - if (em->bb_cage == NULL) { + if (object->runtime.editmesh_bb_cage == NULL) { float min[3], max[3]; INIT_MINMAX(min, max); - if (em->mesh_eval_cage) { - BKE_mesh_wrapper_minmax(em->mesh_eval_cage, min, max); + if (object->runtime.editmesh_eval_cage) { + BKE_mesh_wrapper_minmax(object->runtime.editmesh_eval_cage, min, max); } - em->bb_cage = MEM_callocN(sizeof(BoundBox), "BMEditMesh.bb_cage"); - BKE_boundbox_init_from_minmax(em->bb_cage, min, max); + object->runtime.editmesh_bb_cage = MEM_callocN(sizeof(BoundBox), "BMEditMesh.bb_cage"); + BKE_boundbox_init_from_minmax(object->runtime.editmesh_bb_cage, min, max); } - return em->bb_cage; + return object->runtime.editmesh_bb_cage; } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index bbf9e9edfd2..1f1915f60ca 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -908,9 +908,9 @@ static void do_texture_effector(EffectorCache *eff, eff->pd->tex, tex_co, NULL, NULL, 0, result, 0, NULL, scene_color_manage, false); if (hasrgb && mode == PFIELD_TEX_RGB) { - force[0] = (0.5f - result->tr) * strength; - force[1] = (0.5f - result->tg) * strength; - force[2] = (0.5f - result->tb) * strength; + force[0] = (0.5f - result->trgba[0]) * strength; + force[1] = (0.5f - result->trgba[1]) * strength; + force[2] = (0.5f - result->trgba[2]) * strength; } else if (nabla != 0) { strength /= nabla; @@ -933,7 +933,8 @@ static void do_texture_effector(EffectorCache *eff, /* generate intensity if texture only has rgb value */ if (hasrgb & TEX_RGB) { for (int i = 0; i < 4; i++) { - result[i].tin = (1.0f / 3.0f) * (result[i].tr + result[i].tg + result[i].tb); + result[i].tin = (1.0f / 3.0f) * + (result[i].trgba[0] + result[i].trgba[1] + result[i].trgba[2]); } } force[0] = (result[0].tin - result[1].tin) * strength; @@ -943,12 +944,12 @@ static void do_texture_effector(EffectorCache *eff, else { /*PFIELD_TEX_CURL*/ float dbdy, dgdz, drdz, dbdx, dgdx, drdy; - dbdy = result[2].tb - result[0].tb; - dgdz = result[3].tg - result[0].tg; - drdz = result[3].tr - result[0].tr; - dbdx = result[1].tb - result[0].tb; - dgdx = result[1].tg - result[0].tg; - drdy = result[2].tr - result[0].tr; + dbdy = result[2].trgba[2] - result[0].trgba[2]; + dgdz = result[3].trgba[1] - result[0].trgba[1]; + drdz = result[3].trgba[0] - result[0].trgba[0]; + dbdx = result[1].trgba[2] - result[0].trgba[2]; + dgdx = result[1].trgba[1] - result[0].trgba[1]; + drdy = result[2].trgba[0] - result[0].trgba[0]; force[0] = (dbdy - dgdz) * strength; force[1] = (drdz - dbdx) * strength; diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c index ce30f80ba65..e71217fea9e 100644 --- a/source/blender/blenkernel/intern/fcurve_driver.c +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -88,14 +88,6 @@ typedef struct DriverVarTypeInfo { /** \name Driver Target Utilities * \{ */ -static ID *dtar_id_ensure_proxy_from(ID *id) -{ - if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) { - return (ID *)(((Object *)id)->proxy_from); - } - return id; -} - /** * Helper function to obtain a value using RNA from the specified source * (for evaluating drivers). @@ -113,7 +105,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) return 0.0f; } - id = dtar_id_ensure_proxy_from(dtar->id); + id = dtar->id; /* Error check for missing pointer. */ if (id == NULL) { @@ -217,7 +209,7 @@ bool driver_get_variable_property(ChannelDriver *driver, return false; } - id = dtar_id_ensure_proxy_from(dtar->id); + id = dtar->id; /* Error check for missing pointer. */ if (id == NULL) { @@ -273,7 +265,7 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) short valid_targets = 0; DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + Object *ob = (Object *)dtar->id; /* Check if this target has valid data. */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { @@ -328,7 +320,7 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) for (int i = 0; i < 2; i++) { /* Get pointer to loc values to store in. */ DriverTarget *dtar = &dvar->targets[i]; - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + Object *ob = (Object *)dtar->id; bPoseChannel *pchan; /* After the checks above, the targets should be valid here. */ @@ -389,7 +381,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) /* NOTE: for now, these are all just world-space */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { /* Get pointer to loc values to store in. */ - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + Object *ob = (Object *)dtar->id; bPoseChannel *pchan; float tmp_loc[3]; @@ -472,7 +464,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) { DriverTarget *dtar = &dvar->targets[0]; - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + Object *ob = (Object *)dtar->id; bPoseChannel *pchan; float mat[4][4]; float oldEul[3] = {0.0f, 0.0f, 0.0f}; diff --git a/source/blender/blenkernel/intern/hair.cc b/source/blender/blenkernel/intern/hair.cc deleted file mode 100644 index b7ba159f631..00000000000 --- a/source/blender/blenkernel/intern/hair.cc +++ /dev/null @@ -1,435 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup bke - */ - -#include <cmath> -#include <cstring> - -#include "MEM_guardedalloc.h" - -#include "DNA_defaults.h" -#include "DNA_hair_types.h" -#include "DNA_material_types.h" -#include "DNA_object_types.h" - -#include "BLI_listbase.h" -#include "BLI_math_base.h" -#include "BLI_math_vec_types.hh" -#include "BLI_rand.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "BKE_anim_data.h" -#include "BKE_customdata.h" -#include "BKE_global.h" -#include "BKE_hair.h" -#include "BKE_idtype.h" -#include "BKE_lib_id.h" -#include "BKE_lib_query.h" -#include "BKE_lib_remap.h" -#include "BKE_main.h" -#include "BKE_modifier.h" -#include "BKE_object.h" - -#include "BLT_translation.h" - -#include "DEG_depsgraph_query.h" - -#include "BLO_read_write.h" - -using blender::float3; - -static const char *HAIR_ATTR_POSITION = "position"; -static const char *HAIR_ATTR_RADIUS = "radius"; - -/* Hair datablock */ - -static void hair_random(Hair *hair); - -static void hair_init_data(ID *id) -{ - Hair *hair = (Hair *)id; - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(hair, id)); - - MEMCPY_STRUCT_AFTER(hair, DNA_struct_default_get(Hair), id); - - CustomData_reset(&hair->pdata); - CustomData_reset(&hair->cdata); - - CustomData_add_layer_named( - &hair->pdata, CD_PROP_FLOAT3, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_POSITION); - CustomData_add_layer_named( - &hair->pdata, CD_PROP_FLOAT, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_RADIUS); - CustomData_add_layer(&hair->cdata, CD_HAIRCURVE, CD_CALLOC, nullptr, hair->totcurve); - BKE_hair_update_customdata_pointers(hair); - - hair_random(hair); -} - -static void hair_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) -{ - Hair *hair_dst = (Hair *)id_dst; - const Hair *hair_src = (const Hair *)id_src; - hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat)); - - const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; - CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, alloc_type, hair_dst->totpoint); - CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, alloc_type, hair_dst->totcurve); - BKE_hair_update_customdata_pointers(hair_dst); - - hair_dst->batch_cache = nullptr; -} - -static void hair_free_data(ID *id) -{ - Hair *hair = (Hair *)id; - BKE_animdata_free(&hair->id, false); - - BKE_hair_batch_cache_free(hair); - - CustomData_free(&hair->pdata, hair->totpoint); - CustomData_free(&hair->cdata, hair->totcurve); - - MEM_SAFE_FREE(hair->mat); -} - -static void hair_foreach_id(ID *id, LibraryForeachIDData *data) -{ - Hair *hair = (Hair *)id; - for (int i = 0; i < hair->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, hair->mat[i], IDWALK_CB_USER); - } -} - -static void hair_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - Hair *hair = (Hair *)id; - - CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); - - /* Write LibData */ - BLO_write_id_struct(writer, Hair, id_address, &hair->id); - BKE_id_blend_write(writer, &hair->id); - - /* Direct data */ - CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id); - CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id); - - BLO_write_pointer_array(writer, hair->totcol, hair->mat); - if (hair->adt) { - BKE_animdata_blend_write(writer, hair->adt); - } - - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } - if (clayers && clayers != clayers_buff) { - MEM_freeN(clayers); - } -} - -static void hair_blend_read_data(BlendDataReader *reader, ID *id) -{ - Hair *hair = (Hair *)id; - BLO_read_data_address(reader, &hair->adt); - BKE_animdata_blend_read_data(reader, hair->adt); - - /* Geometry */ - CustomData_blend_read(reader, &hair->pdata, hair->totpoint); - CustomData_blend_read(reader, &hair->cdata, hair->totcurve); - BKE_hair_update_customdata_pointers(hair); - - /* Materials */ - BLO_read_pointer_array(reader, (void **)&hair->mat); -} - -static void hair_blend_read_lib(BlendLibReader *reader, ID *id) -{ - Hair *hair = (Hair *)id; - for (int a = 0; a < hair->totcol; a++) { - BLO_read_id_address(reader, hair->id.lib, &hair->mat[a]); - } -} - -static void hair_blend_read_expand(BlendExpander *expander, ID *id) -{ - Hair *hair = (Hair *)id; - for (int a = 0; a < hair->totcol; a++) { - BLO_expand(expander, hair->mat[a]); - } -} - -IDTypeInfo IDType_ID_HA = { - /*id_code */ ID_HA, - /*id_filter */ FILTER_ID_HA, - /*main_listbase_index */ INDEX_ID_HA, - /*struct_size */ sizeof(Hair), - /*name */ "Hair", - /*name_plural */ "hairs", - /*translation_context */ BLT_I18NCONTEXT_ID_HAIR, - /*flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, - /*asset_type_info */ nullptr, - - /*init_data */ hair_init_data, - /*copy_data */ hair_copy_data, - /*free_data */ hair_free_data, - /*make_local */ nullptr, - /*foreach_id */ hair_foreach_id, - /*foreach_cache */ nullptr, - /*foreach_path */ nullptr, - /*owner_get */ nullptr, - - /*blend_write */ hair_blend_write, - /*blend_read_data */ hair_blend_read_data, - /*blend_read_lib */ hair_blend_read_lib, - /*blend_read_expand */ hair_blend_read_expand, - - /*blend_read_undo_preserve */ nullptr, - - /*lib_override_apply_post */ nullptr, -}; - -static void hair_random(Hair *hair) -{ - const int numpoints = 8; - - hair->totcurve = 500; - hair->totpoint = hair->totcurve * numpoints; - - CustomData_realloc(&hair->pdata, hair->totpoint); - CustomData_realloc(&hair->cdata, hair->totcurve); - BKE_hair_update_customdata_pointers(hair); - - RNG *rng = BLI_rng_new(0); - - for (int i = 0; i < hair->totcurve; i++) { - HairCurve *curve = &hair->curves[i]; - curve->firstpoint = i * numpoints; - curve->numpoints = numpoints; - - float theta = 2.0f * M_PI * BLI_rng_get_float(rng); - float phi = saacosf(2.0f * BLI_rng_get_float(rng) - 1.0f); - - float no[3] = {sinf(theta) * sinf(phi), cosf(theta) * sinf(phi), cosf(phi)}; - normalize_v3(no); - - float co[3]; - copy_v3_v3(co, no); - - float(*curve_co)[3] = hair->co + curve->firstpoint; - float *curve_radius = hair->radius + curve->firstpoint; - for (int key = 0; key < numpoints; key++) { - float t = key / (float)(numpoints - 1); - copy_v3_v3(curve_co[key], co); - curve_radius[key] = 0.02f * (1.0f - t); - - float offset[3] = {2.0f * BLI_rng_get_float(rng) - 1.0f, - 2.0f * BLI_rng_get_float(rng) - 1.0f, - 2.0f * BLI_rng_get_float(rng) - 1.0f}; - add_v3_v3(offset, no); - madd_v3_v3fl(co, offset, 1.0f / numpoints); - } - } - - BLI_rng_free(rng); -} - -void *BKE_hair_add(Main *bmain, const char *name) -{ - Hair *hair = static_cast<Hair *>(BKE_id_new(bmain, ID_HA, name)); - - return hair; -} - -BoundBox *BKE_hair_boundbox_get(Object *ob) -{ - BLI_assert(ob->type == OB_HAIR); - Hair *hair = static_cast<Hair *>(ob->data); - - if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { - return ob->runtime.bb; - } - - if (ob->runtime.bb == nullptr) { - ob->runtime.bb = MEM_cnew<BoundBox>(__func__); - - float min[3], max[3]; - INIT_MINMAX(min, max); - - float(*hair_co)[3] = hair->co; - float *hair_radius = hair->radius; - for (int a = 0; a < hair->totpoint; a++) { - float *co = hair_co[a]; - float radius = (hair_radius) ? hair_radius[a] : 0.0f; - const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius}; - const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius}; - DO_MIN(co_min, min); - DO_MAX(co_max, max); - } - - BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); - } - - return ob->runtime.bb; -} - -void BKE_hair_update_customdata_pointers(Hair *hair) -{ - hair->co = (float(*)[3])CustomData_get_layer_named( - &hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION); - hair->radius = (float *)CustomData_get_layer_named( - &hair->pdata, CD_PROP_FLOAT, HAIR_ATTR_RADIUS); - hair->curves = (HairCurve *)CustomData_get_layer(&hair->cdata, CD_HAIRCURVE); - hair->mapping = (HairMaping *)CustomData_get_layer(&hair->cdata, CD_HAIRMAPPING); -} - -bool BKE_hair_customdata_required(Hair *UNUSED(hair), CustomDataLayer *layer) -{ - return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, HAIR_ATTR_POSITION); -} - -/* Dependency Graph */ - -Hair *BKE_hair_new_for_eval(const Hair *hair_src, int totpoint, int totcurve) -{ - Hair *hair_dst = static_cast<Hair *>(BKE_id_new_nomain(ID_HA, nullptr)); - - STRNCPY(hair_dst->id.name, hair_src->id.name); - hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat)); - hair_dst->totcol = hair_src->totcol; - - hair_dst->totpoint = totpoint; - hair_dst->totcurve = totcurve; - CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint); - CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, CD_CALLOC, totcurve); - BKE_hair_update_customdata_pointers(hair_dst); - - return hair_dst; -} - -Hair *BKE_hair_copy_for_eval(Hair *hair_src, bool reference) -{ - int flags = LIB_ID_COPY_LOCALIZE; - - if (reference) { - flags |= LIB_ID_COPY_CD_REFERENCE; - } - - Hair *result = (Hair *)BKE_id_copy_ex(nullptr, &hair_src->id, nullptr, flags); - return result; -} - -static Hair *hair_evaluate_modifiers(struct Depsgraph *depsgraph, - struct Scene *scene, - Object *object, - Hair *hair_input) -{ - Hair *hair = hair_input; - - /* Modifier evaluation modes. */ - const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); - const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; - ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE; - const ModifierEvalContext mectx = {depsgraph, object, apply_flag}; - - /* Get effective list of modifiers to execute. Some effects like shape keys - * are added as virtual modifiers before the user created modifiers. */ - VirtualModifierData virtualModifierData; - ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData); - - /* Evaluate modifiers. */ - for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); - - if (!BKE_modifier_is_enabled(scene, md, required_mode)) { - continue; - } - - if ((mti->type == eModifierTypeType_OnlyDeform) && - (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) { - /* Ensure we are not modifying the input. */ - if (hair == hair_input) { - hair = BKE_hair_copy_for_eval(hair, true); - } - - /* Ensure we are not overwriting referenced data. */ - CustomData_duplicate_referenced_layer_named( - &hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION, hair->totpoint); - BKE_hair_update_customdata_pointers(hair); - - /* Created deformed coordinates array on demand. */ - mti->deformVerts(md, &mectx, nullptr, hair->co, hair->totpoint); - } - else if (mti->modifyHair) { - /* Ensure we are not modifying the input. */ - if (hair == hair_input) { - hair = BKE_hair_copy_for_eval(hair, true); - } - - Hair *hair_next = mti->modifyHair(md, &mectx, hair); - - if (hair_next && hair_next != hair) { - /* If the modifier returned a new hair, release the old one. */ - if (hair != hair_input) { - BKE_id_free(nullptr, hair); - } - hair = hair_next; - } - } - } - - return hair; -} - -void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) -{ - /* Free any evaluated data and restore original data. */ - BKE_object_free_derived_caches(object); - - /* Evaluate modifiers. */ - Hair *hair = static_cast<Hair *>(object->data); - Hair *hair_eval = hair_evaluate_modifiers(depsgraph, scene, object, hair); - - /* Assign evaluated object. */ - const bool is_owned = (hair != hair_eval); - BKE_object_eval_assign_data(object, &hair_eval->id, is_owned); -} - -/* Draw Cache */ - -void (*BKE_hair_batch_cache_dirty_tag_cb)(Hair *hair, int mode) = nullptr; -void (*BKE_hair_batch_cache_free_cb)(Hair *hair) = nullptr; - -void BKE_hair_batch_cache_dirty_tag(Hair *hair, int mode) -{ - if (hair->batch_cache) { - BKE_hair_batch_cache_dirty_tag_cb(hair, mode); - } -} - -void BKE_hair_batch_cache_free(Hair *hair) -{ - if (hair->batch_cache) { - BKE_hair_batch_cache_free_cb(hair); - } -} diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index e6fd6c14d42..e9c8df76351 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -110,7 +110,7 @@ static void id_type_init(void) INIT_TYPE(ID_CF); INIT_TYPE(ID_WS); INIT_TYPE(ID_LP); - INIT_TYPE(ID_HA); + INIT_TYPE(ID_CV); INIT_TYPE(ID_PT); INIT_TYPE(ID_VO); INIT_TYPE(ID_SIM); @@ -237,7 +237,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(CU); CASE_IDFILTER(GD); CASE_IDFILTER(GR); - CASE_IDFILTER(HA); + CASE_IDFILTER(CV); CASE_IDFILTER(IM); CASE_IDFILTER(LA); CASE_IDFILTER(LS); @@ -286,7 +286,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(CU); CASE_IDFILTER(GD); CASE_IDFILTER(GR); - CASE_IDFILTER(HA); + CASE_IDFILTER(CV); CASE_IDFILTER(IM); CASE_IDFILTER(LA); CASE_IDFILTER(LS); @@ -334,7 +334,7 @@ int BKE_idtype_idcode_to_index(const short idcode) CASE_IDINDEX(CU); CASE_IDINDEX(GD); CASE_IDINDEX(GR); - CASE_IDINDEX(HA); + CASE_IDINDEX(CV); CASE_IDINDEX(IM); CASE_IDINDEX(IP); CASE_IDINDEX(KE); @@ -393,7 +393,7 @@ short BKE_idtype_idcode_from_index(const int index) CASE_IDCODE(CU); CASE_IDCODE(GD); CASE_IDCODE(GR); - CASE_IDCODE(HA); + CASE_IDCODE(CV); CASE_IDCODE(IM); CASE_IDCODE(IP); CASE_IDCODE(KE); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 6947fcc16a3..130691dc7ff 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -134,6 +134,22 @@ static void image_runtime_reset_on_copy(struct Image *image) { image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); BLI_mutex_init(image->runtime.cache_mutex); + + image->runtime.partial_update_register = NULL; + image->runtime.partial_update_user = NULL; +} + +static void image_runtime_free_data(struct Image *image) +{ + BLI_mutex_end(image->runtime.cache_mutex); + MEM_freeN(image->runtime.cache_mutex); + image->runtime.cache_mutex = NULL; + + if (image->runtime.partial_update_user != NULL) { + BKE_image_partial_update_free(image->runtime.partial_update_user); + image->runtime.partial_update_user = NULL; + } + BKE_image_partial_update_register_free(image); } static void image_init_data(ID *id) @@ -213,10 +229,8 @@ static void image_free_data(ID *id) BKE_previewimg_free(&image->preview); BLI_freelistN(&image->tiles); - BLI_freelistN(&image->gpu_refresh_areas); - BLI_mutex_end(image->runtime.cache_mutex); - MEM_freeN(image->runtime.cache_mutex); + image_runtime_free_data(image); } static void image_foreach_cache(ID *id, @@ -321,7 +335,8 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres ima->cache = NULL; ima->gpuflag = 0; BLI_listbase_clear(&ima->anims); - BLI_listbase_clear(&ima->gpu_refresh_areas); + ima->runtime.partial_update_register = NULL; + ima->runtime.partial_update_user = NULL; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { @@ -401,7 +416,6 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id) ima->lastused = 0; ima->gpuflag = 0; - BLI_listbase_clear(&ima->gpu_refresh_areas); image_runtime_reset(ima); } diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index c82de02e52a..eaee1fd2c30 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -38,6 +38,7 @@ #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_partial_update.hh" #include "BKE_main.h" #include "GPU_capabilities.h" @@ -46,6 +47,10 @@ #include "PIL_time.h" +using namespace blender::bke::image::partial_update; + +extern "C" { + /* Prototypes. */ static void gpu_free_unused_buffers(); static void image_free_gpu(Image *ima, const bool immediate); @@ -53,14 +58,6 @@ static void image_free_gpu_limited_scale(Image *ima); static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h); -/* Internal structs. */ -#define IMA_PARTIAL_REFRESH_TILE_SIZE 256 -struct ImagePartialRefresh { - struct ImagePartialRefresh *next, *prev; - int tile_x; - int tile_y; -}; - bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) { if (image) { @@ -337,6 +334,48 @@ static void image_update_reusable_textures(Image *ima, } } +static void image_gpu_texture_partial_update_changes_available( + Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes) +{ + while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) { + const int tile_offset_x = changes.changed_region.region.xmin; + const int tile_offset_y = changes.changed_region.region.ymin; + const int tile_width = min_ii(changes.tile_data.tile_buffer->x, + BLI_rcti_size_x(&changes.changed_region.region)); + const int tile_height = min_ii(changes.tile_data.tile_buffer->y, + BLI_rcti_size_y(&changes.changed_region.region)); + image_update_gputexture_ex(image, + changes.tile_data.tile, + changes.tile_data.tile_buffer, + tile_offset_x, + tile_offset_y, + tile_width, + tile_height); + } +} + +static void image_gpu_texture_try_partial_update(Image *image, ImageUser *iuser) +{ + PartialUpdateChecker<ImageTileData> checker(image, iuser, image->runtime.partial_update_user); + PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes(); + switch (changes.get_result_code()) { + case ePartialUpdateCollectResult::FullUpdateNeeded: { + image_free_gpu(image, true); + break; + } + + case ePartialUpdateCollectResult::PartialChangesDetected: { + image_gpu_texture_partial_update_changes_available(image, changes); + break; + } + + case ePartialUpdateCollectResult::NoChangesDetected: { + /* GPUTextures are up to date. */ + break; + } + } +} + static GPUTexture *image_get_gpu_texture(Image *ima, ImageUser *iuser, ImBuf *ibuf, @@ -370,31 +409,20 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } #undef GPU_FLAGS_TO_CHECK - /* Check if image has been updated and tagged to be updated (full or partial). */ - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (((ima->gpuflag & IMA_GPU_REFRESH) != 0) || - ((ibuf == nullptr || tile == nullptr) && ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) { - image_free_gpu(ima, true); - BLI_freelistN(&ima->gpu_refresh_areas); - ima->gpuflag &= ~(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH); - } - else if (ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) { - BLI_assert(ibuf); - BLI_assert(tile); - ImagePartialRefresh *refresh_area; - while (( - refresh_area = static_cast<ImagePartialRefresh *>(BLI_pophead(&ima->gpu_refresh_areas)))) { - const int tile_offset_x = refresh_area->tile_x * IMA_PARTIAL_REFRESH_TILE_SIZE; - const int tile_offset_y = refresh_area->tile_y * IMA_PARTIAL_REFRESH_TILE_SIZE; - const int tile_width = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->x - tile_offset_x); - const int tile_height = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->y - tile_offset_y); - image_update_gputexture_ex( - ima, tile, ibuf, tile_offset_x, tile_offset_y, tile_width, tile_height); - MEM_freeN(refresh_area); - } - ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH; + /* TODO(jbakker): We should replace the IMA_GPU_REFRESH flag with a call to + * BKE_image-partial_update_mark_full_update. Although the flag is quicker it leads to double + * administration. */ + if ((ima->gpuflag & IMA_GPU_REFRESH) != 0) { + BKE_image_partial_update_mark_full_update(ima); + ima->gpuflag &= ~IMA_GPU_REFRESH; + } + + if (ima->runtime.partial_update_user == nullptr) { + ima->runtime.partial_update_user = BKE_image_partial_update_create(ima); } + image_gpu_texture_try_partial_update(ima, iuser); + /* Tag as in active use for garbage collector. */ BKE_image_tag_time(ima); @@ -417,6 +445,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, /* Check if we have a valid image. If not, we return a dummy * texture with zero bind-code so we don't keep trying. */ + ImageTile *tile = BKE_image_get_tile(ima, 0); if (tile == nullptr) { *tex = image_gpu_texture_error_create(textarget); return *tex; @@ -427,8 +456,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (ibuf_intern == nullptr) { ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, nullptr); if (ibuf_intern == nullptr) { - *tex = image_gpu_texture_error_create(textarget); - return *tex; + return image_gpu_texture_error_create(textarget); } } @@ -477,15 +505,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima, break; } - /* if `ibuf` was given, we don't own the `ibuf_intern` */ - if (ibuf == nullptr) { - BKE_image_release_ibuf(ima, ibuf_intern, nullptr); - } - if (*tex) { GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); } + if (ibuf != ibuf_intern) { + BKE_image_release_ibuf(ima, ibuf_intern, nullptr); + } + return *tex; } @@ -903,87 +930,29 @@ static void image_update_gputexture_ex( void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h) { + ImageTile *image_tile = BKE_image_get_tile_from_iuser(ima, iuser); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, nullptr); - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - - if ((ibuf == nullptr) || (w == 0) || (h == 0)) { - /* Full reload of texture. */ - BKE_image_free_gputextures(ima); - } - image_update_gputexture_ex(ima, tile, ibuf, x, y, w, h); + BKE_image_update_gputexture_delayed(ima, image_tile, ibuf, x, y, w, h); BKE_image_release_ibuf(ima, ibuf, nullptr); } -void BKE_image_update_gputexture_delayed( - struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h) +void BKE_image_update_gputexture_delayed(struct Image *ima, + struct ImageTile *image_tile, + struct ImBuf *ibuf, + int x, + int y, + int w, + int h) { /* Check for full refresh. */ - if (ibuf && x == 0 && y == 0 && w == ibuf->x && h == ibuf->y) { - ima->gpuflag |= IMA_GPU_REFRESH; - } - /* Check if we can promote partial refresh to a full refresh. */ - if ((ima->gpuflag & (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) == - (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) { - ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH; - BLI_freelistN(&ima->gpu_refresh_areas); - } - /* Image is already marked for complete refresh. */ - if (ima->gpuflag & IMA_GPU_REFRESH) { - return; - } - - /* Schedule the tiles that covers the requested area. */ - const int start_tile_x = x / IMA_PARTIAL_REFRESH_TILE_SIZE; - const int start_tile_y = y / IMA_PARTIAL_REFRESH_TILE_SIZE; - const int end_tile_x = (x + w) / IMA_PARTIAL_REFRESH_TILE_SIZE; - const int end_tile_y = (y + h) / IMA_PARTIAL_REFRESH_TILE_SIZE; - const int num_tiles_x = (end_tile_x + 1) - (start_tile_x); - const int num_tiles_y = (end_tile_y + 1) - (start_tile_y); - const int num_tiles = num_tiles_x * num_tiles_y; - const bool allocate_on_heap = BLI_BITMAP_SIZE(num_tiles) > 16; - BLI_bitmap *requested_tiles = nullptr; - if (allocate_on_heap) { - requested_tiles = BLI_BITMAP_NEW(num_tiles, __func__); + if (ibuf != nullptr && ima->source != IMA_SRC_TILED && x == 0 && y == 0 && w == ibuf->x && + h == ibuf->y) { + BKE_image_partial_update_mark_full_update(ima); } else { - requested_tiles = BLI_BITMAP_NEW_ALLOCA(num_tiles); - } - - /* Mark the tiles that have already been requested. They don't need to be requested again. */ - int num_tiles_not_scheduled = num_tiles; - LISTBASE_FOREACH (ImagePartialRefresh *, area, &ima->gpu_refresh_areas) { - if (area->tile_x < start_tile_x || area->tile_x > end_tile_x || area->tile_y < start_tile_y || - area->tile_y > end_tile_y) { - continue; - } - int requested_tile_index = (area->tile_x - start_tile_x) + - (area->tile_y - start_tile_y) * num_tiles_x; - BLI_BITMAP_ENABLE(requested_tiles, requested_tile_index); - num_tiles_not_scheduled--; - if (num_tiles_not_scheduled == 0) { - break; - } - } - - /* Schedule the tiles that aren't requested yet. */ - if (num_tiles_not_scheduled) { - int tile_index = 0; - for (int tile_y = start_tile_y; tile_y <= end_tile_y; tile_y++) { - for (int tile_x = start_tile_x; tile_x <= end_tile_x; tile_x++) { - if (!BLI_BITMAP_TEST_BOOL(requested_tiles, tile_index)) { - ImagePartialRefresh *area = static_cast<ImagePartialRefresh *>( - MEM_mallocN(sizeof(ImagePartialRefresh), __func__)); - area->tile_x = tile_x; - area->tile_y = tile_y; - BLI_addtail(&ima->gpu_refresh_areas, area); - } - tile_index++; - } - } - ima->gpuflag |= IMA_GPU_PARTIAL_REFRESH; - } - if (allocate_on_heap) { - MEM_freeN(requested_tiles); + rcti dirty_region; + BLI_rcti_init(&dirty_region, x, x + w, y, y + h); + BKE_image_partial_update_mark_region(ima, image_tile, ibuf, &dirty_region); } } @@ -1016,3 +985,4 @@ void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) } /** \} */ +} diff --git a/source/blender/blenkernel/intern/image_partial_update.cc b/source/blender/blenkernel/intern/image_partial_update.cc new file mode 100644 index 00000000000..876e5276e5a --- /dev/null +++ b/source/blender/blenkernel/intern/image_partial_update.cc @@ -0,0 +1,599 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ +/** + * \file image_gpu_partial_update.cc + * \ingroup bke + * + * To reduce the overhead of image processing this file contains a mechanism to detect areas of the + * image that are changed. These areas are organized in chunks. Changes that happen over time are + * organized in changesets. + * + * A common use case is to update #GPUTexture for drawing where only that part is uploaded that + * only changed. + * + * Usage: + * + * ``` + * Image *image = ...; + * ImBuf *image_buffer = ...; + * + * // Partial_update_user should be kept for the whole session where the changes needs to be + * // tracked. Keep this instance alive as long as you need to track image changes. + * + * PartialUpdateUser *partial_update_user = BKE_image_partial_update_create(image); + * + * ... + * + * switch (BKE_image_partial_update_collect_changes(image, image_buffer)) + * { + * case ePartialUpdateCollectResult::FullUpdateNeeded: + * // Unable to do partial updates. Perform a full update. + * break; + * case ePartialUpdateCollectResult::PartialChangesDetected: + * PartialUpdateRegion change; + * while (BKE_image_partial_update_get_next_change(partial_update_user, &change) == + * ePartialUpdateIterResult::ChangeAvailable){ + * // Do something with the change. + * } + * case ePartialUpdateCollectResult::NoChangesDetected: + * break; + * } + * + * ... + * + * // Free partial_update_user. + * BKE_image_partial_update_free(partial_update_user); + * + * ``` + */ + +#include <optional> + +#include "BKE_image.h" +#include "BKE_image_partial_update.hh" + +#include "DNA_image_types.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BLI_vector.hh" + +namespace blender::bke::image::partial_update { + +/** \brief Size of chunks to track changes. */ +constexpr int CHUNK_SIZE = 256; + +/** + * \brief Max number of changesets to keep in history. + * + * A higher number would need more memory and processing + * to calculate a changeset, but would lead to do partial updates for requests that don't happen + * every frame. + * + * A to small number would lead to more full updates when changes couldn't be reconstructed from + * the available history. + */ +constexpr int MAX_HISTORY_LEN = 4; + +/** + * \brief get the chunk number for the give pixel coordinate. + * + * As chunks are squares the this member can be used for both x and y axis. + */ +static int chunk_number_for_pixel(int pixel_offset) +{ + int chunk_offset = pixel_offset / CHUNK_SIZE; + if (pixel_offset < 0) { + chunk_offset -= 1; + } + return chunk_offset; +} + +struct PartialUpdateUserImpl; +struct PartialUpdateRegisterImpl; + +/** + * Wrap PartialUpdateUserImpl to its C-struct (PartialUpdateUser). + */ +static struct PartialUpdateUser *wrap(PartialUpdateUserImpl *user) +{ + return static_cast<struct PartialUpdateUser *>(static_cast<void *>(user)); +} + +/** + * Unwrap the PartialUpdateUser C-struct to its CPP counterpart (PartialUpdateUserImpl). + */ +static PartialUpdateUserImpl *unwrap(struct PartialUpdateUser *user) +{ + return static_cast<PartialUpdateUserImpl *>(static_cast<void *>(user)); +} + +/** + * Wrap PartialUpdateRegisterImpl to its C-struct (PartialUpdateRegister). + */ +static struct PartialUpdateRegister *wrap(PartialUpdateRegisterImpl *partial_update_register) +{ + return static_cast<struct PartialUpdateRegister *>(static_cast<void *>(partial_update_register)); +} + +/** + * Unwrap the PartialUpdateRegister C-struct to its CPP counterpart (PartialUpdateRegisterImpl). + */ +static PartialUpdateRegisterImpl *unwrap(struct PartialUpdateRegister *partial_update_register) +{ + return static_cast<PartialUpdateRegisterImpl *>(static_cast<void *>(partial_update_register)); +} + +using TileNumber = int32_t; +using ChangesetID = int64_t; +constexpr ChangesetID UnknownChangesetID = -1; + +struct PartialUpdateUserImpl { + /** \brief last changeset id that was seen by this user. */ + ChangesetID last_changeset_id = UnknownChangesetID; + + /** \brief regions that have been updated. */ + Vector<PartialUpdateRegion> updated_regions; + +#ifdef NDEBUG + /** \brief reference to image to validate correct API usage. */ + const void *debug_image_; +#endif + + /** + * \brief Clear the list of updated regions. + * + * Updated regions should be cleared at the start of #BKE_image_partial_update_collect_changes so + * the + */ + void clear_updated_regions() + { + updated_regions.clear(); + } +}; + +/** + * \brief Dirty chunks of an ImageTile. + * + * Internally dirty tiles are grouped together in change sets to make sure that the correct + * answer can be built for different users reducing the amount of merges. + */ +struct TileChangeset { + private: + /** \brief Dirty flag for each chunk. */ + std::vector<bool> chunk_dirty_flags_; + /** \brief are there dirty/ */ + bool has_dirty_chunks_ = false; + + public: + /** \brief Width of the tile in pixels. */ + int tile_width; + /** \brief Height of the tile in pixels. */ + int tile_height; + /** \brief Number of chunks along the x-axis. */ + int chunk_x_len; + /** \brief Number of chunks along the y-axis. */ + int chunk_y_len; + + TileNumber tile_number; + + void clear() + { + init_chunks(chunk_x_len, chunk_y_len); + } + + /** + * \brief Update the resolution of the tile. + * + * \returns true: resolution has been updated. + * false: resolution was unchanged. + */ + bool update_resolution(const ImBuf *image_buffer) + { + if (tile_width == image_buffer->x && tile_height == image_buffer->y) { + return false; + } + + tile_width = image_buffer->x; + tile_height = image_buffer->y; + + int chunk_x_len = tile_width / CHUNK_SIZE; + int chunk_y_len = tile_height / CHUNK_SIZE; + init_chunks(chunk_x_len, chunk_y_len); + return true; + } + + void mark_region(const rcti *updated_region) + { + int start_x_chunk = chunk_number_for_pixel(updated_region->xmin); + int end_x_chunk = chunk_number_for_pixel(updated_region->xmax - 1); + int start_y_chunk = chunk_number_for_pixel(updated_region->ymin); + int end_y_chunk = chunk_number_for_pixel(updated_region->ymax - 1); + + /* Clamp tiles to tiles in image. */ + start_x_chunk = max_ii(0, start_x_chunk); + start_y_chunk = max_ii(0, start_y_chunk); + end_x_chunk = min_ii(chunk_x_len - 1, end_x_chunk); + end_y_chunk = min_ii(chunk_y_len - 1, end_y_chunk); + + /* Early exit when no tiles need to be updated. */ + if (start_x_chunk >= chunk_x_len) { + return; + } + if (start_y_chunk >= chunk_y_len) { + return; + } + if (end_x_chunk < 0) { + return; + } + if (end_y_chunk < 0) { + return; + } + + mark_chunks_dirty(start_x_chunk, start_y_chunk, end_x_chunk, end_y_chunk); + } + + void mark_chunks_dirty(int start_x_chunk, int start_y_chunk, int end_x_chunk, int end_y_chunk) + { + for (int chunk_y = start_y_chunk; chunk_y <= end_y_chunk; chunk_y++) { + for (int chunk_x = start_x_chunk; chunk_x <= end_x_chunk; chunk_x++) { + int chunk_index = chunk_y * chunk_x_len + chunk_x; + chunk_dirty_flags_[chunk_index] = true; + } + } + has_dirty_chunks_ = true; + } + + bool has_dirty_chunks() const + { + return has_dirty_chunks_; + } + + void init_chunks(int chunk_x_len_, int chunk_y_len_) + { + chunk_x_len = chunk_x_len_; + chunk_y_len = chunk_y_len_; + const int chunk_len = chunk_x_len * chunk_y_len; + const int previous_chunk_len = chunk_dirty_flags_.size(); + + chunk_dirty_flags_.resize(chunk_len); + /* Fast exit. When the changeset was already empty no need to + * re-initialize the chunk_validity. */ + if (!has_dirty_chunks()) { + return; + } + for (int index = 0; index < min_ii(chunk_len, previous_chunk_len); index++) { + chunk_dirty_flags_[index] = false; + } + has_dirty_chunks_ = false; + } + + /** \brief Merge the given changeset into the receiver. */ + void merge(const TileChangeset &other) + { + BLI_assert(chunk_x_len == other.chunk_x_len); + BLI_assert(chunk_y_len == other.chunk_y_len); + const int chunk_len = chunk_x_len * chunk_y_len; + + for (int chunk_index = 0; chunk_index < chunk_len; chunk_index++) { + chunk_dirty_flags_[chunk_index] = chunk_dirty_flags_[chunk_index] | + other.chunk_dirty_flags_[chunk_index]; + } + has_dirty_chunks_ |= other.has_dirty_chunks_; + } + + /** \brief has a chunk changed inside this changeset. */ + bool is_chunk_dirty(int chunk_x, int chunk_y) const + { + const int chunk_index = chunk_y * chunk_x_len + chunk_x; + return chunk_dirty_flags_[chunk_index]; + } +}; + +/** \brief Changeset keeping track of changes for an image */ +struct Changeset { + private: + Vector<TileChangeset> tiles; + + public: + /** \brief Keep track if any of the tiles have dirty chunks. */ + bool has_dirty_chunks; + + /** + * \brief Retrieve the TileChangeset for the given ImageTile. + * + * When the TileChangeset isn't found, it will be added. + */ + TileChangeset &operator[](const ImageTile *image_tile) + { + for (TileChangeset &tile_changeset : tiles) { + if (tile_changeset.tile_number == image_tile->tile_number) { + return tile_changeset; + } + } + + TileChangeset tile_changeset; + tile_changeset.tile_number = image_tile->tile_number; + tiles.append_as(tile_changeset); + + return tiles.last(); + } + + /** \brief Does this changeset contain data for the given tile. */ + bool has_tile(const ImageTile *image_tile) + { + for (TileChangeset &tile_changeset : tiles) { + if (tile_changeset.tile_number == image_tile->tile_number) { + return true; + } + } + return false; + } + + /** \brief Clear this changeset. */ + void clear() + { + tiles.clear(); + has_dirty_chunks = false; + } +}; + +/** + * \brief Partial update changes stored inside the image runtime. + * + * The PartialUpdateRegisterImpl will keep track of changes over time. Changes are groups inside + * TileChangesets. + */ +struct PartialUpdateRegisterImpl { + /** \brief changeset id of the first changeset kept in #history. */ + ChangesetID first_changeset_id; + /** \brief changeset id of the top changeset kept in #history. */ + ChangesetID last_changeset_id; + + /** \brief history of changesets. */ + Vector<Changeset> history; + /** \brief The current changeset. New changes will be added to this changeset. */ + Changeset current_changeset; + + void update_resolution(const ImageTile *image_tile, const ImBuf *image_buffer) + { + TileChangeset &tile_changeset = current_changeset[image_tile]; + const bool has_dirty_chunks = tile_changeset.has_dirty_chunks(); + const bool resolution_changed = tile_changeset.update_resolution(image_buffer); + + if (has_dirty_chunks && resolution_changed && !history.is_empty()) { + mark_full_update(); + } + } + + void mark_full_update() + { + history.clear(); + last_changeset_id++; + current_changeset.clear(); + first_changeset_id = last_changeset_id; + } + + void mark_region(const ImageTile *image_tile, const rcti *updated_region) + { + TileChangeset &tile_changeset = current_changeset[image_tile]; + tile_changeset.mark_region(updated_region); + current_changeset.has_dirty_chunks |= tile_changeset.has_dirty_chunks(); + } + + void ensure_empty_changeset() + { + if (!current_changeset.has_dirty_chunks) { + /* No need to create a new changeset when previous changeset does not contain any dirty + * tiles. */ + return; + } + commit_current_changeset(); + limit_history(); + } + + /** \brief Move the current changeset to the history and resets the current changeset. */ + void commit_current_changeset() + { + history.append_as(std::move(current_changeset)); + current_changeset.clear(); + last_changeset_id++; + } + + /** \brief Limit the number of items in the changeset. */ + void limit_history() + { + const int num_items_to_remove = max_ii(history.size() - MAX_HISTORY_LEN, 0); + if (num_items_to_remove == 0) { + return; + } + history.remove(0, num_items_to_remove); + first_changeset_id += num_items_to_remove; + } + + /** + * /brief Check if data is available to construct the update tiles for the given + * changeset_id. + * + * The update tiles can be created when changeset id is between + */ + bool can_construct(ChangesetID changeset_id) + { + return changeset_id >= first_changeset_id; + } + + /** + * \brief collect all historic changes since a given changeset. + */ + std::optional<TileChangeset> changed_tile_chunks_since(const ImageTile *image_tile, + const ChangesetID from_changeset) + { + std::optional<TileChangeset> changed_chunks = std::nullopt; + for (int index = from_changeset - first_changeset_id; index < history.size(); index++) { + if (!history[index].has_tile(image_tile)) { + continue; + } + + TileChangeset &tile_changeset = history[index][image_tile]; + if (!changed_chunks.has_value()) { + changed_chunks = std::make_optional<TileChangeset>(); + changed_chunks->init_chunks(tile_changeset.chunk_x_len, tile_changeset.chunk_y_len); + changed_chunks->tile_number = image_tile->tile_number; + } + + changed_chunks->merge(tile_changeset); + } + return changed_chunks; + } +}; + +static PartialUpdateRegister *image_partial_update_register_ensure(Image *image) +{ + if (image->runtime.partial_update_register == nullptr) { + PartialUpdateRegisterImpl *partial_update_register = MEM_new<PartialUpdateRegisterImpl>( + __func__); + image->runtime.partial_update_register = wrap(partial_update_register); + } + return image->runtime.partial_update_register; +} + +ePartialUpdateCollectResult BKE_image_partial_update_collect_changes(Image *image, + PartialUpdateUser *user) +{ + PartialUpdateUserImpl *user_impl = unwrap(user); +#ifdef NDEBUG + BLI_assert(image == user_impl->debug_image_); +#endif + + user_impl->clear_updated_regions(); + + PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image)); + partial_updater->ensure_empty_changeset(); + + if (!partial_updater->can_construct(user_impl->last_changeset_id)) { + user_impl->last_changeset_id = partial_updater->last_changeset_id; + return ePartialUpdateCollectResult::FullUpdateNeeded; + } + + /* Check if there are changes since last invocation for the user. */ + if (user_impl->last_changeset_id == partial_updater->last_changeset_id) { + return ePartialUpdateCollectResult::NoChangesDetected; + } + + /* Collect changed tiles. */ + LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { + std::optional<TileChangeset> changed_chunks = partial_updater->changed_tile_chunks_since( + tile, user_impl->last_changeset_id); + /* Check if chunks of this tile are dirty. */ + if (!changed_chunks.has_value()) { + continue; + } + if (!changed_chunks->has_dirty_chunks()) { + continue; + } + + /* Convert tiles in the changeset to rectangles that are dirty. */ + for (int chunk_y = 0; chunk_y < changed_chunks->chunk_y_len; chunk_y++) { + for (int chunk_x = 0; chunk_x < changed_chunks->chunk_x_len; chunk_x++) { + if (!changed_chunks->is_chunk_dirty(chunk_x, chunk_y)) { + continue; + } + + PartialUpdateRegion region; + region.tile_number = tile->tile_number; + BLI_rcti_init(®ion.region, + chunk_x * CHUNK_SIZE, + (chunk_x + 1) * CHUNK_SIZE, + chunk_y * CHUNK_SIZE, + (chunk_y + 1) * CHUNK_SIZE); + user_impl->updated_regions.append_as(region); + } + } + } + + user_impl->last_changeset_id = partial_updater->last_changeset_id; + return ePartialUpdateCollectResult::PartialChangesDetected; +} + +ePartialUpdateIterResult BKE_image_partial_update_get_next_change(PartialUpdateUser *user, + PartialUpdateRegion *r_region) +{ + PartialUpdateUserImpl *user_impl = unwrap(user); + if (user_impl->updated_regions.is_empty()) { + return ePartialUpdateIterResult::Finished; + } + PartialUpdateRegion region = user_impl->updated_regions.pop_last(); + *r_region = region; + return ePartialUpdateIterResult::ChangeAvailable; +} + +} // namespace blender::bke::image::partial_update + +extern "C" { + +using namespace blender::bke::image::partial_update; + +// TODO(jbakker): cleanup parameter. +struct PartialUpdateUser *BKE_image_partial_update_create(const struct Image *image) +{ + PartialUpdateUserImpl *user_impl = MEM_new<PartialUpdateUserImpl>(__func__); + +#ifdef NDEBUG + user_impl->debug_image_ = image; +#else + UNUSED_VARS(image); +#endif + + return wrap(user_impl); +} + +void BKE_image_partial_update_free(PartialUpdateUser *user) +{ + PartialUpdateUserImpl *user_impl = unwrap(user); + MEM_delete<PartialUpdateUserImpl>(user_impl); +} + +/* --- Image side --- */ + +void BKE_image_partial_update_register_free(Image *image) +{ + PartialUpdateRegisterImpl *partial_update_register = unwrap( + image->runtime.partial_update_register); + if (partial_update_register) { + MEM_delete<PartialUpdateRegisterImpl>(partial_update_register); + } + image->runtime.partial_update_register = nullptr; +} + +void BKE_image_partial_update_mark_region(Image *image, + const ImageTile *image_tile, + const ImBuf *image_buffer, + const rcti *updated_region) +{ + PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image)); + partial_updater->update_resolution(image_tile, image_buffer); + partial_updater->mark_region(image_tile, updated_region); +} + +void BKE_image_partial_update_mark_full_update(Image *image) +{ + PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image)); + partial_updater->mark_full_update(); +} +} diff --git a/source/blender/blenkernel/intern/image_partial_update_test.cc b/source/blender/blenkernel/intern/image_partial_update_test.cc new file mode 100644 index 00000000000..bf12c27241e --- /dev/null +++ b/source/blender/blenkernel/intern/image_partial_update_test.cc @@ -0,0 +1,393 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 by Blender Foundation. + */ +#include "testing/testing.h" + +#include "CLG_log.h" + +#include "BKE_appdir.h" +#include "BKE_idtype.h" +#include "BKE_image.h" +#include "BKE_image_partial_update.hh" +#include "BKE_main.h" + +#include "IMB_imbuf.h" +#include "IMB_moviecache.h" + +#include "DNA_image_types.h" + +#include "MEM_guardedalloc.h" + +namespace blender::bke::image::partial_update { + +constexpr float black_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + +class ImagePartialUpdateTest : public testing::Test { + protected: + Main *bmain; + Image *image; + ImageTile *image_tile; + ImageUser image_user = {nullptr}; + ImBuf *image_buffer; + PartialUpdateUser *partial_update_user; + + private: + Image *create_test_image(int width, int height) + { + return BKE_image_add_generated(bmain, + width, + height, + "Test Image", + 32, + true, + IMA_GENTYPE_BLANK, + black_color, + false, + false, + false); + } + + protected: + void SetUp() override + { + CLG_init(); + BKE_idtype_init(); + BKE_appdir_init(); + IMB_init(); + + bmain = BKE_main_new(); + /* Creating an image generates a memory-leak during tests. */ + image = create_test_image(1024, 1024); + image_tile = BKE_image_get_tile(image, 0); + image_buffer = BKE_image_acquire_ibuf(image, nullptr, nullptr); + + partial_update_user = BKE_image_partial_update_create(image); + } + + void TearDown() override + { + BKE_image_release_ibuf(image, image_buffer, nullptr); + BKE_image_partial_update_free(partial_update_user); + BKE_main_free(bmain); + + IMB_moviecache_destruct(); + IMB_exit(); + BKE_appdir_exit(); + CLG_exit(); + } +}; + +TEST_F(ImagePartialUpdateTest, mark_full_update) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark full update */ + BKE_image_partial_update_mark_full_update(image); + + /* Validate need full update followed by no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, mark_single_tile) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 10, 20, 40, 50); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + ePartialUpdateIterResult iter_result; + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, mark_unconnected_tiles) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region_a; + BLI_rcti_init(®ion_a, 10, 20, 40, 50); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion_a); + rcti region_b; + BLI_rcti_init(®ion_b, 710, 720, 740, 750); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion_b); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + ePartialUpdateIterResult iter_result; + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion_b), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion_a), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, donot_mark_outside_image) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + /* Axis. */ + BLI_rcti_init(®ion, -100, 0, 50, 100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 1024, 1100, 50, 100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 50, 100, -100, 0); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 50, 100, 1024, 1100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Diagonals. */ + BLI_rcti_init(®ion, -100, 0, -100, 0); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, -100, 0, 1024, 1100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 1024, 1100, -100, 0); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 1024, 1100, 1024, 1100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, mark_inside_image) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 0, 1, 0, 1); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + BLI_rcti_init(®ion, 1023, 1024, 0, 1); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + BLI_rcti_init(®ion, 1023, 1024, 1023, 1024); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + BLI_rcti_init(®ion, 1023, 1024, 0, 1); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, sequential_mark_region) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + { + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 10, 20, 40, 50); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + ePartialUpdateIterResult iter_result; + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + } + + { + /* Mark different region. */ + rcti region; + BLI_rcti_init(®ion, 710, 720, 740, 750); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + ePartialUpdateIterResult iter_result; + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + } +} + +TEST_F(ImagePartialUpdateTest, mark_multiple_chunks) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 300, 700, 300, 700); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + int num_chunks_found = 0; + while (BKE_image_partial_update_get_next_change(partial_update_user, &changed_region) == + ePartialUpdateIterResult::ChangeAvailable) { + BLI_rcti_isect(&changed_region.region, ®ion, nullptr); + num_chunks_found++; + } + EXPECT_EQ(num_chunks_found, 4); +} + +TEST_F(ImagePartialUpdateTest, iterator) +{ + PartialUpdateChecker<NoTileData> checker(image, &image_user, partial_update_user); + /* First tile should always return a full update. */ + PartialUpdateChecker<NoTileData>::CollectResult changes = checker.collect_changes(); + EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + changes = checker.collect_changes(); + EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 300, 700, 300, 700); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + changes = checker.collect_changes(); + EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + int num_tiles_found = 0; + while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) { + BLI_rcti_isect(&changes.changed_region.region, ®ion, nullptr); + num_tiles_found++; + } + EXPECT_EQ(num_tiles_found, 4); +} + +} // namespace blender::bke::image::partial_update diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 9e3cea40fb8..a59dd6f2e0e 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1196,6 +1196,23 @@ static bool view_layer_objects_base_cache_validate(ViewLayer *UNUSED(view_layer) } #endif +void BKE_layer_collection_doversion_2_80(const Scene *scene, ViewLayer *view_layer) +{ + LayerCollection *first_layer_collection = view_layer->layer_collections.first; + if (BLI_listbase_count_at_most(&view_layer->layer_collections, 2) > 1 || + first_layer_collection->collection != scene->master_collection) { + /* In some cases (from older files) we do have a master collection, but no matching layer, + * instead all the children of the master collection have their layer collections in the + * viewlayer's list. This is not a valid situation, add a layer for the master collection and + * add all existing first-level layers as children of that new master layer. */ + ListBase layer_collections = view_layer->layer_collections; + BLI_listbase_clear(&view_layer->layer_collections); + LayerCollection *master_layer_collection = layer_collection_add(&view_layer->layer_collections, + scene->master_collection); + master_layer_collection->layer_collections = layer_collections; + } +} + void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) { if (no_resync) { @@ -1208,21 +1225,24 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) } if (BLI_listbase_is_empty(&view_layer->layer_collections)) { - /* In some cases (from older files) we do have a master collection, yet no matching layer. - * Create the master one here, so that the rest of the code can work as expected. */ + /* In some cases (from older files, or when creating a new ViewLayer from + * #BKE_view_layer_add), we do have a master collection, yet no matching layer. Create the + * master one here, so that the rest of the code can work as expected. */ layer_collection_add(&view_layer->layer_collections, scene->master_collection); } - else if (BLI_listbase_count_at_most(&view_layer->layer_collections, 2) > 1) { - /* In some cases (from older files) we do have a master collection, but no matching layer, - * instead all the children of the master collection have their layer collections in the - * viewlayer's list. This is not a valid situation, add a layer for the master collection and - * add all existing first-level layers as children of that new master layer. */ - ListBase layer_collections = view_layer->layer_collections; - BLI_listbase_clear(&view_layer->layer_collections); - LayerCollection *master_layer_collection = layer_collection_add(&view_layer->layer_collections, - scene->master_collection); - master_layer_collection->layer_collections = layer_collections; + +#ifndef NDEBUG + { + BLI_assert_msg(BLI_listbase_count_at_most(&view_layer->layer_collections, 2) == 1, + "ViewLayer's first level of children layer collections should always have " + "exactly one item"); + + LayerCollection *first_layer_collection = view_layer->layer_collections.first; + BLI_assert_msg(first_layer_collection->collection == scene->master_collection, + "ViewLayer's first layer collection should always be the one for the scene's " + "master collection"); } +#endif /* Free cache. */ MEM_SAFE_FREE(view_layer->object_bases_array); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 692e27731c5..6c19c7a328b 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -454,38 +454,57 @@ static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id, } } -void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) +void BKE_lib_id_make_local_generic_action_define( + struct Main *bmain, struct ID *id, int flags, bool *r_force_local, bool *r_force_copy) { - if (!ID_IS_LINKED(id)) { - return; - } - - const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; BLI_assert(force_copy == false || force_copy != force_local); + if (force_local || force_copy) { + /* Already set by caller code, nothing to do here. */ + *r_force_local = force_local; + *r_force_copy = force_copy; + return; + } + + const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; bool is_local = false, is_lib = false; - /* - only lib users: do nothing (unless force_local is set) - * - only local users: set flag + /* - no user (neither lib nor local): make local (happens e.g. with UI-used only data). + * - only lib users: do nothing (unless force_local is set) + * - only local users: make local * - mixed: make copy * In case we make a whole lib's content local, * we always want to localize, and we skip remapping (done later). */ - if (!force_copy && !force_local) { - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - if (lib_local || is_local) { - if (!is_lib) { - force_local = true; - } - else { - force_copy = true; - } + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + if (!lib_local && !is_local && !is_lib) { + force_local = true; + } + else if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; } } + *r_force_local = force_local; + *r_force_copy = force_copy; +} + +void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) +{ + if (!ID_IS_LINKED(id)) { + return; + } + + bool force_local, force_copy; + BKE_lib_id_make_local_generic_action_define(bmain, id, flags, &force_local, &force_copy); + if (force_local) { BKE_lib_id_clear_library_data(bmain, id, flags); BKE_lib_id_expand_local(bmain, id, flags); @@ -516,6 +535,7 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) } } + const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; if (!lib_local) { BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE); } @@ -1898,7 +1918,6 @@ void BKE_library_make_local(Main *bmain, * but complicates slightly the pre-processing of relations between IDs at step 2... */ else if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && ELEM(lib, NULL, id->lib) && - !(GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) && ((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING))) { BLI_linklist_prepend_arena(&todo_ids, id, linklist_mem); id->tag |= LIB_TAG_DOIT; @@ -1962,12 +1981,8 @@ void BKE_library_make_local(Main *bmain, } } else { - /* In this specific case, we do want to make ID local even if it has no local usage yet... - * Note that for objects, we don't want proxy pointers to be cleared yet. This will happen - * down the road in this function. - */ - BKE_lib_id_make_local( - bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + /* In this specific case, we do want to make ID local even if it has no local usage yet... */ + BKE_lib_id_make_local(bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY); if (id->newid) { if (GS(id->newid->name) == ID_OB) { @@ -2029,62 +2044,6 @@ void BKE_library_make_local(Main *bmain, TIMEIT_VALUE_PRINT(make_local); #endif - /* Step 5: proxy 'remapping' hack. */ - for (LinkNode *it = copied_ids; it; it = it->next) { - ID *id = it->link; - - /* Attempt to re-link copied proxy objects. This allows appending of an entire scene - * from another blend file into this one, even when that blend file contains proxified - * armatures that have local references. Since the proxified object needs to be linked - * (not local), this will only work when the "Localize all" checkbox is disabled. - * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ - if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { - Object *ob = (Object *)id; - Object *ob_new = (Object *)id->newid; - bool is_local = false, is_lib = false; - - /* Proxies only work when the proxified object is linked-in from a library. */ - if (!ID_IS_LINKED(ob->proxy)) { - CLOG_WARN(&LOG, - "proxy object %s will lose its link to %s, because the " - "proxified object is local.", - id->newid->name, - ob->proxy->id.name); - continue; - } - - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - - /* We can only switch the proxy'ing to a made-local proxy if it is no longer - * referred to from a library. Not checking for local use; if new local proxy - * was not used locally would be a nasty bug! */ - if (is_local || is_lib) { - CLOG_WARN(&LOG, - "made-local proxy object %s will lose its link to %s, " - "because the linked-in proxy is referenced (is_local=%i, is_lib=%i).", - id->newid->name, - ob->proxy->id.name, - is_local, - is_lib); - } - else { - /* we can switch the proxy'ing from the linked-in to the made-local proxy. - * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that - * was already allocated by object_make_local() (which called BKE_object_copy). */ - ob_new->proxy = ob->proxy; - ob_new->proxy_group = ob->proxy_group; - ob_new->proxy_from = ob->proxy_from; - ob_new->proxy->proxy_from = ob_new; - ob->proxy = ob->proxy_from = ob->proxy_group = NULL; - } - } - } - -#ifdef DEBUG_TIME - printf("Step 5: Proxy 'remapping' hack: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - /* This is probably more of a hack than something we should do here, but... * Issue is, the whole copying + remapping done in complex cases above may leave pose-channels * of armatures in complete invalid state (more precisely, the bone pointers of the diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 6d2e89187f7..5db70d85e71 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -154,7 +154,10 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i } if (remap_editor_id_reference_cb) { - remap_editor_id_reference_cb(id, NULL); + struct IDRemapper *remapper = BKE_id_remapper_create(); + BKE_id_remapper_add(remapper, id, NULL); + remap_editor_id_reference_cb(remapper); + BKE_id_remapper_free(remapper); } } @@ -262,8 +265,8 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) } for (id = last_remapped_id->next; id; id = id->next) { /* Will tag 'never NULL' users of this ID too. - * Note that we cannot use BKE_libblock_unlink() here, - * since it would ignore indirect (and proxy!) + * + * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect * links, this can lead to nasty crashing here in second, actual deleting loop. * Also, this will also flag users of deleted data that cannot be unlinked * (object using deleted obdata, etc.), so that they also get deleted. */ @@ -292,32 +295,40 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) * Note that we go forward here, since we want to check dependencies before users * (e.g. meshes before objects). * Avoids to have to loop twice. */ + struct IDRemapper *remapper = BKE_id_remapper_create(); for (i = 0; i < base_count; i++) { ListBase *lb = lbarray[i]; ID *id, *id_next; + BKE_id_remapper_clear(remapper); for (id = lb->first; id; id = id_next) { id_next = id->next; /* NOTE: in case we delete a library, we also delete all its datablocks! */ if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { id->tag |= tag; - - /* Will tag 'never NULL' users of this ID too. - * Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect - * (and proxy!) links, this can lead to nasty crashing here in second, - * actual deleting loop. - * Also, this will also flag users of deleted data that cannot be unlinked - * (object using deleted obdata, etc.), so that they also get deleted. */ - BKE_libblock_remap_locked(bmain, - id, - NULL, - (ID_REMAP_FLAG_NEVER_NULL_USAGE | - ID_REMAP_FORCE_NEVER_NULL_USAGE | - ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS)); + BKE_id_remapper_add(remapper, id, NULL); } } + + if (BKE_id_remapper_is_empty(remapper)) { + continue; + } + + /* Will tag 'never NULL' users of this ID too. + * + * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect + * links, this can lead to nasty crashing here in second, actual deleting loop. + * Also, this will also flag users of deleted data that cannot be unlinked + * (object using deleted obdata, etc.), so that they also get deleted. */ + BKE_libblock_remap_multiple_locked(bmain, + remapper, + (ID_REMAP_FLAG_NEVER_NULL_USAGE | + ID_REMAP_FORCE_NEVER_NULL_USAGE | + ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS)); } + BKE_id_remapper_free(remapper); } + BKE_main_unlock(bmain); /* In usual reversed order, such that all usage of a given ID, even 'never NULL' ones, diff --git a/source/blender/blenkernel/intern/lib_id_remapper.cc b/source/blender/blenkernel/intern/lib_id_remapper.cc new file mode 100644 index 00000000000..c1734c9826a --- /dev/null +++ b/source/blender/blenkernel/intern/lib_id_remapper.cc @@ -0,0 +1,175 @@ +/* + * 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) 2022 by Blender Foundation. + */ + +#include "DNA_ID.h" + +#include "BKE_idtype.h" +#include "BKE_lib_id.h" +#include "BKE_lib_remap.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_map.hh" + +using IDTypeFilter = uint64_t; + +namespace blender::bke::id::remapper { +struct IDRemapper { + private: + Map<ID *, ID *> mappings; + IDTypeFilter source_types = 0; + + public: + void clear() + { + mappings.clear(); + source_types = 0; + } + + bool is_empty() const + { + return mappings.is_empty(); + } + + void add(ID *old_id, ID *new_id) + { + BLI_assert(old_id != nullptr); + BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name))); + mappings.add(old_id, new_id); + source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name)); + } + + bool contains_mappings_for_any(IDTypeFilter filter) const + { + return (source_types & filter) != 0; + } + + IDRemapperApplyResult apply(ID **r_id_ptr, IDRemapperApplyOptions options) const + { + BLI_assert(r_id_ptr != nullptr); + if (*r_id_ptr == nullptr) { + return ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE; + } + + if (!mappings.contains(*r_id_ptr)) { + return ID_REMAP_RESULT_SOURCE_UNAVAILABLE; + } + + if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) { + id_us_min(*r_id_ptr); + } + + *r_id_ptr = mappings.lookup(*r_id_ptr); + if (*r_id_ptr == nullptr) { + return ID_REMAP_RESULT_SOURCE_UNASSIGNED; + } + + if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) { + id_us_plus(*r_id_ptr); + } + + if (options & ID_REMAP_APPLY_ENSURE_REAL) { + id_us_ensure_real(*r_id_ptr); + } + return ID_REMAP_RESULT_SOURCE_REMAPPED; + } + + void iter(IDRemapperIterFunction func, void *user_data) const + { + for (auto item : mappings.items()) { + func(item.key, item.value, user_data); + } + } +}; + +} // namespace blender::bke::id::remapper + +/** \brief wrap CPP IDRemapper to a C handle. */ +static IDRemapper *wrap(blender::bke::id::remapper::IDRemapper *remapper) +{ + return static_cast<IDRemapper *>(static_cast<void *>(remapper)); +} + +/** \brief wrap C handle to a CPP IDRemapper. */ +static blender::bke::id::remapper::IDRemapper *unwrap(IDRemapper *remapper) +{ + return static_cast<blender::bke::id::remapper::IDRemapper *>(static_cast<void *>(remapper)); +} + +/** \brief wrap C handle to a CPP IDRemapper. */ +static const blender::bke::id::remapper::IDRemapper *unwrap(const IDRemapper *remapper) +{ + return static_cast<const blender::bke::id::remapper::IDRemapper *>( + static_cast<const void *>(remapper)); +} + +extern "C" { + +IDRemapper *BKE_id_remapper_create(void) +{ + blender::bke::id::remapper::IDRemapper *remapper = + MEM_new<blender::bke::id::remapper::IDRemapper>(__func__); + return wrap(remapper); +} + +void BKE_id_remapper_free(IDRemapper *id_remapper) +{ + blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + MEM_delete<blender::bke::id::remapper::IDRemapper>(remapper); +} + +void BKE_id_remapper_clear(struct IDRemapper *id_remapper) +{ + blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + remapper->clear(); +} + +bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper) +{ + const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + return remapper->is_empty(); +} + +void BKE_id_remapper_add(IDRemapper *id_remapper, ID *old_id, ID *new_id) +{ + blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + remapper->add(old_id, new_id); +} + +bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter) +{ + const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + return remapper->contains_mappings_for_any(type_filter); +} + +IDRemapperApplyResult BKE_id_remapper_apply(const IDRemapper *id_remapper, + ID **r_id_ptr, + const IDRemapperApplyOptions options) +{ + const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + return remapper->apply(r_id_ptr, options); +} + +void BKE_id_remapper_iter(const struct IDRemapper *id_remapper, + IDRemapperIterFunction func, + void *user_data) +{ + const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + remapper->iter(func, user_data); +} +} diff --git a/source/blender/blenkernel/intern/lib_id_remapper_test.cc b/source/blender/blenkernel/intern/lib_id_remapper_test.cc new file mode 100644 index 00000000000..594f64dac73 --- /dev/null +++ b/source/blender/blenkernel/intern/lib_id_remapper_test.cc @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2022 by Blender Foundation. + */ + +#include "testing/testing.h" + +#include "BKE_lib_remap.h" + +#include "BLI_string.h" + +#include "DNA_ID.h" + +namespace blender::bke::id::remapper::tests { + +TEST(lib_id_remapper, unavailable) +{ + ID id1; + ID *idp = &id1; + + IDRemapper *remapper = BKE_id_remapper_create(); + IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT); + EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNAVAILABLE); + + BKE_id_remapper_free(remapper); +} + +TEST(lib_id_remapper, not_mappable) +{ + ID *idp = nullptr; + + IDRemapper *remapper = BKE_id_remapper_create(); + IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT); + EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE); + + BKE_id_remapper_free(remapper); +} + +TEST(lib_id_remapper, mapped) +{ + ID id1; + ID id2; + ID *idp = &id1; + BLI_strncpy(id1.name, "OB1", sizeof(id1.name)); + BLI_strncpy(id2.name, "OB2", sizeof(id2.name)); + + IDRemapper *remapper = BKE_id_remapper_create(); + BKE_id_remapper_add(remapper, &id1, &id2); + IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT); + EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_REMAPPED); + EXPECT_EQ(idp, &id2); + + BKE_id_remapper_free(remapper); +} + +TEST(lib_id_remapper, unassigned) +{ + ID id1; + ID *idp = &id1; + + IDRemapper *remapper = BKE_id_remapper_create(); + BKE_id_remapper_add(remapper, &id1, nullptr); + IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT); + EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNASSIGNED); + EXPECT_EQ(idp, nullptr); + + BKE_id_remapper_free(remapper); +} + +} // namespace blender::bke::id::remapper::tests diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 38ce8ea88b9..314351e4ad7 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -211,6 +211,7 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo } static ID *lib_override_library_create_from(Main *bmain, + Library *owner_library, ID *reference_id, const int lib_id_copy_flags) { @@ -227,6 +228,12 @@ static ID *lib_override_library_create_from(Main *bmain, } id_us_min(local_id); + /* TODO: Handle this properly in LIB_NO_MAIN case as well (i.e. resync case). Or offload to + * generic ID copy code? */ + if ((lib_id_copy_flags & LIB_ID_CREATE_NO_MAIN) == 0) { + local_id->lib = owner_library; + } + BKE_lib_override_library_init(local_id, reference_id); /* NOTE: From liboverride perspective (and RNA one), shape keys are considered as local embedded @@ -281,7 +288,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, BLI_assert(reference_id != NULL); BLI_assert(ID_IS_LINKED(reference_id)); - ID *local_id = lib_override_library_create_from(bmain, reference_id, 0); + ID *local_id = lib_override_library_create_from(bmain, NULL, reference_id, 0); /* We cannot allow automatic hierarchy resync on this ID, it is highly likely to generate a giant * mess in case there are a lot of hidden, non-instantiated, non-properly organized dependencies. * Ref T94650. */ @@ -320,6 +327,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, } bool BKE_lib_override_library_create_from_tag(Main *bmain, + Library *owner_library, const Library *reference_library, const bool do_no_main) { @@ -351,7 +359,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, * This requires extra care further down the resync process, * see: #BKE_lib_override_library_resync. */ reference_id->newid = lib_override_library_create_from( - bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0); + bmain, owner_library, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0); if (reference_id->newid == NULL) { success = false; break; @@ -616,6 +624,35 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat } } +static bool lib_override_linked_group_tag_collections_keep_tagged_check_recursive( + LibOverrideGroupTagData *data, Collection *collection) +{ + /* NOTE: Collection's object cache (using bases, as returned by #BKE_collection_object_cache_get) + * is not usable here, as it may have become invalid from some previous operation and it should + * not be updated here. So instead only use collections' reliable 'raw' data to check if some + * object in the hierarchy of the given collection is still tagged for override. */ + for (CollectionObject *collection_object = collection->gobject.first; collection_object != NULL; + collection_object = collection_object->next) { + Object *object = collection_object->ob; + if (object == NULL) { + continue; + } + if ((object->id.tag & data->tag) != 0) { + return true; + } + } + + for (CollectionChild *collection_child = collection->children.first; collection_child != NULL; + collection_child = collection_child->next) { + if (lib_override_linked_group_tag_collections_keep_tagged_check_recursive( + data, collection_child->collection)) { + return true; + } + } + + return false; +} + static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; @@ -638,15 +675,8 @@ static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGr if ((collection->id.tag & data->tag) == 0) { continue; } - bool keep_tagged = false; - const ListBase object_bases = BKE_collection_object_cache_get(collection); - LISTBASE_FOREACH (Base *, base, &object_bases) { - if ((base->object->id.tag & data->tag) != 0) { - keep_tagged = true; - break; - } - } - if (!keep_tagged) { + + if (!lib_override_linked_group_tag_collections_keep_tagged_check_recursive(data, collection)) { collection->id.tag &= ~data->tag; } } @@ -813,7 +843,10 @@ 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, Scene *scene, ID *id_root) +static bool lib_override_library_create_do(Main *bmain, + Scene *scene, + Library *owner_library, + ID *id_root) { BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, @@ -832,12 +865,13 @@ static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_roo BKE_main_relations_free(bmain); lib_override_group_tag_data_clear(&data); - return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false); + return BKE_lib_override_library_create_from_tag(bmain, owner_library, id_root->lib, false); } static void lib_override_library_create_post_process(Main *bmain, Scene *scene, ViewLayer *view_layer, + const Library *owner_library, ID *id_root, ID *id_reference, Collection *residual_storage, @@ -859,7 +893,8 @@ static void lib_override_library_create_post_process(Main *bmain, /* Instantiating the root collection or object should never be needed in resync case, since the * old override would be remapped to the new one. */ - if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) { + if (!is_resync && id_root != NULL && id_root->newid != NULL && + (!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) { switch (GS(id_root->name)) { case ID_GR: { Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? @@ -904,7 +939,7 @@ static void lib_override_library_create_post_process(Main *bmain, Collection *default_instantiating_collection = residual_storage; LISTBASE_FOREACH (Object *, ob, &bmain->objects) { Object *ob_new = (Object *)ob->id.newid; - if (ob_new == NULL || ID_IS_LINKED(ob_new)) { + if (ob_new == NULL || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) { continue; } @@ -967,6 +1002,7 @@ static void lib_override_library_create_post_process(Main *bmain, bool BKE_lib_override_library_create(Main *bmain, Scene *scene, ViewLayer *view_layer, + Library *owner_library, ID *id_root, ID *id_reference, ID **r_id_root_override) @@ -975,7 +1011,7 @@ bool BKE_lib_override_library_create(Main *bmain, *r_id_root_override = NULL; } - const bool success = lib_override_library_create_do(bmain, scene, id_root); + const bool success = lib_override_library_create_do(bmain, scene, owner_library, id_root); if (!success) { return success; @@ -986,7 +1022,7 @@ bool BKE_lib_override_library_create(Main *bmain, } lib_override_library_create_post_process( - bmain, scene, view_layer, id_root, id_reference, NULL, false); + bmain, scene, view_layer, owner_library, id_root, id_reference, NULL, false); /* Cleanup. */ BKE_main_id_newptr_and_tag_clear(bmain); @@ -1011,114 +1047,43 @@ bool BKE_lib_override_library_template_create(struct ID *id) return true; } -bool BKE_lib_override_library_proxy_convert(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - Object *ob_proxy) +static void lib_override_library_remap(Main *bmain, + const ID *id_root_reference, + GHash *linkedref_to_old_override) { - /* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is - * coming. */ - Object *ob_proxy_group = ob_proxy->proxy_group; - const bool is_override_instancing_object = ob_proxy_group != NULL; - ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id : - &ob_proxy->proxy->id; - ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; - - /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not - * sure this is a valid state, but for now just abort the overriding process. */ - if (!ID_IS_OVERRIDABLE_LIBRARY(id_root)) { - return false; - } - - /* We manually convert the proxy object into a library override, further override handling will - * then be handled by `BKE_lib_override_library_create()` just as for a regular override - * creation. - */ - ob_proxy->proxy->id.tag |= LIB_TAG_DOIT; - ob_proxy->proxy->id.newid = &ob_proxy->id; - BKE_lib_override_library_init(&ob_proxy->id, &ob_proxy->proxy->id); - - ob_proxy->proxy->proxy_from = NULL; - ob_proxy->proxy = ob_proxy->proxy_group = NULL; - - DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE); - - /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created - * overrides. - * While this might not be 100% the desired behavior, it is likely to be the case most of the - * time. Ref: T91711. */ - ID *id_iter; - FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (!ID_IS_LINKED(id_iter)) { - id_iter->tag |= LIB_TAG_DOIT; - } - } - FOREACH_MAIN_ID_END; - - return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL); -} - -static void lib_override_library_proxy_convert_do(Main *bmain, - Scene *scene, - Object *ob_proxy, - BlendFileReadReport *reports) -{ - Object *ob_proxy_group = ob_proxy->proxy_group; - const bool is_override_instancing_object = ob_proxy_group != NULL; - - const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy); - - if (success) { - CLOG_INFO(&LOG, - 4, - "Proxy object '%s' successfully converted to library overrides", - ob_proxy->id.name); - /* Remove the instance empty from this scene, the items now have an overridden collection - * instead. */ - if (is_override_instancing_object) { - BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true); - } - reports->count.proxies_to_lib_overrides_success++; - } -} + ID *id; + struct IDRemapper *remapper = BKE_id_remapper_create(); + FOREACH_MAIN_ID_BEGIN (bmain, id) { -void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports) -{ - LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { - FOREACH_SCENE_OBJECT_BEGIN (scene, object) { - if (object->proxy_group == NULL) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { + ID *id_override_new = id->newid; + ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + if (id_override_old == NULL) { continue; } - lib_override_library_proxy_convert_do(bmain, scene, object, reports); - } - FOREACH_SCENE_OBJECT_END; + BKE_id_remapper_add(remapper, id_override_old, id_override_new); + /* Remap no-main override IDs we just created too. */ + GHashIterator linkedref_to_old_override_iter; + GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { + ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); + if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) { + continue; + } - FOREACH_SCENE_OBJECT_BEGIN (scene, object) { - if (object->proxy == NULL) { - continue; + BKE_libblock_relink_ex(bmain, + id_override_old_iter, + id_override_old, + id_override_new, + ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); } - - lib_override_library_proxy_convert_do(bmain, scene, object, reports); } - FOREACH_SCENE_OBJECT_END; } + FOREACH_MAIN_ID_END; - LISTBASE_FOREACH (Object *, object, &bmain->objects) { - if (ID_IS_LINKED(object)) { - if (object->proxy != NULL) { - CLOG_WARN(&LOG, "Did not try to convert linked proxy object '%s'", object->id.name); - reports->count.linked_proxies++; - } - continue; - } - - if (object->proxy_group != NULL || object->proxy != NULL) { - CLOG_WARN( - &LOG, "Proxy object '%s' failed to be converted to library override", object->id.name); - reports->count.proxies_to_lib_overrides_failures++; - } - } + /* Remap all IDs to use the new override. */ + BKE_libblock_remap_multiple(bmain, remapper, 0); + BKE_id_remapper_free(remapper); } static bool lib_override_library_resync(Main *bmain, @@ -1249,7 +1214,7 @@ static bool lib_override_library_resync(Main *bmain, * override IDs (including within the old overrides themselves, since those are tagged too * above). */ const bool success = BKE_lib_override_library_create_from_tag( - bmain, id_root_reference->lib, true); + bmain, NULL, id_root_reference->lib, true); if (!success) { return success; @@ -1312,32 +1277,9 @@ static bool lib_override_library_resync(Main *bmain, } FOREACH_MAIN_LISTBASE_END; - /* We need to remap old to new override usages in a separate loop, after all new overrides have + /* We remap old to new override usages in a separate loop, after all new overrides have * been added to Main. */ - FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { - ID *id_override_new = id->newid; - ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); - - if (id_override_old != NULL) { - /* Remap all IDs to use the new override. */ - BKE_libblock_remap(bmain, id_override_old, id_override_new, 0); - /* Remap no-main override IDs we just created too. */ - GHashIterator linkedref_to_old_override_iter; - GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { - ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); - if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) { - BKE_libblock_relink_ex(bmain, - id_override_old_iter, - id_override_old, - id_override_new, - ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); - } - } - } - } - } - FOREACH_MAIN_ID_END; + lib_override_library_remap(bmain, id_root_reference, linkedref_to_old_override); BKE_main_collection_sync(bmain); @@ -1486,6 +1428,7 @@ static bool lib_override_library_resync(Main *bmain, lib_override_library_create_post_process(bmain, scene, view_layer, + NULL, id_root_reference, id_root, override_resync_residual_storage, @@ -1537,11 +1480,13 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain, CLOG_ERROR( &LOG, "While processing indirect level %d, ID %s from lib %s of indirect level %d detected " - "as needing resync.", + "as needing resync, skipping.", library_indirect_level, id->name, id->lib->filepath, id->lib->temp_index); + id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + return; } MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); @@ -1908,7 +1853,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ lib_override_library_create_post_process( - bmain, scene, view_layer, NULL, NULL, override_resync_residual_storage, true); + bmain, scene, view_layer, NULL, NULL, NULL, override_resync_residual_storage, true); if (BKE_collection_is_empty(override_resync_residual_storage)) { BKE_collection_delete(bmain, override_resync_residual_storage, true); @@ -2961,10 +2906,6 @@ void BKE_lib_override_library_main_update(Main *bmain) bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id) { - if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) { - return true; - } - /* The only strong known case currently are objects used by override collections. */ /* TODO: There are most likely other cases... This may need to be addressed in a better way at * some point. */ diff --git a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c new file mode 100644 index 00000000000..d99a9b57a6e --- /dev/null +++ b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c @@ -0,0 +1,176 @@ +/* + * 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) 2016 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_linklist.h" + +/* Required for proxy to liboverrides conversion code. */ +#define DNA_DEPRECATED_ALLOW + +#include "DNA_ID.h" +#include "DNA_collection_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "DEG_depsgraph.h" + +#include "BKE_collection.h" +#include "BKE_idtype.h" +#include "BKE_lib_id.h" +#include "BKE_lib_override.h" +#include "BKE_main.h" + +#include "BLO_readfile.h" + +static CLG_LogRef LOG = {"bke.liboverride_proxy_conversion"}; + +bool BKE_lib_override_library_proxy_convert(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Object *ob_proxy) +{ + /* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is + * coming. */ + Object *ob_proxy_group = ob_proxy->proxy_group; + const bool is_override_instancing_object = ob_proxy_group != NULL; + ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id : + &ob_proxy->proxy->id; + ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; + + /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not + * sure this is a valid state, but for now just abort the overriding process. */ + if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_root)) { + if (ob_proxy->proxy != NULL) { + ob_proxy->proxy->proxy_from = NULL; + } + id_us_min((ID *)ob_proxy->proxy); + ob_proxy->proxy = ob_proxy->proxy_group = NULL; + return false; + } + + /* We manually convert the proxy object into a library override, further override handling will + * then be handled by `BKE_lib_override_library_create()` just as for a regular override + * creation. + */ + ob_proxy->proxy->id.tag |= LIB_TAG_DOIT; + ob_proxy->proxy->id.newid = &ob_proxy->id; + BKE_lib_override_library_init(&ob_proxy->id, &ob_proxy->proxy->id); + + ob_proxy->proxy->proxy_from = NULL; + ob_proxy->proxy = ob_proxy->proxy_group = NULL; + + DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE); + + /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created + * overrides. Also do that for the IDs from the same lib as the proxy in case it is linked. + * While this might not be 100% the desired behavior, it is likely to be the case most of the + * time. Ref: T91711. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (!ID_IS_LINKED(id_iter) || id_iter->lib == ob_proxy->id.lib) { + id_iter->tag |= LIB_TAG_DOIT; + } + } + FOREACH_MAIN_ID_END; + + return BKE_lib_override_library_create( + bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_reference, NULL); +} + +static void lib_override_library_proxy_convert_do(Main *bmain, + Scene *scene, + Object *ob_proxy, + BlendFileReadReport *reports) +{ + Object *ob_proxy_group = ob_proxy->proxy_group; + const bool is_override_instancing_object = ob_proxy_group != NULL; + + const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy); + + if (success) { + CLOG_INFO(&LOG, + 4, + "Proxy object '%s' successfully converted to library overrides", + ob_proxy->id.name); + /* Remove the instance empty from this scene, the items now have an overridden collection + * instead. */ + if (is_override_instancing_object) { + BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true); + } + reports->count.proxies_to_lib_overrides_success++; + } +} + +void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports) +{ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + LinkNodePair proxy_objects = {NULL}; + + FOREACH_SCENE_OBJECT_BEGIN (scene, object) { + if (object->proxy_group != NULL) { + BLI_linklist_append(&proxy_objects, object); + } + } + FOREACH_SCENE_OBJECT_END; + + FOREACH_SCENE_OBJECT_BEGIN (scene, object) { + if (object->proxy != NULL && object->proxy_group == NULL) { + BLI_linklist_append(&proxy_objects, object); + } + } + FOREACH_SCENE_OBJECT_END; + + for (LinkNode *proxy_object_iter = proxy_objects.list; proxy_object_iter != NULL; + proxy_object_iter = proxy_object_iter->next) { + Object *proxy_object = proxy_object_iter->link; + lib_override_library_proxy_convert_do(bmain, scene, proxy_object, reports); + } + + BLI_linklist_free(proxy_objects.list, NULL); + } + + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (object->proxy_group != NULL || object->proxy != NULL) { + if (ID_IS_LINKED(object)) { + CLOG_WARN(&LOG, + "Linked proxy object '%s' from '%s' failed to be converted to library override", + object->id.name + 2, + object->id.lib->filepath); + } + else { + CLOG_WARN(&LOG, + "Proxy object '%s' failed to be converted to library override", + object->id.name + 2); + } + reports->count.proxies_to_lib_overrides_failures++; + if (object->proxy != NULL) { + object->proxy->proxy_from = NULL; + } + id_us_min((ID *)object->proxy); + object->proxy = object->proxy_group = NULL; + } + } +} diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 1f20a84098c..f69fba5b540 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -471,7 +471,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return ELEM(id_type_used, ID_MA); case ID_WS: return ELEM(id_type_used, ID_SCR, ID_SCE); - case ID_HA: + case ID_CV: return ELEM(id_type_used, ID_MA); case ID_PT: return ELEM(id_type_used, ID_MA); @@ -517,7 +517,7 @@ static int foreach_libblock_id_users_callback(LibraryIDLinkCallbackData *cb_data IDUsersIter *iter = cb_data->user_data; if (*id_p) { - /* 'Loopback' ID pointers (the ugly 'from' ones, Object->proxy_from and Key->from). + /* 'Loopback' ID pointers (the ugly 'from' ones, like Key->from). * Those are not actually ID usage, we can ignore them here. */ if (cb_flag & IDWALK_CB_LOOPBACK) { @@ -768,7 +768,7 @@ static int foreach_libblock_used_linked_data_tag_clear_cb(LibraryIDLinkCallbackD bool *is_changed = cb_data->user_data; if (*id_p) { - /* The infamous 'from' pointers (Key.from, Object.proxy_from, ...). + /* The infamous 'from' pointers (Key.from, ...). * those are not actually ID usage, so we ignore them here. */ if (cb_flag & IDWALK_CB_LOOPBACK) { return IDWALK_RET_NOP; diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index ec97ca83703..a9de5b4a189 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -91,26 +91,18 @@ enum { ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1, /* There was some skipped 'user_one' usages of old_id. */ }; -static void foreach_libblock_remap_callback_skip(const ID *id_owner, - ID **id_ptr, +static void foreach_libblock_remap_callback_skip(const ID *UNUSED(id_owner), + ID **UNUSED(id_ptr), IDRemap *id_remap_data, const int cb_flag, const bool is_indirect, const bool is_reference, const bool is_never_null, - const bool is_obj, + const bool UNUSED(is_obj), const bool is_obj_editmode) { if (is_indirect) { id_remap_data->skipped_indirect++; - if (is_obj) { - Object *ob = (Object *)id_owner; - if (ob->data == *id_ptr && ob->proxy != NULL) { - /* And another 'Proudly brought to you by Proxy Hell' hack! - * This will allow us to avoid clearing 'LIB_EXTERN' flag of obdata of proxies... */ - id_remap_data->skipped_direct++; - } - } } else if (is_never_null || is_obj_editmode || is_reference) { id_remap_data->skipped_direct++; @@ -136,8 +128,7 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner, const int cb_flag, const bool is_indirect, const bool is_never_null, - const bool force_user_refcount, - const bool is_obj_proxy) + const bool force_user_refcount) { if (!is_never_null) { *id_ptr = new_id; @@ -170,16 +161,9 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner, /* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET) * are assumed to be set as needed, that extra user is processed in final handling. */ } - if (!is_indirect || is_obj_proxy) { + if (!is_indirect) { id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; } - /* We need to remap proxy_from pointer of remapped proxy... sigh. */ - if (is_obj_proxy && new_id != NULL) { - Object *ob = (Object *)id_owner; - if (ob->proxy == (Object *)new_id) { - ob->proxy->proxy_from = ob; - } - } } static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) @@ -221,12 +205,9 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) const bool is_reference = (cb_flag & IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE) != 0; const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0; const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; - /* NOTE: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct, - * on the other hand since they get reset to lib data on file open/reload it is indirect too. - * Edit Mode is also a 'skip direct' case. */ const bool is_obj = (GS(id_owner->name) == ID_OB); - const bool is_obj_proxy = (is_obj && - (((Object *)id_owner)->proxy || ((Object *)id_owner)->proxy_group)); + /* NOTE: Edit Mode is a 'skip direct' case, unless specifically requested, obdata should not be + * remapped in this situation. */ const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner) && (id_remap_data->flag & ID_REMAP_FORCE_OBDATA_IN_EDITMODE) == 0); const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && @@ -281,8 +262,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) cb_flag, is_indirect, is_never_null, - force_user_refcount, - is_obj_proxy); + force_user_refcount); } return IDWALK_RET_NOP; @@ -430,10 +410,7 @@ static void libblock_remap_data( Main *bmain, ID *id, ID *old_id, ID *new_id, const short remap_flags, IDRemap *r_id_remap_data) { IDRemap id_remap_data; - const int foreach_id_flags = ((remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ? - IDWALK_NO_INDIRECT_PROXY_DATA_USAGE : - IDWALK_NOP) | - ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ? + const int foreach_id_flags = ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ? IDWALK_DO_INTERNAL_RUNTIME_POINTERS : IDWALK_NOP); @@ -510,11 +487,18 @@ static void libblock_remap_data( #endif } -void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +typedef struct LibblockRemapMultipleUserData { + Main *bmain; + short remap_flags; +} LibBlockRemapMultipleUserData; + +static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_data) { + LibBlockRemapMultipleUserData *data = user_data; + Main *bmain = data->bmain; + const short remap_flags = data->remap_flags; + IDRemap id_remap_data; - ID *old_id = old_idv; - ID *new_id = new_idv; int skipped_direct, skipped_refcounted; BLI_assert(old_id != NULL); @@ -527,13 +511,6 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const free_notifier_reference_cb(old_id); } - /* We assume editors do not hold references to their IDs... This is false in some cases - * (Image is especially tricky here), - * editors' code is to handle refcount (id->us) itself then. */ - if (remap_editor_id_reference_cb) { - remap_editor_id_reference_cb(old_id, new_id); - } - skipped_direct = id_remap_data.skipped_direct; skipped_refcounted = id_remap_data.skipped_refcounted; @@ -581,7 +558,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const case ID_ME: case ID_CU: case ID_MB: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: if (new_id) { /* Only affects us in case obdata was relinked (changed). */ @@ -606,6 +583,41 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const DEG_relations_tag_update(bmain); } +void BKE_libblock_remap_multiple_locked(Main *bmain, + const struct IDRemapper *mappings, + const short remap_flags) +{ + if (BKE_id_remapper_is_empty(mappings)) { + /* Early exit nothing to do. */ + return; + } + + LibBlockRemapMultipleUserData user_data; + user_data.bmain = bmain; + user_data.remap_flags = remap_flags; + BKE_id_remapper_iter(mappings, libblock_remap_foreach_idpair_cb, &user_data); + + /* We assume editors do not hold references to their IDs... This is false in some cases + * (Image is especially tricky here), + * editors' code is to handle refcount (id->us) itself then. */ + if (remap_editor_id_reference_cb) { + remap_editor_id_reference_cb(mappings); + } + + /* Full rebuild of DEG! */ + DEG_relations_tag_update(bmain); +} + +void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +{ + struct IDRemapper *remapper = BKE_id_remapper_create(); + ID *old_id = old_idv; + ID *new_id = new_idv; + BKE_id_remapper_add(remapper, old_id, new_id); + BKE_libblock_remap_multiple_locked(bmain, remapper, remap_flags); + BKE_id_remapper_free(remapper); +} + void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) { BKE_main_lock(bmain); @@ -615,6 +627,17 @@ void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short r BKE_main_unlock(bmain); } +void BKE_libblock_remap_multiple(Main *bmain, + const struct IDRemapper *mappings, + const short remap_flags) +{ + BKE_main_lock(bmain); + + BKE_libblock_remap_multiple_locked(bmain, mappings, remap_flags); + + BKE_main_unlock(bmain); +} + void BKE_libblock_unlink(Main *bmain, void *idv, const bool do_flag_never_null, diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 64731be57ac..53b1a9c9e16 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -643,8 +643,8 @@ ListBase *which_libbase(Main *bmain, short type) return &(bmain->cachefiles); case ID_WS: return &(bmain->workspaces); - case ID_HA: - return &(bmain->hairs); + case ID_CV: + return &(bmain->hair_curves); case ID_PT: return &(bmain->pointclouds); case ID_VO: @@ -688,7 +688,7 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/]) lb[INDEX_ID_ME] = &(bmain->meshes); lb[INDEX_ID_CU] = &(bmain->curves); lb[INDEX_ID_MB] = &(bmain->metaballs); - lb[INDEX_ID_HA] = &(bmain->hairs); + lb[INDEX_ID_CV] = &(bmain->hair_curves); lb[INDEX_ID_PT] = &(bmain->pointclouds); lb[INDEX_ID_VO] = &(bmain->volumes); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index d6035887790..1c1b2c2cd27 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -36,10 +36,10 @@ #include "DNA_anim_types.h" #include "DNA_collection_types.h" #include "DNA_curve_types.h" +#include "DNA_curves_types.h" #include "DNA_customdata_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_types.h" -#include "DNA_hair_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -341,9 +341,9 @@ Material ***BKE_object_material_array_p(Object *ob) bGPdata *gpd = ob->data; return &(gpd->mat); } - if (ob->type == OB_HAIR) { - Hair *hair = ob->data; - return &(hair->mat); + if (ob->type == OB_CURVES) { + Curves *curves = ob->data; + return &(curves->mat); } if (ob->type == OB_POINTCLOUD) { PointCloud *pointcloud = ob->data; @@ -374,9 +374,9 @@ short *BKE_object_material_len_p(Object *ob) bGPdata *gpd = ob->data; return &(gpd->totcol); } - if (ob->type == OB_HAIR) { - Hair *hair = ob->data; - return &(hair->totcol); + if (ob->type == OB_CURVES) { + Curves *curves = ob->data; + return &(curves->totcol); } if (ob->type == OB_POINTCLOUD) { PointCloud *pointcloud = ob->data; @@ -403,8 +403,8 @@ Material ***BKE_id_material_array_p(ID *id) return &(((MetaBall *)id)->mat); case ID_GD: return &(((bGPdata *)id)->mat); - case ID_HA: - return &(((Hair *)id)->mat); + case ID_CV: + return &(((Curves *)id)->mat); case ID_PT: return &(((PointCloud *)id)->mat); case ID_VO: @@ -429,8 +429,8 @@ short *BKE_id_material_len_p(ID *id) return &(((MetaBall *)id)->totcol); case ID_GD: return &(((bGPdata *)id)->totcol); - case ID_HA: - return &(((Hair *)id)->totcol); + case ID_CV: + return &(((Curves *)id)->totcol); case ID_PT: return &(((PointCloud *)id)->totcol); case ID_VO: @@ -454,7 +454,7 @@ static void material_data_index_remove_id(ID *id, short index) BKE_curve_material_index_remove((Curve *)id, index); break; case ID_MB: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: /* No material indices for these object data types. */ @@ -509,7 +509,7 @@ static void material_data_index_clear_id(ID *id) BKE_curve_material_index_clear((Curve *)id); break; case ID_MB: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: /* No material indices for these object data types. */ @@ -720,8 +720,9 @@ static ID *get_evaluated_object_data_with_materials(Object *ob) /* Meshes in edit mode need special handling. */ if (ob->type == OB_MESH && ob->mode == OB_MODE_EDIT) { Mesh *mesh = ob->data; - if (mesh->edit_mesh && mesh->edit_mesh->mesh_eval_final) { - data = &mesh->edit_mesh->mesh_eval_final->id; + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob); + if (mesh->edit_mesh && editmesh_eval_final) { + data = &editmesh_eval_final->id; } } return data; diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 9b0cf17a424..c1b1f62a881 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -173,19 +173,26 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int BKE_mesh_assert_normals_dirty_or_calculated(mesh_dst); } +void BKE_mesh_free_editmesh(struct Mesh *mesh) +{ + if (mesh->edit_mesh == nullptr) { + return; + } + + if (mesh->edit_mesh->is_shallow_copy == false) { + BKE_editmesh_free_data(mesh->edit_mesh); + } + MEM_freeN(mesh->edit_mesh); + mesh->edit_mesh = nullptr; +} + static void mesh_free_data(ID *id) { Mesh *mesh = (Mesh *)id; BLI_freelistN(&mesh->vertex_group_names); - if (mesh->edit_mesh) { - if (mesh->edit_mesh->is_shallow_copy == false) { - BKE_editmesh_free_data(mesh->edit_mesh); - } - MEM_freeN(mesh->edit_mesh); - mesh->edit_mesh = nullptr; - } + BKE_mesh_free_editmesh(mesh); BKE_mesh_runtime_free_data(mesh); mesh_clear_geometry(mesh); @@ -678,6 +685,17 @@ static int customdata_compare( } break; } + case CD_PROP_INT8: { + const int8_t *l1_data = (int8_t *)l1->data; + const int8_t *l2_data = (int8_t *)l2->data; + + for (int i = 0; i < total_length; i++) { + if (l1_data[i] != l2_data[i]) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } case CD_PROP_BOOL: { const bool *l1_data = (bool *)l1->data; const bool *l2_data = (bool *)l2->data; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index cbc772d93a6..7d5f156040d 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -1102,8 +1102,11 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph, Mesh *mesh_input = (Mesh *)object->data; /* If we are in edit mode, use evaluated mesh from edit structure, matching to what * viewport is using for visualization. */ - if (mesh_input->edit_mesh != nullptr && mesh_input->edit_mesh->mesh_eval_final) { - mesh_input = mesh_input->edit_mesh->mesh_eval_final; + if (mesh_input->edit_mesh != nullptr) { + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object); + if (editmesh_eval_final != nullptr) { + mesh_input = editmesh_eval_final; + } } return mesh_new_from_mesh(object, mesh_input); } diff --git a/source/blender/blenkernel/intern/mesh_iterators.c b/source/blender/blenkernel/intern/mesh_iterators.c index b77d0e38f0f..4fb03c7b329 100644 --- a/source/blender/blenkernel/intern/mesh_iterators.c +++ b/source/blender/blenkernel/intern/mesh_iterators.c @@ -34,13 +34,23 @@ #include "MEM_guardedalloc.h" +/* General note on iterating verts/loops/edges/polys and end mode. + * + * The edit mesh pointer is set for both final and cage meshes in both cases when there are + * modifiers applied and not. This helps consistency of checks in the draw manager, where the + * existence of the edit mesh pointer does not depend on object configuration. + * + * For the iterating, however, we need to follow the `CD_ORIGINDEX` code paths when there are + * modifiers applied on the cage. In the code terms it means that the check for the edit mode code + * path needs to consist of both edit mesh and edit data checks. */ + void BKE_mesh_foreach_mapped_vert( Mesh *mesh, void (*func)(void *userData, int index, const float co[3], const float no[3]), void *userData, MeshForeachFlag flag) { - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; @@ -100,7 +110,7 @@ void BKE_mesh_foreach_mapped_edge( void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]), void *userData) { - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; @@ -158,7 +168,7 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh, /* We can't use dm->getLoopDataLayout(dm) here, * we want to always access dm->loopData, EditDerivedBMesh would * return loop data from bmesh itself. */ - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; @@ -231,7 +241,7 @@ void BKE_mesh_foreach_mapped_face_center( void *userData, MeshForeachFlag flag) { - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; const float(*polyCos)[3]; diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c index 134a1344f83..3c01d5a4a50 100644 --- a/source/blender/blenkernel/intern/mesh_merge.c +++ b/source/blender/blenkernel/intern/mesh_merge.c @@ -27,6 +27,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BLI_bitmap.h" #include "BLI_edgehash.h" #include "BLI_ghash.h" #include "BLI_utildefines.h" @@ -351,6 +352,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, &poly_map, &poly_map_mem, mesh->mpoly, mesh->mloop, totvert, totpoly, totloop); } /* done preparing for fast poly compare */ + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__); + mp = mesh->mpoly; mv = mesh->mvert; for (i = 0; i < totpoly; i++, mp++) { @@ -365,11 +368,11 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, if (vtargetmap[ml->v] == -1) { all_vertices_merged = false; /* This will be used to check for poly using several time the same vert. */ - mv[ml->v].flag &= ~ME_VERT_TMP_TAG; + BLI_BITMAP_DISABLE(vert_tag, ml->v); } else { /* This will be used to check for poly using several time the same vert. */ - mv[vtargetmap[ml->v]].flag &= ~ME_VERT_TMP_TAG; + BLI_BITMAP_DISABLE(vert_tag, vtargetmap[ml->v]); } } @@ -457,8 +460,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, #endif /* A loop is only valid if its matching edge is, * and it's not reusing a vertex already used by this poly. */ - if (LIKELY((newe[ml->e] != -1) && ((mv[mlv].flag & ME_VERT_TMP_TAG) == 0))) { - mv[mlv].flag |= ME_VERT_TMP_TAG; + if (LIKELY((newe[ml->e] != -1) && !BLI_BITMAP_TEST(vert_tag, mlv))) { + BLI_BITMAP_ENABLE(vert_tag, mlv); if (UNLIKELY(last_valid_ml != NULL && need_edge_from_last_valid_ml)) { /* We need to create a new edge between last valid loop and this one! */ @@ -644,6 +647,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, MEM_freeN(oldl); MEM_freeN(oldp); + MEM_freeN(vert_tag); + BLI_edgehash_free(ehash, NULL); if (poly_map != NULL) { diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c index abc0b518d92..ec3655ac491 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.c +++ b/source/blender/blenkernel/intern/mesh_mirror.c @@ -420,7 +420,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, /* calculate custom normals into loop_normals, then mirror first half into second half */ BKE_mesh_normals_loop_split(result->mvert, - BKE_mesh_vertex_normals_ensure(mesh), + BKE_mesh_vertex_normals_ensure(result), result->totvert, result->medge, result->totedge, @@ -428,7 +428,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, loop_normals, totloop, result->mpoly, - BKE_mesh_poly_normals_ensure(mesh), + BKE_mesh_poly_normals_ensure(result), totpoly, true, mesh->smoothresh, diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index 005c916b4e0..a5ba2767301 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -28,6 +28,7 @@ #include "CLG_log.h" +#include "BLI_bitmap.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -560,6 +561,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, * so be sure to leave at most one poly per loop! */ { + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__); + SortPoly *sort_polys = MEM_callocN(sizeof(SortPoly) * totpoly, "mesh validate's sort_polys"); SortPoly *prev_sp, *sp = sort_polys; int prev_end; @@ -608,7 +611,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, * so we have to ensure here all verts of current poly are cleared. */ for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) { if (ml->v < totvert) { - mverts[ml->v].flag &= ~ME_VERT_TMP_TAG; + BLI_BITMAP_DISABLE(vert_tag, ml->v); } } @@ -619,12 +622,12 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, PRINT_ERR("\tLoop %u has invalid vert reference (%u)", sp->loopstart + j, ml->v); sp->invalid = true; } - else if (mverts[ml->v].flag & ME_VERT_TMP_TAG) { + else if (BLI_BITMAP_TEST(vert_tag, ml->v)) { PRINT_ERR("\tPoly %u has duplicated vert reference at corner (%u)", i, j); sp->invalid = true; } else { - mverts[ml->v].flag |= ME_VERT_TMP_TAG; + BLI_BITMAP_ENABLE(vert_tag, ml->v); } *v = ml->v; } @@ -698,6 +701,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, } } + MEM_freeN(vert_tag); + /* Second check pass, testing polys using the same verts. */ qsort(sort_polys, totpoly, sizeof(SortPoly), search_poly_cmp); sp = prev_sp = sort_polys; diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 3f9faa415cb..829ef08a8fb 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -290,6 +290,16 @@ ModifierData *BKE_modifiers_findby_name(const Object *ob, const char *name) return BLI_findstring(&(ob->modifiers), name, offsetof(ModifierData, name)); } +ModifierData *BKE_modifiers_findby_session_uuid(const Object *ob, const SessionUUID *session_uuid) +{ + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (BLI_session_uuid_is_equal(&md->session_uuid, session_uuid)) { + return md; + } + } + return NULL; +} + void BKE_modifiers_clear_errors(Object *ob) { LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { @@ -439,9 +449,7 @@ void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_for #ifndef NDEBUG if ((md->mode & eModifierMode_Virtual) == 0) { /* Ensure correct object is passed in. */ - const Object *ob_orig = (Object *)DEG_get_original_id((ID *)&ob->id); - const ModifierData *md_orig = md->orig_modifier_data ? md->orig_modifier_data : md; - BLI_assert(BLI_findindex(&ob_orig->modifiers, md_orig) != -1); + BLI_assert(BKE_modifier_get_original(ob, md) != NULL); } #endif @@ -1036,8 +1044,11 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval, BMEditMesh *em = BKE_editmesh_from_object(ob_eval); /* 'em' might not exist yet in some cases, just after loading a .blend file, see T57878. */ if (em != NULL) { - me = (get_cage_mesh && em->mesh_eval_cage != NULL) ? em->mesh_eval_cage : - em->mesh_eval_final; + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval); + Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval); + + me = (get_cage_mesh && editmesh_eval_cage != NULL) ? editmesh_eval_cage : + editmesh_eval_final; } } if (me == NULL) { @@ -1049,12 +1060,10 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval, return me; } -ModifierData *BKE_modifier_get_original(ModifierData *md) +ModifierData *BKE_modifier_get_original(const Object *object, ModifierData *md) { - if (md->orig_modifier_data == NULL) { - return md; - } - return md->orig_modifier_data; + const Object *object_orig = DEG_get_original_object((Object *)object); + return BKE_modifiers_findby_session_uuid(object_orig, &md->session_uuid); } struct ModifierData *BKE_modifier_get_evaluated(Depsgraph *depsgraph, @@ -1065,7 +1074,7 @@ struct ModifierData *BKE_modifier_get_evaluated(Depsgraph *depsgraph, if (object_eval == object) { return md; } - return BKE_modifiers_findby_name(object_eval, md->name); + return BKE_modifiers_findby_session_uuid(object_eval, &md->session_uuid); } void BKE_modifier_check_uuids_unique_and_report(const Object *object) @@ -1179,8 +1188,8 @@ void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) #if 0 CollisionModifierData *collmd = (CollisionModifierData *)md; - // TODO: CollisionModifier should use pointcache - // + have proper reset events before enabling this + /* TODO: CollisionModifier should use pointcache + * + have proper reset events before enabling this. */ writestruct(wd, DATA, MVert, collmd->numverts, collmd->x); writestruct(wd, DATA, MVert, collmd->numverts, collmd->xnew); writestruct(wd, DATA, MFace, collmd->numfaces, collmd->mfaces); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 8baffff9ecf..a1c10a733ce 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -648,7 +648,6 @@ static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_addres bNodeTree *ntree = (bNodeTree *)id; /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - ntree->init = 0; /* to set callbacks and force setting types */ ntree->is_updating = false; ntree->typeinfo = nullptr; ntree->interface_type = nullptr; @@ -677,7 +676,6 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) { /* NOTE: writing and reading goes in sync, for speed. */ - ntree->init = 0; /* to set callbacks and force setting types */ ntree->is_updating = false; ntree->typeinfo = nullptr; ntree->interface_type = nullptr; @@ -1145,8 +1143,6 @@ static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo) } else { ntree->typeinfo = &NodeTreeTypeUndefined; - - ntree->init &= ~NTREE_TYPE_INIT; } /* Deprecated integer type. */ @@ -1177,8 +1173,6 @@ static void node_set_typeinfo(const struct bContext *C, } else { node->typeinfo = &NodeTypeUndefined; - - ntree->init &= ~NTREE_TYPE_INIT; } } @@ -1199,8 +1193,6 @@ static void node_socket_set_typeinfo(bNodeTree *ntree, } else { sock->typeinfo = &NodeSocketTypeUndefined; - - ntree->init &= ~NTREE_TYPE_INIT; } BKE_ntree_update_tag_socket_type(ntree, sock); } @@ -1218,8 +1210,6 @@ static void update_typeinfo(Main *bmain, } FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - ntree->init |= NTREE_TYPE_INIT; - if (treetype && STREQ(ntree->idname, treetype->idname)) { ntree_set_typeinfo(ntree, unregister ? nullptr : treetype); } @@ -1260,8 +1250,6 @@ static void update_typeinfo(Main *bmain, void ntreeSetTypes(const struct bContext *C, bNodeTree *ntree) { - ntree->init |= NTREE_TYPE_INIT; - ntree_set_typeinfo(ntree, ntreeTypeFind(ntree->idname)); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -2674,11 +2662,6 @@ bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname) ntree->id.flag |= LIB_EMBEDDED_DATA; } - /* Types are fully initialized at this point, - * if an undefined node is added later this will be reset. - */ - ntree->init |= NTREE_TYPE_INIT; - BLI_strncpy(ntree->idname, idname, sizeof(ntree->idname)); ntree_set_typeinfo(ntree, ntreeTypeFind(idname)); @@ -4503,6 +4486,8 @@ static void registerCompositNodes() register_node_type_cmp_sepycca(); register_node_type_cmp_combycca(); register_node_type_cmp_premulkey(); + register_node_type_cmp_separate_xyz(); + register_node_type_cmp_combine_xyz(); register_node_type_cmp_diff_matte(); register_node_type_cmp_distance_matte(); @@ -4668,6 +4653,7 @@ static void registerTextureNodes() register_node_type_sh_tangent(); register_node_type_sh_normal_map(); register_node_type_sh_hair_info(); + register_node_type_sh_point_info(); register_node_type_sh_volume_info(); register_node_type_tex_checker(); @@ -4801,6 +4787,7 @@ static void registerGeometryNodes() register_node_type_geo_join_geometry(); register_node_type_geo_material_replace(); register_node_type_geo_material_selection(); + register_node_type_geo_merge_by_distance(); register_node_type_geo_mesh_primitive_circle(); register_node_type_geo_mesh_primitive_cone(); register_node_type_geo_mesh_primitive_cube(); diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index be9777281cb..904a0de9a90 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -34,6 +34,7 @@ #include "NOD_node_declaration.hh" #include "NOD_node_tree_ref.hh" +#include "NOD_texture.h" #include "DEG_depsgraph_query.h" @@ -272,6 +273,12 @@ static OutputFieldDependency find_group_output_dependencies( while (!sockets_to_check.is_empty()) { const InputSocketRef *input_socket = sockets_to_check.pop(); + if (!input_socket->is_directly_linked() && + !field_state_by_socket_id[input_socket->id()].is_single) { + /* This socket uses a field as input by default. */ + return OutputFieldDependency::ForFieldSource(); + } + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { const NodeRef &origin_node = origin_socket->node(); const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 5045851d7f9..8faae6efb26 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -87,7 +87,9 @@ #include "BKE_camera.h" #include "BKE_collection.h" #include "BKE_constraint.h" +#include "BKE_crazyspace.h" #include "BKE_curve.h" +#include "BKE_curves.h" #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_duplilist.h" @@ -102,7 +104,6 @@ #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" -#include "BKE_hair.h" #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_idtype.h" @@ -324,66 +325,6 @@ static void object_free_data(ID *id) BKE_previewimg_free(&ob->preview); } -static void object_make_local(Main *bmain, ID *id, const int flags) -{ - if (!ID_IS_LINKED(id)) { - return; - } - - Object *ob = (Object *)id; - const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; - const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0; - bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; - bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; - BLI_assert(force_copy == false || force_copy != force_local); - - bool is_local = false, is_lib = false; - - /* - only lib users: do nothing (unless force_local is set) - * - only local users: set flag - * - mixed: make copy - * In case we make a whole lib's content local, - * we always want to localize, and we skip remapping (done later). - */ - - if (!force_local && !force_copy) { - BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); - if (lib_local || is_local) { - if (!is_lib) { - force_local = true; - } - else { - force_copy = true; - } - } - } - - if (force_local) { - BKE_lib_id_clear_library_data(bmain, &ob->id, flags); - BKE_lib_id_expand_local(bmain, &ob->id, flags); - if (clear_proxy) { - if (ob->proxy_from != nullptr) { - ob->proxy_from->proxy = nullptr; - ob->proxy_from->proxy_group = nullptr; - } - ob->proxy = ob->proxy_from = ob->proxy_group = nullptr; - } - } - else if (force_copy) { - Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id); - id_us_min(&ob_new->id); - - ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = nullptr; - - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(ob, ob_new); - - if (!lib_local) { - BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE); - } - } -} - static void library_foreach_modifiersForeachIDLink(void *user_data, Object *UNUSED(object), ID **id_pointer, @@ -439,51 +380,25 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) { Object *object = (Object *)id; - /* Object is special, proxies make things hard... */ - const int proxy_cb_flag = ((BKE_lib_query_foreachid_process_flags_get(data) & - IDWALK_NO_INDIRECT_PROXY_DATA_USAGE) == 0 && - (object->proxy || object->proxy_group)) ? - IDWALK_CB_INDIRECT_USAGE : - 0; - /* object data special case */ if (object->type == OB_EMPTY) { /* empty can have nullptr or Image */ - BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, proxy_cb_flag | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, IDWALK_CB_USER); } else { /* when set, this can't be nullptr */ if (object->data) { - BKE_LIB_FOREACHID_PROCESS_ID( - data, object->data, proxy_cb_flag | IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); + BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); } } BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF); - /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP); - - /* Special case! - * Since this field is set/owned by 'user' of this ID (and not ID itself), - * it is only indirect usage if proxy object is linked... Twisted. */ - { - const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( - data, - (object->proxy_from != nullptr && ID_IS_LINKED(object->proxy_from)) ? - IDWALK_CB_INDIRECT_USAGE : - 0, - true); - BKE_LIB_FOREACHID_PROCESS_IDSUPER( - data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); - BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); - } BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER); for (int i = 0; i < object->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], IDWALK_CB_USER); } /* Note that ob->gpd is deprecated, so no need to handle it here. */ @@ -496,8 +411,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) /* Note that ob->effect is deprecated, so no need to handle it here. */ if (object->pose) { - const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( - data, proxy_cb_flag, false); LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( data, @@ -512,7 +425,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) BKE_constraints_id_loop( &pchan->constraints, library_foreach_constraintObjectLooper, data)); } - BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } if (object->rigidbody_constraint) { @@ -647,9 +559,6 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre bArmature *arm = nullptr; if (ob->type == OB_ARMATURE) { arm = (bArmature *)ob->data; - if (arm && ob->pose && arm->act_bone) { - BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone)); - } } BKE_pose_blend_write(writer, ob->pose, arm); @@ -1325,7 +1234,7 @@ IDTypeInfo IDType_ID_OB = { /* init_data */ object_init_data, /* copy_data */ object_copy_data, /* free_data */ object_free_data, - /* make_local */ object_make_local, + /* make_local */ nullptr, /* foreach_id */ object_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ object_foreach_path, @@ -1506,10 +1415,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) } /* Only geometry objects should be able to get modifiers T25291. */ - if (ob->type == OB_HAIR) { - return (mti->modifyHair != nullptr) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); - } - if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) { + if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME, OB_CURVES)) { return (mti->modifyGeometrySet != nullptr); } if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { @@ -1867,6 +1773,12 @@ void BKE_object_free_derived_caches(Object *ob) object_update_from_subsurf_ccg(ob); + if (ob->runtime.editmesh_eval_cage && + ob->runtime.editmesh_eval_cage != reinterpret_cast<Mesh *>(ob->runtime.data_eval)) { + BKE_mesh_eval_delete(ob->runtime.editmesh_eval_cage); + } + ob->runtime.editmesh_eval_cage = nullptr; + if (ob->runtime.data_eval != nullptr) { if (ob->runtime.is_data_eval_owned) { ID *data_eval = ob->runtime.data_eval; @@ -1896,6 +1808,8 @@ void BKE_object_free_derived_caches(Object *ob) BKE_object_to_curve_clear(ob); BKE_object_free_curve_cache(ob); + BKE_crazyspace_api_eval_clear(ob); + /* Clear grease pencil data. */ if (ob->runtime.gpd_eval != nullptr) { BKE_gpencil_eval_delete(ob->runtime.gpd_eval); @@ -1906,6 +1820,8 @@ void BKE_object_free_derived_caches(Object *ob) BKE_geometry_set_free(ob->runtime.geometry_set_eval); ob->runtime.geometry_set_eval = nullptr; } + + MEM_SAFE_FREE(ob->runtime.editmesh_bb_cage); } void BKE_object_free_caches(Object *object) @@ -2182,8 +2098,8 @@ static const char *get_obdata_defname(int type) return DATA_("Armature"); case OB_SPEAKER: return DATA_("Speaker"); - case OB_HAIR: - return DATA_("Hair"); + case OB_CURVES: + return DATA_("HairCurves"); case OB_POINTCLOUD: return DATA_("PointCloud"); case OB_VOLUME: @@ -2257,8 +2173,8 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) return BKE_lightprobe_add(bmain, name); case OB_GPENCIL: return BKE_gpencil_data_addnew(bmain, name); - case OB_HAIR: - return BKE_hair_add(bmain, name); + case OB_CURVES: + return BKE_curves_add(bmain, name); case OB_POINTCLOUD: return BKE_pointcloud_add_default(bmain, name); case OB_VOLUME: @@ -2295,8 +2211,8 @@ int BKE_object_obdata_to_type(const ID *id) return OB_ARMATURE; case ID_LP: return OB_LIGHTPROBE; - case ID_HA: - return OB_HAIR; + case ID_CV: + return OB_CURVES; case ID_PT: return OB_POINTCLOUD; case ID_VO: @@ -2813,8 +2729,8 @@ Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplica id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; - case OB_HAIR: - if (dupflag & USER_DUP_HAIR) { + case OB_CURVES: + if (dupflag & USER_DUP_CURVES) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; @@ -2886,161 +2802,6 @@ bool BKE_object_obdata_is_libdata(const Object *ob) return (ob && ob->data && ID_IS_LINKED(ob->data)); } -/* -------------------------------------------------------------------- */ -/** \name Object Proxy API - * \{ */ - -/* when you make proxy, ensure the exposed layers are extern */ -static void armature_set_id_extern(Object *ob) -{ - bArmature *arm = (bArmature *)ob->data; - unsigned int lay = arm->layer_protected; - - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - if (!(pchan->bone->layer & lay)) { - id_lib_extern((ID *)pchan->custom); - } - } -} - -void BKE_object_copy_proxy_drivers(Object *ob, Object *target) -{ - if ((target->adt) && (target->adt->drivers.first)) { - - /* add new animdata block */ - if (!ob->adt) { - ob->adt = BKE_animdata_ensure_id(&ob->id); - } - - /* make a copy of all the drivers (for now), then correct any links that need fixing */ - BKE_fcurves_free(&ob->adt->drivers); - BKE_fcurves_copy(&ob->adt->drivers, &target->adt->drivers); - - LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->drivers) { - ChannelDriver *driver = fcu->driver; - - LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { - /* all drivers */ - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - if (dtar->id) { - if ((Object *)dtar->id == target) { - dtar->id = (ID *)ob; - } - else { - /* only on local objects because this causes indirect links - * 'a -> b -> c', blend to point directly to a.blend - * when a.blend has a proxy that's linked into `c.blend`. */ - if (!ID_IS_LINKED(ob)) { - id_lib_extern((ID *)dtar->id); - } - } - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - } -} - -void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob) -{ - /* paranoia checks */ - if (ID_IS_LINKED(ob) || !ID_IS_LINKED(target)) { - CLOG_ERROR(&LOG, "cannot make proxy"); - return; - } - - ob->proxy = target; - id_us_plus(&target->id); - ob->proxy_group = cob; - - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - DEG_id_tag_update(&target->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - - /* copy transform - * - cob means this proxy comes from a collection, just apply the matrix - * 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. */ - if (cob) { - ob->rotmode = target->rotmode; - mul_m4_m4m4(ob->obmat, cob->obmat, target->obmat); - if (cob->instance_collection) { /* should always be true */ - float tvec[3]; - mul_v3_mat3_m4v3(tvec, ob->obmat, cob->instance_collection->instance_offset); - sub_v3_v3(ob->obmat[3], tvec); - } - BKE_object_apply_mat4(ob, ob->obmat, false, true); - } - else { - BKE_object_transform_copy(ob, target); - ob->parent = target->parent; /* libdata */ - copy_m4_m4(ob->parentinv, target->parentinv); - } - - /* copy animdata stuff - drivers only for now... */ - BKE_object_copy_proxy_drivers(ob, target); - - /* skip constraints? */ - /* FIXME: this is considered by many as a bug */ - - /* set object type and link to data */ - ob->type = target->type; - ob->data = target->data; - id_us_plus((ID *)ob->data); /* ensures lib data becomes LIB_TAG_EXTERN */ - - /* copy material and index information */ - ob->actcol = ob->totcol = 0; - if (ob->mat) { - MEM_freeN(ob->mat); - } - if (ob->matbits) { - MEM_freeN(ob->matbits); - } - ob->mat = nullptr; - ob->matbits = nullptr; - if ((target->totcol) && (target->mat) && OB_TYPE_SUPPORT_MATERIAL(ob->type)) { - int i; - - ob->actcol = target->actcol; - ob->totcol = target->totcol; - - ob->mat = (Material **)MEM_dupallocN(target->mat); - ob->matbits = (char *)MEM_dupallocN(target->matbits); - for (i = 0; i < target->totcol; i++) { - /* don't need to run BKE_object_materials_test - * since we know this object is new and not used elsewhere */ - id_us_plus((ID *)ob->mat[i]); - } - } - - /* type conversions */ - if (target->type == OB_ARMATURE) { - copy_object_pose(ob, target, 0); /* data copy, object pointers in constraints */ - BKE_pose_rest(ob->pose, false); /* clear all transforms in channels */ - BKE_pose_rebuild(bmain, ob, (bArmature *)ob->data, true); /* set all internal links */ - - armature_set_id_extern(ob); - } - else if (target->type == OB_EMPTY) { - ob->empty_drawtype = target->empty_drawtype; - ob->empty_drawsize = target->empty_drawsize; - } - - /* copy IDProperties */ - if (ob->id.properties) { - IDP_FreeProperty(ob->id.properties); - ob->id.properties = nullptr; - } - if (target->id.properties) { - ob->id.properties = IDP_CopyProperty(target->id.properties); - } - - /* copy drawtype info */ - ob->dt = target->dt; -} - void BKE_object_obdata_size_init(struct Object *ob, const float size) { /* apply radius as a scale to types that support it */ @@ -3082,8 +2843,6 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size) } } -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Object Matrix Get/Set API * \{ */ @@ -3411,7 +3170,8 @@ static void give_parvert(Object *par, int nr, float vec[3]) if (par->type == OB_MESH) { Mesh *me = (Mesh *)par->data; BMEditMesh *em = me->edit_mesh; - Mesh *me_eval = (em) ? em->mesh_eval_final : BKE_object_get_evaluated_mesh(par); + Mesh *me_eval = (em) ? BKE_object_get_editmesh_eval_final(par) : + BKE_object_get_evaluated_mesh(par); if (me_eval) { int count = 0; @@ -3864,8 +3624,8 @@ BoundBox *BKE_object_boundbox_get(Object *ob) case OB_GPENCIL: bb = BKE_gpencil_boundbox_get(ob); break; - case OB_HAIR: - bb = BKE_hair_boundbox_get(ob); + case OB_CURVES: + bb = BKE_curves_boundbox_get(ob); break; case OB_POINTCLOUD: bb = BKE_pointcloud_boundbox_get(ob); @@ -4066,8 +3826,8 @@ void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool us } break; } - case OB_HAIR: { - BoundBox bb = *BKE_hair_boundbox_get(ob); + case OB_CURVES: { + BoundBox bb = *BKE_curves_boundbox_get(ob); BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); changed = true; break; @@ -4198,7 +3958,11 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph, /* pass */ } else { - BoundBox *bb = BKE_object_boundbox_get(dob->ob); + Object temp_ob = *dob->ob; + /* Do not modify the original boundbox. */ + temp_ob.runtime.bb = nullptr; + BKE_object_replace_data_on_shallow_copy(&temp_ob, dob->ob_data); + BoundBox *bb = BKE_object_boundbox_get(&temp_ob); if (bb) { int i; @@ -4210,6 +3974,8 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph, ok = true; } + + MEM_SAFE_FREE(temp_ob.runtime.bb); } } free_object_duplilist(lb); /* does restore */ @@ -4368,33 +4134,10 @@ void BKE_object_tfm_restore(Object *ob, void *obtfm_pt) /** \name Object Evaluation/Update API * \{ */ -static void object_handle_update_proxy(Depsgraph *depsgraph, - Scene *scene, - Object *object, - const bool do_proxy_update) -{ - /* The case when this is a collection proxy, object_update is called in collection.c */ - if (object->proxy == nullptr) { - return; - } - /* set pointer in library proxy target, for copying, but restore it */ - object->proxy->proxy_from = object; - // printf("set proxy pointer for later collection stuff %s\n", ob->id.name); - - /* the no-group proxy case, we call update */ - if (object->proxy_group == nullptr) { - if (do_proxy_update) { - // printf("call update, lib ob %s proxy %s\n", ob->proxy->id.name, ob->id.name); - BKE_object_handle_update(depsgraph, scene, object->proxy); - } - } -} - void BKE_object_handle_update_ex(Depsgraph *depsgraph, Scene *scene, Object *ob, - RigidBodyWorld *rbw, - const bool do_proxy_update) + RigidBodyWorld *rbw) { const ID *object_data = (ID *)ob->data; const bool recalc_object = (ob->id.recalc & ID_RECALC_ALL) != 0; @@ -4402,7 +4145,6 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph, ((object_data->recalc & ID_RECALC_ALL) != 0) : false; if (!recalc_object && !recalc_data) { - object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update); return; } /* Speed optimization for animation lookups. */ @@ -4431,22 +4173,17 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph, if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) { printf("recalcob %s\n", ob->id.name + 2); } - /* Handle proxy copy for target. */ - if (!BKE_object_eval_proxy_copy(depsgraph, ob)) { - BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr); - } + BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr); } if (recalc_data) { BKE_object_handle_data_update(depsgraph, scene, ob); } - - object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update); } void BKE_object_handle_update(Depsgraph *depsgraph, Scene *scene, Object *ob) { - BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr, true); + BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr); } void BKE_object_sculpt_data_create(Object *ob) @@ -4574,6 +4311,33 @@ Mesh *BKE_object_get_original_mesh(const Object *object) return result; } +Mesh *BKE_object_get_editmesh_eval_final(const Object *object) +{ + BLI_assert(!DEG_is_original_id(&object->id)); + BLI_assert(object->type == OB_MESH); + + const Mesh *mesh = static_cast<const Mesh *>(object->data); + if (mesh->edit_mesh == nullptr) { + /* Happens when requesting material of evaluated 3d font object: the evaluated object get + * converted to mesh, and it does not have edit mesh. */ + return nullptr; + } + + return reinterpret_cast<Mesh *>(object->runtime.data_eval); +} + +Mesh *BKE_object_get_editmesh_eval_cage(const Object *object) +{ + BLI_assert(!DEG_is_original_id(&object->id)); + BLI_assert(object->type == OB_MESH); + + const Mesh *mesh = static_cast<const Mesh *>(object->data); + BLI_assert(mesh->edit_mesh != nullptr); + UNUSED_VARS_NDEBUG(mesh); + + return object->runtime.editmesh_eval_cage; +} + Lattice *BKE_object_get_lattice(const Object *object) { ID *data = (ID *)object->data; @@ -5177,7 +4941,7 @@ bool BKE_object_supports_material_slots(struct Object *ob) OB_SURF, OB_FONT, OB_MBALL, - OB_HAIR, + OB_CURVES, OB_POINTCLOUD, OB_VOLUME, OB_GPENCIL); @@ -5204,6 +4968,9 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) runtime->object_as_temp_mesh = nullptr; runtime->object_as_temp_curve = nullptr; runtime->geometry_set_eval = nullptr; + + runtime->crazyspace_deform_imats = nullptr; + runtime->crazyspace_deform_cos = nullptr; } void BKE_object_runtime_free_data(Object *object) diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 5dcb753abf4..3082d6f25f3 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -371,7 +371,7 @@ static const Mesh *mesh_data_from_duplicator_object(Object *ob, if (em != nullptr) { /* Note that this will only show deformation if #eModifierMode_OnCage is enabled. * We could change this but it matches 2.7x behavior. */ - me_eval = em->mesh_eval_cage; + me_eval = BKE_object_get_editmesh_eval_cage(ob); if ((me_eval == nullptr) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : nullptr; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 904fe9be51f..803dde50d96 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -41,12 +41,12 @@ #include "BKE_armature.h" #include "BKE_constraint.h" #include "BKE_curve.h" +#include "BKE_curves.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_effect.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" -#include "BKE_hair.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_lattice.h" @@ -160,12 +160,6 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o /* includes all keys and modifiers */ switch (ob->type) { case OB_MESH: { -#if 0 - BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? BKE_editmesh_from_object(ob) : NULL; -#else - BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? ((Mesh *)ob->data)->edit_mesh : NULL; -#endif - CustomData_MeshMasks cddata_masks = scene->customdata_mask; CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH); /* Custom attributes should not be removed automatically. They might be used by the render @@ -192,25 +186,11 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL; cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR; } - if (em) { - makeDerivedMesh(depsgraph, scene, ob, em, &cddata_masks); /* was CD_MASK_BAREMESH */ - } - else { - makeDerivedMesh(depsgraph, scene, ob, NULL, &cddata_masks); - } + makeDerivedMesh(depsgraph, scene, ob, &cddata_masks); /* was CD_MASK_BAREMESH */ break; } case OB_ARMATURE: - if (ID_IS_LINKED(ob) && ob->proxy_from) { - if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) { - printf("Proxy copy error, lib Object: %s proxy Object: %s\n", - ob->id.name + 2, - ob->proxy_from->id.name + 2); - } - } - else { - BKE_pose_where_is(depsgraph, scene, ob); - } + BKE_pose_where_is(depsgraph, scene, ob); break; case OB_MBALL: @@ -234,8 +214,8 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o BKE_gpencil_update_layer_transforms(depsgraph, ob); break; } - case OB_HAIR: - BKE_hair_data_update(depsgraph, scene, ob); + case OB_CURVES: + BKE_curves_data_update(depsgraph, scene, ob); break; case OB_POINTCLOUD: BKE_pointcloud_data_update(depsgraph, scene, ob); @@ -322,33 +302,9 @@ void BKE_object_sync_to_original(Depsgraph *depsgraph, Object *object) object_sync_boundbox_to_original(object_orig, object); } -bool BKE_object_eval_proxy_copy(Depsgraph *depsgraph, Object *object) -{ - /* Handle proxy copy for target, */ - if (ID_IS_LINKED(object) && object->proxy_from) { - DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); - if (object->proxy_from->proxy_group) { - /* Transform proxy into group space. */ - Object *obg = object->proxy_from->proxy_group; - float imat[4][4]; - invert_m4_m4(imat, obg->obmat); - mul_m4_m4m4(object->obmat, imat, object->proxy_from->obmat); - /* Should always be true. */ - if (obg->instance_collection) { - add_v3_v3(object->obmat[3], obg->instance_collection->instance_offset); - } - } - else { - copy_m4_m4(object->obmat, object->proxy_from->obmat); - } - return true; - } - return false; -} - -void BKE_object_eval_uber_transform(Depsgraph *depsgraph, Object *object) +void BKE_object_eval_uber_transform(Depsgraph *UNUSED(depsgraph), Object *UNUSED(object)) { - BKE_object_eval_proxy_copy(depsgraph, object); + return; } void BKE_object_data_batch_cache_dirty_tag(ID *object_data) @@ -370,8 +326,8 @@ void BKE_object_data_batch_cache_dirty_tag(ID *object_data) case ID_GD: BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)object_data); break; - case ID_HA: - BKE_hair_batch_cache_dirty_tag((struct Hair *)object_data, BKE_HAIR_BATCH_DIRTY_ALL); + case ID_CV: + BKE_curves_batch_cache_dirty_tag((struct Curves *)object_data, BKE_CURVES_BATCH_DIRTY_ALL); break; case ID_PT: BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)object_data, diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 407375c4d22..72210eea71d 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1648,7 +1648,6 @@ static void sculpt_update_object(Depsgraph *depsgraph, ss->totvert = me->totvert; ss->totpoly = me->totpoly; ss->totfaces = me->totpoly; - ss->vert_normals = BKE_mesh_vertex_normals_ensure(me); ss->mvert = me->mvert; ss->mpoly = me->mpoly; ss->mloop = me->mloop; diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 4dba13ce4c2..aa08a6494d1 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -4640,7 +4640,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, * account when subdividing for instance. */ pind.mesh = psys_in_edit_mode(sim->depsgraph, psys) ? NULL : - psys->hair_out_mesh; /* XXX(@sybren) EEK. */ + psys->hair_out_mesh; /* XXX(@sybren): EEK. */ init_particle_interpolation(sim->ob, psys, pa, &pind); do_particle_interpolation(psys, p, pa, t, &pind, state); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 1926bbcda02..bfedd4d3f49 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1007,6 +1007,28 @@ typedef struct PBVHUpdateData { bool show_sculpt_face_sets; } PBVHUpdateData; +static void pbvh_update_normals_clear_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + float(*vnors)[3] = data->vnors; + + if (node->flag & PBVH_UpdateNormals) { + const int *verts = node->vert_indices; + const int totvert = node->uniq_verts; + for (int i = 0; i < totvert; i++) { + const int v = verts[i]; + const MVert *mvert = &pbvh->verts[v]; + if (mvert->flag & ME_VERT_PBVH_UPDATE) { + zero_v3(vnors[v]); + } + } + } +} + static void pbvh_update_normals_accum_task_cb(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -1107,6 +1129,8 @@ static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode) TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); + /* Zero normals before accumulation. */ + BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_clear_task_cb, &settings); BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings); BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings); } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 916a2786a98..ed9f10aaa91 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2522,7 +2522,7 @@ void BKE_scene_update_sound(Depsgraph *depsgraph, Main *bmain) Scene *scene = DEG_get_evaluated_scene(depsgraph); const int recalc = scene->id.recalc; BKE_sound_ensure_scene(scene); - if (recalc & ID_RECALC_AUDIO_SEEK) { + if (recalc & ID_RECALC_FRAME_CHANGE) { BKE_sound_seek_scene(bmain, scene); } if (recalc & ID_RECALC_AUDIO_FPS) { diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index 65809782f8f..525c4837bc4 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -92,7 +92,7 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene, return false; } - if (!GPU_compute_shader_support()) { + if (!(GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support())) { return false; } diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 9d66c354b54..2f63edebb67 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -580,7 +580,6 @@ static void ss_sync_ccg_from_derivedmesh(CCGSubSurf *ss, #endif MVert *mvert = dm->getVertArray(dm); MEdge *medge = dm->getEdgeArray(dm); - // MFace *mface = dm->getTessFaceArray(dm); /* UNUSED */ MVert *mv; MEdge *me; MLoop *mloop = dm->getLoopArray(dm), *ml; @@ -1129,44 +1128,6 @@ static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, MEdge *medge) } } -static void ccgDM_copyFinalFaceArray(DerivedMesh *dm, MFace *mface) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int index; - int totface; - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int i = 0; - DMFlagMat *faceFlags = ccgdm->faceFlags; - - totface = dm->getNumTessFaces(dm); - for (index = 0; index < totface; index++) { - CCGFace *f = ccgdm->faceMap[index].face; - int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f); - /* keep types in sync with MFace, avoid many conversions */ - char flag = (faceFlags) ? faceFlags[index].flag : ME_SMOOTH; - short mat_nr = (faceFlags) ? faceFlags[index].mat_nr : 0; - - for (S = 0; S < numVerts; S++) { - for (y = 0; y < gridSize - 1; y++) { - for (x = 0; x < gridSize - 1; x++) { - MFace *mf = &mface[i]; - mf->v1 = getFaceIndex(ss, f, S, x + 0, y + 0, edgeSize, gridSize); - mf->v2 = getFaceIndex(ss, f, S, x + 0, y + 1, edgeSize, gridSize); - mf->v3 = getFaceIndex(ss, f, S, x + 1, y + 1, edgeSize, gridSize); - mf->v4 = getFaceIndex(ss, f, S, x + 1, y + 0, edgeSize, gridSize); - mf->mat_nr = mat_nr; - mf->flag = flag; - mf->edcode = 0; - - i++; - } - } - } - } -} - typedef struct CopyFinalLoopArrayData { CCGDerivedMesh *ccgdm; MLoop *mloop; @@ -1457,63 +1418,6 @@ static void *ccgDM_get_edge_data_layer(DerivedMesh *dm, int type) return DM_get_edge_data_layer(dm, type); } -static void *ccgDM_get_tessface_data_layer(DerivedMesh *dm, int type) -{ - if (type == CD_ORIGINDEX) { - /* create origindex on demand to save memory */ - int *origindex; - - /* Avoid re-creation if the layer exists already */ - origindex = DM_get_tessface_data_layer(dm, CD_ORIGINDEX); - if (origindex) { - return origindex; - } - - DM_add_tessface_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); - origindex = DM_get_tessface_data_layer(dm, CD_ORIGINDEX); - - /* silly loop counting up */ - range_vn_i(origindex, dm->getNumTessFaces(dm), 0); - - return origindex; - } - - if (type == CD_TESSLOOPNORMAL) { - /* Create tessloopnormal on demand to save memory. */ - /* Note that since tessellated face corners are the same a loops in CCGDM, - * and since all faces have four loops/corners, we can simplify the code - * here by converting tessloopnormals from 'short (*)[4][3]' to 'short (*)[3]'. */ - short(*tlnors)[3]; - - /* Avoid re-creation if the layer exists already */ - tlnors = DM_get_tessface_data_layer(dm, CD_TESSLOOPNORMAL); - if (!tlnors) { - float(*lnors)[3]; - short(*tlnors_it)[3]; - const int numLoops = ccgDM_getNumLoops(dm); - int i; - - lnors = dm->getLoopDataArray(dm, CD_NORMAL); - if (!lnors) { - return NULL; - } - - DM_add_tessface_layer(dm, CD_TESSLOOPNORMAL, CD_CALLOC, NULL); - tlnors = tlnors_it = (short(*)[3])DM_get_tessface_data_layer(dm, CD_TESSLOOPNORMAL); - - /* With ccgdm, we have a simple one to one mapping between loops - * and tessellated face corners. */ - for (i = 0; i < numLoops; i++, tlnors_it++, lnors++) { - normal_float_to_short_v3(*tlnors_it, *lnors); - } - } - - return tlnors; - } - - return DM_get_tessface_data_layer(dm, type); -} - static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type) { if (type == CD_ORIGINDEX) { @@ -1551,46 +1455,6 @@ static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type) return DM_get_poly_data_layer(dm, type); } -static void *ccgDM_get_vert_data(DerivedMesh *dm, int index, int type) -{ - if (type == CD_ORIGINDEX) { - /* ensure creation of CD_ORIGINDEX layer */ - ccgDM_get_vert_data_layer(dm, type); - } - - return DM_get_vert_data(dm, index, type); -} - -static void *ccgDM_get_edge_data(DerivedMesh *dm, int index, int type) -{ - if (type == CD_ORIGINDEX) { - /* ensure creation of CD_ORIGINDEX layer */ - ccgDM_get_edge_data_layer(dm, type); - } - - return DM_get_edge_data(dm, index, type); -} - -static void *ccgDM_get_tessface_data(DerivedMesh *dm, int index, int type) -{ - if (ELEM(type, CD_ORIGINDEX, CD_TESSLOOPNORMAL)) { - /* ensure creation of CD_ORIGINDEX/CD_TESSLOOPNORMAL layers */ - ccgDM_get_tessface_data_layer(dm, type); - } - - return DM_get_tessface_data(dm, index, type); -} - -static void *ccgDM_get_poly_data(DerivedMesh *dm, int index, int type) -{ - if (type == CD_ORIGINDEX) { - /* ensure creation of CD_ORIGINDEX layer */ - ccgDM_get_tessface_data_layer(dm, type); - } - - return DM_get_poly_data(dm, index, type); -} - static int ccgDM_getNumGrids(DerivedMesh *dm) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; @@ -1705,25 +1569,6 @@ static BLI_bitmap **ccgDM_getGridHidden(DerivedMesh *dm) return ccgdm->gridHidden; } -static const MeshElemMap *ccgDM_getPolyMap(Object *ob, DerivedMesh *dm) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - - if (!ccgdm->multires.mmd && !ccgdm->pmap && ob->type == OB_MESH) { - Mesh *me = ob->data; - - BKE_mesh_vert_poly_map_create(&ccgdm->pmap, - &ccgdm->pmap_mem, - me->mpoly, - me->mloop, - me->totvert, - me->totpoly, - me->totloop); - } - - return ccgdm->pmap; -} - /* WARNING! *MUST* be called in an 'loops_cache_rwlock' protected thread context! */ static void ccgDM_recalcLoopTri(DerivedMesh *dm) { @@ -1773,17 +1618,11 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm) ccgdm->dm.copyVertArray = ccgDM_copyFinalVertArray; ccgdm->dm.copyEdgeArray = ccgDM_copyFinalEdgeArray; - ccgdm->dm.copyTessFaceArray = ccgDM_copyFinalFaceArray; ccgdm->dm.copyLoopArray = ccgDM_copyFinalLoopArray; ccgdm->dm.copyPolyArray = ccgDM_copyFinalPolyArray; - ccgdm->dm.getVertData = ccgDM_get_vert_data; - ccgdm->dm.getEdgeData = ccgDM_get_edge_data; - ccgdm->dm.getTessFaceData = ccgDM_get_tessface_data; - ccgdm->dm.getPolyData = ccgDM_get_poly_data; ccgdm->dm.getVertDataArray = ccgDM_get_vert_data_layer; ccgdm->dm.getEdgeDataArray = ccgDM_get_edge_data_layer; - ccgdm->dm.getTessFaceDataArray = ccgDM_get_tessface_data_layer; ccgdm->dm.getPolyDataArray = ccgDM_get_poly_data_layer; ccgdm->dm.getNumGrids = ccgDM_getNumGrids; ccgdm->dm.getGridSize = ccgDM_getGridSize; @@ -1792,7 +1631,6 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm) ccgdm->dm.getGridKey = ccgDM_getGridKey; ccgdm->dm.getGridFlagMats = ccgDM_getGridFlagMats; ccgdm->dm.getGridHidden = ccgDM_getGridHidden; - ccgdm->dm.getPolyMap = ccgDM_getPolyMap; ccgdm->dm.recalcLoopTri = ccgDM_recalcLoopTri; @@ -1848,7 +1686,7 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, int index; int i; int vertNum = 0, edgeNum = 0, faceNum = 0; - int *vertOrigIndex, *faceOrigIndex, *polyOrigIndex, *base_polyOrigIndex, *edgeOrigIndex; + int *vertOrigIndex, *polyOrigIndex, *base_polyOrigIndex, *edgeOrigIndex; short *edgeFlags = ccgdm->edgeFlags; DMFlagMat *faceFlags = ccgdm->faceFlags; int *polyidx = NULL; @@ -1884,7 +1722,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX); edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX); - faceOrigIndex = DM_get_tessface_data_layer(&ccgdm->dm, CD_ORIGINDEX); polyOrigIndex = DM_get_poly_data_layer(&ccgdm->dm, CD_ORIGINDEX); has_edge_cd = ((ccgdm->dm.edgeData.totlayer - (edgeOrigIndex ? 1 : 0)) != 0); @@ -2006,12 +1843,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, /* Copy over poly data, e.g. #CD_FACEMAP. */ CustomData_copy_data(&dm->polyData, &ccgdm->dm.polyData, origIndex, faceNum, 1); - /* Set original index data. */ - if (faceOrigIndex) { - /* reference the index in 'polyOrigIndex' */ - *faceOrigIndex = faceNum; - faceOrigIndex++; - } if (polyOrigIndex) { *polyOrigIndex = base_polyOrigIndex ? base_polyOrigIndex[origIndex] : origIndex; polyOrigIndex++; diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index ee9247e6e60..c37e2fb6144 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -67,6 +67,8 @@ #include "BKE_scene.h" #include "BKE_texture.h" +#include "NOD_texture.h" + #include "RE_texture.h" #include "BLO_read_write.h" @@ -721,10 +723,10 @@ void BKE_texture_get_value_ex(const Scene *scene, * if the texture didn't give an RGB value, copy the intensity across */ if (result_type & TEX_RGB) { - texres->tin = (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb); + texres->tin = (1.0f / 3.0f) * (texres->trgba[0] + texres->trgba[1] + texres->trgba[2]); } else { - copy_v3_fl(&texres->tr, texres->tin); + copy_v3_fl(texres->trgba, texres->tin); } } diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc index cb05337ef2a..b2011d2baf7 100644 --- a/source/blender/blenkernel/intern/type_conversions.cc +++ b/source/blender/blenkernel/intern/type_conversions.cc @@ -62,6 +62,11 @@ static bool float_to_bool(const float &a) { return a > 0.0f; } +static int8_t float_to_int8(const float &a) +{ + return std::clamp( + a, float(std::numeric_limits<int8_t>::min()), float(std::numeric_limits<int8_t>::max())); +} static ColorGeometry4f float_to_color(const float &a) { return ColorGeometry4f(a, a, a, 1.0f); @@ -83,6 +88,10 @@ static bool float2_to_bool(const float2 &a) { return !is_zero_v2(a); } +static int8_t float2_to_int8(const float2 &a) +{ + return float_to_int8((a.x + a.y) / 2.0f); +} static ColorGeometry4f float2_to_color(const float2 &a) { return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); @@ -92,6 +101,10 @@ static bool float3_to_bool(const float3 &a) { return !is_zero_v3(a); } +static int8_t float3_to_int8(const float3 &a) +{ + return float_to_int8((a.x + a.y + a.z) / 3.0f); +} static float float3_to_float(const float3 &a) { return (a.x + a.y + a.z) / 3.0f; @@ -113,6 +126,11 @@ static bool int_to_bool(const int32_t &a) { return a > 0; } +static int8_t int_to_int8(const int32_t &a) +{ + return std::clamp( + a, int(std::numeric_limits<int8_t>::min()), int(std::numeric_limits<int8_t>::max())); +} static float int_to_float(const int32_t &a) { return (float)a; @@ -130,10 +148,39 @@ static ColorGeometry4f int_to_color(const int32_t &a) return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); } +static bool int8_to_bool(const int8_t &a) +{ + return a > 0; +} +static int int8_to_int(const int8_t &a) +{ + return static_cast<int>(a); +} +static float int8_to_float(const int8_t &a) +{ + return (float)a; +} +static float2 int8_to_float2(const int8_t &a) +{ + return float2((float)a); +} +static float3 int8_to_float3(const int8_t &a) +{ + return float3((float)a); +} +static ColorGeometry4f int8_to_color(const int8_t &a) +{ + return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); +} + static float bool_to_float(const bool &a) { return (bool)a; } +static int8_t bool_to_int8(const bool &a) +{ + return static_cast<int8_t>(a); +} static int32_t bool_to_int(const bool &a) { return (int32_t)a; @@ -163,6 +210,10 @@ static int32_t color_to_int(const ColorGeometry4f &a) { return (int)rgb_to_grayscale(a); } +static int8_t color_to_int8(const ColorGeometry4f &a) +{ + return int_to_int8(color_to_int(a)); +} static float2 color_to_float2(const ColorGeometry4f &a) { return float2(a.r, a.g); @@ -180,33 +231,46 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<float, float3, float_to_float3>(conversions); add_implicit_conversion<float, int32_t, float_to_int>(conversions); add_implicit_conversion<float, bool, float_to_bool>(conversions); + add_implicit_conversion<float, int8_t, float_to_int8>(conversions); add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions); add_implicit_conversion<float2, float3, float2_to_float3>(conversions); add_implicit_conversion<float2, float, float2_to_float>(conversions); add_implicit_conversion<float2, int32_t, float2_to_int>(conversions); add_implicit_conversion<float2, bool, float2_to_bool>(conversions); + add_implicit_conversion<float2, int8_t, float2_to_int8>(conversions); add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions); add_implicit_conversion<float3, bool, float3_to_bool>(conversions); + add_implicit_conversion<float3, int8_t, float3_to_int8>(conversions); add_implicit_conversion<float3, float, float3_to_float>(conversions); add_implicit_conversion<float3, int32_t, float3_to_int>(conversions); add_implicit_conversion<float3, float2, float3_to_float2>(conversions); add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions); add_implicit_conversion<int32_t, bool, int_to_bool>(conversions); + add_implicit_conversion<int32_t, int8_t, int_to_int8>(conversions); add_implicit_conversion<int32_t, float, int_to_float>(conversions); add_implicit_conversion<int32_t, float2, int_to_float2>(conversions); add_implicit_conversion<int32_t, float3, int_to_float3>(conversions); add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions); + add_implicit_conversion<int8_t, bool, int8_to_bool>(conversions); + add_implicit_conversion<int8_t, int32_t, int8_to_int>(conversions); + add_implicit_conversion<int8_t, float, int8_to_float>(conversions); + add_implicit_conversion<int8_t, float2, int8_to_float2>(conversions); + add_implicit_conversion<int8_t, float3, int8_to_float3>(conversions); + add_implicit_conversion<int8_t, ColorGeometry4f, int8_to_color>(conversions); + add_implicit_conversion<bool, float, bool_to_float>(conversions); + add_implicit_conversion<bool, int8_t, bool_to_int8>(conversions); add_implicit_conversion<bool, int32_t, bool_to_int>(conversions); add_implicit_conversion<bool, float2, bool_to_float2>(conversions); add_implicit_conversion<bool, float3, bool_to_float3>(conversions); add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions); add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions); + add_implicit_conversion<ColorGeometry4f, int8_t, color_to_int8>(conversions); add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions); add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions); add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions); diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 4d94132e6fd..9effeb831b6 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -726,14 +726,12 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - if (codec_id == AV_CODEC_ID_VP9) { - if (rd->im_format.planes == R_IMF_PLANES_RGBA) { - c->pix_fmt = AV_PIX_FMT_YUVA420P; - } + if (codec_id == AV_CODEC_ID_VP9 && rd->im_format.planes == R_IMF_PLANES_RGBA) { + c->pix_fmt = AV_PIX_FMT_YUVA420P; } - - /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */ - if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && context->ffmpeg_crf == 0) { + else if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && + context->ffmpeg_crf == 0) { + /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */ c->pix_fmt = AV_PIX_FMT_YUV444P; } diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh index b5981028893..ce7df1ff4b9 100644 --- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh +++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh @@ -28,10 +28,12 @@ namespace blender::threading { +#ifndef WITH_TBB namespace enumerable_thread_specific_utils { inline std::atomic<int> next_id = 0; inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed); } // namespace enumerable_thread_specific_utils +#endif /** * This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 81c969d02d0..f451f58f6cf 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -107,6 +107,20 @@ struct float4x4 { return &values[0][0]; } + float *operator[](const int64_t index) + { + BLI_assert(index >= 0); + BLI_assert(index < 4); + return &values[index][0]; + } + + const float *operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < 4); + return &values[index][0]; + } + using c_style_float4x4 = float[4][4]; c_style_float4x4 &ptr() { diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 65d654bc930..03b6ff25a4f 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -404,6 +404,7 @@ void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]); void scale_m3_fl(float R[3][3], float scale); void scale_m4_fl(float R[4][4], float scale); +void scale_m4_v2(float R[4][4], const float scale[2]); /** * This computes the overall volume scale factor of a transformation matrix. diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index 658cc0c3825..8881983fbec 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -49,7 +49,7 @@ void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1); * This function uses an alternative method to get environment variables that does pick up on * runtime environment variables. The result will be UTF-8 encoded. */ -const char *BLI_getenv(const char *env) ATTR_NONNULL(1); +const char *BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Returns in `string` the concatenation of `dir` and `file` (also with `relabase` on the @@ -337,13 +337,13 @@ void BLI_path_frame_strip(char *path, char *r_ext) ATTR_NONNULL(); /** * Check if we have '#' chars, usable for #BLI_path_frame, #BLI_path_frame_range */ -bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(); +bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Checks for a relative path (ignoring Blender's "//") prefix * (unlike `!BLI_path_is_rel(path)`). * When false, #BLI_path_abs_from_cwd would expand the absolute path. */ -bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(); +bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Checks for relative path, expanding them relative to the current working directory. * \returns true if the expansion was performed. @@ -367,7 +367,7 @@ bool BLI_path_is_rel(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; /** * Return true if the path is a UNC share. */ -bool BLI_path_is_unc(const char *path); +bool BLI_path_is_unc(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Creates a display string from path to be used menus and the user interface. @@ -404,6 +404,20 @@ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char # define BLI_path_ncmp strncmp #endif +/** + * Returns the result of #BLI_path_cmp with both paths normalized and slashes made native. + * + * \note #BLI_path_cmp is used for Blender's internal logic to consider paths to be the same + * #BLI_path_cmp_normalized may be used in when handling other kinds of paths + * (e.g. importers/exporters) but should be used consistently. + * + * Checking the normalized paths is not a guarantee the paths reference different files. + * An equivalent to Python's `os.path.samefile` could be supported for checking if paths + * point to the same location on the file-system (following symbolic-links). + */ +int BLI_path_cmp_normalized(const char *p1, const char *p2) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; + /* These values need to be hard-coded in structs, dna does not recognize defines */ /* also defined in `DNA_space_types.h`. */ #ifndef FILE_MAXDIR diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 3769e711a50..32a03213dc1 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -337,6 +337,18 @@ constexpr int64_t StringRefBase::find(StringRef str, int64_t pos) const return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast<size_t>(pos))); } +constexpr int64_t StringRefBase::rfind(char c, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64(std::string_view(*this).rfind(c, static_cast<size_t>(pos))); +} + +constexpr int64_t StringRefBase::rfind(StringRef str, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64(std::string_view(*this).rfind(str, static_cast<size_t>(pos))); +} + constexpr int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); @@ -346,7 +358,9 @@ constexpr int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) con constexpr int64_t StringRefBase::find_first_of(char c, int64_t pos) const { - return this->find_first_of(StringRef(&c, 1), pos); + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_first_of(c, static_cast<size_t>(pos))); } constexpr int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const @@ -358,7 +372,8 @@ constexpr int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) cons constexpr int64_t StringRefBase::find_last_of(char c, int64_t pos) const { - return this->find_last_of(StringRef(&c, 1), pos); + BLI_assert(pos >= 0); + return index_or_npos_to_int64(std::string_view(*this).find_last_of(c, static_cast<size_t>(pos))); } constexpr int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const @@ -370,7 +385,9 @@ constexpr int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) constexpr int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const { - return this->find_first_not_of(StringRef(&c, 1), pos); + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_first_not_of(c, static_cast<size_t>(pos))); } constexpr int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const @@ -382,7 +399,9 @@ constexpr int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) constexpr int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const { - return this->find_last_not_of(StringRef(&c, 1), pos); + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_last_not_of(c, static_cast<size_t>(pos))); } constexpr StringRef StringRefBase::trim() const diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh index 0aac96f93bc..ed744d09314 100644 --- a/source/blender/blenlib/BLI_vector_set.hh +++ b/source/blender/blenlib/BLI_vector_set.hh @@ -570,6 +570,10 @@ class VectorSet { if (this->size() == 0) { try { slots_.reinitialize(total_slots); + if (keys_ != nullptr) { + this->deallocate_keys_array(keys_); + keys_ = nullptr; + } keys_ = this->allocate_keys_array(usable_slots); } catch (...) { diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e9446f36c83..31e550379f1 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -363,8 +363,8 @@ if(WITH_GMP) endif() if(WIN32) - if (WITH_BLENDER_THUMBNAILER) - # Needed for querying the thumbnailer .dll in winstuff.c + if(WITH_BLENDER_THUMBNAILER) + # Needed for querying the `thumbnailer .dll` in `winstuff.c`. add_definitions(-DWITH_BLENDER_THUMBNAILER) endif() list(APPEND INC diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 3800fc58a5b..d61c6c015f6 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -3299,8 +3299,8 @@ static bool point_in_slice(const float p[3], return (h >= 0.0f && h <= 1.0f); } -/* adult sister defining the slice planes by the origin and the normal - * NOTE |normal| may not be 1 but defining the thickness of the slice */ +/* Adult sister defining the slice planes by the origin and the normal. + * NOTE: |normal| may not be 1 but defining the thickness of the slice. */ static bool point_in_slice_as(const float p[3], const float origin[3], const float normal[3]) { float h, rp[3]; diff --git a/source/blender/blenlib/intern/math_interp.c b/source/blender/blenlib/intern/math_interp.c index 54beb74abca..fed330aa2f0 100644 --- a/source/blender/blenlib/intern/math_interp.c +++ b/source/blender/blenlib/intern/math_interp.c @@ -706,9 +706,9 @@ void BLI_ewa_filter(const int width, } } - /* d should hopefully never be zero anymore */ + /* `d` should hopefully never be zero anymore. */ d = 1.0f / d; mul_v3_fl(result, d); - /* clipping can be ignored if alpha used, texr->ta already includes filtered edge */ + /* Clipping can be ignored if alpha used, `texr->trgba[3]` already includes filtered edge. */ result[3] = use_alpha ? result[3] * d : 1.0f; } diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index eaf76696a0a..f307672361b 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -2296,6 +2296,17 @@ void scale_m4_fl(float R[4][4], float scale) R[3][0] = R[3][1] = R[3][2] = 0.0; } +void scale_m4_v2(float R[4][4], const float scale[2]) +{ + R[0][0] = scale[0]; + R[1][1] = scale[1]; + R[2][2] = R[3][3] = 1.0; + R[0][1] = R[0][2] = R[0][3] = 0.0; + R[1][0] = R[1][2] = R[1][3] = 0.0; + R[2][0] = R[2][1] = R[2][3] = 0.0; + R[3][0] = R[3][1] = R[3][2] = 0.0; +} + void translate_m4(float mat[4][4], float Tx, float Ty, float Tz) { mat[3][0] += (Tx * mat[0][0] + Ty * mat[1][0] + Tz * mat[2][0]); diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 64bde1193a6..f39797c980c 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1829,3 +1829,23 @@ void BLI_path_slash_native(char *path) BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP); #endif } + +int BLI_path_cmp_normalized(const char *p1, const char *p2) +{ + BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), "Paths arguments must be absolute"); + + /* Normalize the paths so we can compare them. */ + char norm_p1[FILE_MAX]; + char norm_p2[FILE_MAX]; + + BLI_strncpy(norm_p1, p1, sizeof(norm_p1)); + BLI_strncpy(norm_p2, p2, sizeof(norm_p2)); + + BLI_path_slash_native(norm_p1); + BLI_path_slash_native(norm_p2); + + BLI_path_normalize(NULL, norm_p1); + BLI_path_normalize(NULL, norm_p2); + + return BLI_path_cmp(norm_p1, norm_p2); +} diff --git a/source/blender/blenlib/intern/voronoi_2d.c b/source/blender/blenlib/intern/voronoi_2d.c index 5b998973a20..65f9994bce7 100644 --- a/source/blender/blenlib/intern/voronoi_2d.c +++ b/source/blender/blenlib/intern/voronoi_2d.c @@ -777,7 +777,7 @@ static void voronoi_addTriangle( *r_triangles = MEM_reallocN(*r_triangles, sizeof(int[3]) * (*r_triangles_total + 1)); } else { - *r_triangles = MEM_callocN(sizeof(int[3]), "trianglulation triangles"); + *r_triangles = MEM_callocN(sizeof(int[3]), "triangulation triangles"); } triangle = (int *)&(*r_triangles)[(*r_triangles_total)]; diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc index c4016ca75e1..fe3130a846f 100644 --- a/source/blender/blenlib/tests/BLI_vector_set_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc @@ -271,4 +271,14 @@ TEST(vector_set, LookupKey) EXPECT_EQ(set.lookup_key_ptr("a"), set.lookup_key_ptr_as("a")); } +TEST(vector_set, GrowWhenEmpty) +{ + /* Tests that the internal keys array is freed correctly when growing an empty set. */ + VectorSet<int> set; + set.add(4); + set.remove(4); + EXPECT_TRUE(set.is_empty()); + set.reserve(100); +} + } // namespace blender::tests diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index c4c3b42cb63..066b180dcc9 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -117,8 +117,6 @@ typedef struct BlendFileReadReport { /* Number of root override IDs that were resynced. */ int resynced_lib_overrides; - /* Number of (non-converted) linked proxies. */ - int linked_proxies; /* Number of proxies converted to library overrides. */ int proxies_to_lib_overrides_success; /* Number of proxies that failed to convert to library overrides. */ diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 245514d4977..1ff713160df 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -86,10 +86,6 @@ if(WITH_BUILDINFO) add_definitions(-DWITH_BUILDINFO) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_CODEC_FFMPEG) add_definitions(-DWITH_FFMPEG) endif() diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index f3c92aec338..a334d70819d 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -419,9 +419,6 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, fd->skip_flags = params->skip_flags; BLI_strncpy(fd->relabase, filename, sizeof(fd->relabase)); - /* clear ob->proxy_from pointers in old main */ - blo_clear_proxy_pointers_from_lib(oldmain); - /* separate libraries from old main */ blo_split_main(&old_mainlist, oldmain); /* add the library pointers in oldmap lookup */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 38f2d8bb22c..90366f5b80d 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1580,15 +1580,6 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, } } -void blo_clear_proxy_pointers_from_lib(Main *oldmain) -{ - LISTBASE_FOREACH (Object *, ob, &oldmain->objects) { - if (ID_IS_LINKED(ob) && ob->proxy_from != NULL && !ID_IS_LINKED(ob->proxy_from)) { - ob->proxy_from = NULL; - } - } -} - /* XXX disabled this feature - packed files also belong in temp saves and quit.blend, * to make restore work. */ @@ -2997,7 +2988,7 @@ static const char *dataname(short id_code) return "Data from CF"; case ID_WS: return "Data from WS"; - case ID_HA: + case ID_CV: return "Data from HA"; case ID_PT: return "Data from PT"; @@ -3207,18 +3198,8 @@ static void read_libblock_undo_restore_identical( id_old->recalc |= direct_link_id_restore_recalc_exceptions(id_old); id_old->recalc_after_undo_push = 0; - /* As usual, proxies require some special love... - * In `blo_clear_proxy_pointers_from_lib()` we clear all `proxy_from` pointers to local IDs, for - * undo. This is required since we do not re-read linked data in that case, so we also do not - * re-'lib_link' their pointers. - * Those `proxy_from` pointers are then re-defined properly when lib_linking the newly read local - * object. However, in case of re-used data 'as-is', we never lib_link it again, so we have to - * fix those backward pointers here. */ if (GS(id_old->name) == ID_OB) { Object *ob = (Object *)id_old; - if (ob->proxy != NULL) { - ob->proxy->proxy_from = ob; - } /* For undo we stay in object mode during undo presses, so keep editmode disabled for re-used * data-blocks too. */ ob->mode &= ~OB_MODE_EDIT; diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index 21b0354b097..44045cf99dd 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -144,14 +144,6 @@ FileData *blo_filedata_from_memfile(struct MemFile *memfile, const struct BlendFileReadParams *params, struct BlendFileReadReport *reports); -/** - * Lib linked proxy objects point to our local data, we need - * to clear that pointer before reading the undo memfile since - * the object might be removed, it is set again in reading - * if the local object still exists. - * This is only valid for local proxy objects though, linked ones should not be affected here. - */ -void blo_clear_proxy_pointers_from_lib(struct Main *oldmain); void blo_make_packed_pointer_map(FileData *fd, struct Main *oldmain); /** * Set old main packed data to zero if it has been restored diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 8a22fd07c24..c34b8f735e5 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -78,6 +78,7 @@ #include "IMB_imbuf.h" /* for proxy / time-code versioning stuff. */ #include "NOD_common.h" +#include "NOD_composite.h" #include "NOD_texture.h" #include "BLO_readfile.h" diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index d9052c6b1f7..57105ca5884 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -349,7 +349,7 @@ static void do_version_scene_collection_convert( LISTBASE_FOREACH (LinkData *, link, &sc->objects) { Object *ob = link->data; if (ob) { - BKE_collection_object_add(bmain, collection, ob); + BKE_collection_object_add_notest(bmain, collection, ob); id_us_min(&ob->id); } } @@ -402,6 +402,8 @@ static void do_version_scene_collection_to_collection(Main *bmain, Scene *scene) do_version_layer_collection_pre( view_layer, &view_layer->layer_collections, enabled_set, selectable_set); + BKE_layer_collection_doversion_2_80(scene, view_layer); + BKE_layer_collection_sync(scene, view_layer); do_version_layer_collection_post( @@ -457,7 +459,7 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene) /* Note usually this would do slow collection syncing for view layers, * but since no view layers exists yet at this point it's fast. */ - BKE_collection_object_add(bmain, collections[layer], base->object); + BKE_collection_object_add_notest(bmain, collections[layer], base->object); } if (base->flag & SELECT) { @@ -1233,7 +1235,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) (*collection_hidden)->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; } - BKE_collection_object_add(bmain, *collection_hidden, ob); + BKE_collection_object_add_notest(bmain, *collection_hidden, ob); BKE_collection_object_remove(bmain, collection, ob, true); } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 888bd244007..87b5da09a60 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -32,11 +32,11 @@ #include "DNA_cachefile_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" +#include "DNA_curves_types.h" #include "DNA_fluid_types.h" #include "DNA_genfile.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" -#include "DNA_hair_types.h" #include "DNA_light_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -1120,10 +1120,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Hair and PointCloud attributes. */ - for (Hair *hair = bmain->hairs.first; hair != NULL; hair = hair->id.next) { - do_versions_point_attributes(&hair->pdata); - } + /* PointCloud attributes. */ for (PointCloud *pointcloud = bmain->pointclouds.first; pointcloud != NULL; pointcloud = pointcloud->id.next) { do_versions_point_attributes(&pointcloud->pdata); @@ -1422,10 +1419,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Hair and PointCloud attributes names. */ - LISTBASE_FOREACH (Hair *, hair, &bmain->hairs) { - do_versions_point_attribute_names(&hair->pdata); - } + /* PointCloud attributes names. */ LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) { do_versions_point_attribute_names(&pointcloud->pdata); } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 81fc6086951..001dffdca10 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -782,19 +782,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #blo_do_versions_300 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 301, 6)) { { /* Ensure driver variable names are unique within the driver. */ ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { @@ -829,6 +817,20 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_300 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } static void version_switch_node_input_prefix(Main *bmain) @@ -1105,6 +1107,15 @@ static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data)) return true; } +static bool seq_transform_filter_set(Sequence *seq, void *UNUSED(user_data)) +{ + StripTransform *transform = seq->strip->transform; + if (seq->strip->transform != NULL) { + transform->filter = SEQ_TRANSFORM_FILTER_BILINEAR; + } + return true; +} + static void do_version_subsurface_methods(bNode *node) { if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { @@ -2485,18 +2496,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 301, 6)) { /* Add node storage for map range node. */ FOREACH_NODETREE_BEGIN (bmain, ntree, id) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -2557,4 +2557,25 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + if (!MAIN_VERSION_ATLEAST(bmain, 302, 2)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_filter_set, NULL); + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index f1a2e678b2e..1f18405cdf9 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -42,6 +42,8 @@ #include "BKE_main.h" #include "BKE_node.h" +#include "NOD_shader.h" + #include "MEM_guardedalloc.h" #include "IMB_colormanagement.h" diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 064d7977c68..520059649e6 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -973,6 +973,13 @@ void blo_do_versions_userdef(UserDef *userdef) */ { /* Keep this block, even when empty. */ + if (!USER_VERSION_ATLEAST(301, 7)) { + /* io_scene_obj directory is gone, split into io_import_obj and io_export_obj, + * with io_import_obj enabled by default and io_export_obj replaced by the C++ version. + */ + BKE_addon_remove_safe(&userdef->addons, "io_scene_obj"); + BKE_addon_ensure(&userdef->addons, "io_import_obj"); + } } LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) { diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index 21296143226..379ff923229 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -55,24 +55,14 @@ bool BLT_lang_is_ime_supported(void); #define N_(msgid) msgid #define CTX_N_(context, msgid) msgid -/* Those macros should be used everywhere in UI code. */ -#ifdef WITH_INTERNATIONAL +/* These macros should be used everywhere in UI code. */ /*# define _(msgid) BLT_gettext(msgid) */ -# define IFACE_(msgid) BLT_translate_do_iface(NULL, msgid) -# define TIP_(msgid) BLT_translate_do_tooltip(NULL, msgid) -# define DATA_(msgid) BLT_translate_do_new_dataname(NULL, msgid) -# define CTX_IFACE_(context, msgid) BLT_translate_do_iface(context, msgid) -# define CTX_TIP_(context, msgid) BLT_translate_do_tooltip(context, msgid) -# define CTX_DATA_(context, msgid) BLT_translate_do_new_dataname(context, msgid) -#else -/*# define _(msgid) msgid */ -# define IFACE_(msgid) msgid -# define TIP_(msgid) msgid -# define DATA_(msgid) msgid -# define CTX_IFACE_(context, msgid) ((void)(0 ? (context) : 0), msgid) -# define CTX_TIP_(context, msgid) ((void)(0 ? (context) : 0), msgid) -# define CTX_DATA_(context, msgid) ((void)(0 ? (context) : 0), msgid) -#endif +#define IFACE_(msgid) BLT_translate_do_iface(NULL, msgid) +#define TIP_(msgid) BLT_translate_do_tooltip(NULL, msgid) +#define DATA_(msgid) BLT_translate_do_new_dataname(NULL, msgid) +#define CTX_IFACE_(context, msgid) BLT_translate_do_iface(context, msgid) +#define CTX_TIP_(context, msgid) BLT_translate_do_tooltip(context, msgid) +#define CTX_DATA_(context, msgid) BLT_translate_do_new_dataname(context, msgid) /* Helper macro, when we want to define a same msgid for multiple msgctxt... * Does nothing in C, but is "parsed" by our i18n py tools. @@ -121,7 +111,7 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_CURVE "Curve" #define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle" #define BLT_I18NCONTEXT_ID_GPENCIL "GPencil" -#define BLT_I18NCONTEXT_ID_HAIR "Hair" +#define BLT_I18NCONTEXT_ID_CURVES "Curves" #define BLT_I18NCONTEXT_ID_ID "ID" #define BLT_I18NCONTEXT_ID_IMAGE "Image" // #define BLT_I18NCONTEXT_ID_IPO "Ipo" /* DEPRECATED */ @@ -183,7 +173,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \ - BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_HAIR, "id_hair"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVES, "id_curves"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ID, "id_id"), \ BLT_I18NCONTEXTS_ITEM( \ BLT_I18NCONTEXT_ID_IMAGE, \ diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index e2ed005cf9e..a61327238fe 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -207,10 +207,6 @@ if(WITH_BULLET) add_definitions(-DWITH_BULLET) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 2f471bf0b81..b429399f310 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -451,6 +451,16 @@ static bool nearly_parallel_normalized(const float d1[3], const float d2[3]) return compare_ff(fabsf(direction_dot), 1.0f, BEVEL_EPSILON_ANG_DOT); } +/** + * calculate the determinant of a matrix formed by three vectors + * \return dot(a, cross(b, c)) = determinant(a, b, c) + */ +static float determinant_v3v3v3(const float a[3], const float b[3], const float c[3]) +{ + return a[0] * b[1] * c[2] + a[1] * b[2] * c[0] + a[2] * b[0] * c[1] - a[0] * b[2] * c[1] - + a[1] * b[0] * c[2] - a[2] * b[1] * c[0]; +} + /* Make a new BoundVert of the given kind, inserting it at the end of the circular linked * list with entry point bv->boundstart, and return it. */ static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float co[3]) @@ -4118,44 +4128,114 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in) VMesh *vm_out = new_adj_vmesh(bp->mem_arena, n_boundary, ns_out, vm_in->boundstart); /* First we adjust the boundary vertices of the input mesh, storing in output mesh. */ + BoundVert *bndv = vm_in->boundstart; for (int i = 0; i < n_boundary; i++) { + float co1[3], co2[3], acc[3]; + EdgeHalf *e = bndv->elast; + /* Generate tangents. This is hacked together and would ideally be done elsewhere and then only + * used here. */ + float tangent[3], tangent2[3], normal[3]; + bool convex = true; + bool orthogonal = false; + float stretch = 0.0f; + if (e) { + /* Projection direction is direction of the edge. */ + sub_v3_v3v3(tangent, e->e->v1->co, e->e->v2->co); + if (e->is_rev) { + negate_v3(tangent); + } + normalize_v3(tangent); + if (bndv->is_arc_start || bndv->is_patch_start) { + BMFace *face = e->fnext; + if (face) { + copy_v3_v3(normal, face->no); + } + else { + zero_v3(normal); + } + madd_v3_v3v3fl(co2, bndv->profile.middle, normal, 0.1f); + } + if (bndv->is_arc_start || bp->affect_type == BEVEL_AFFECT_VERTICES) { + EdgeHalf *e1 = bndv->next->elast; + BLI_assert(e1); + sub_v3_v3v3(tangent2, e1->e->v1->co, e1->e->v2->co); + if (e1->is_rev) { + negate_v3(tangent2); + } + normalize_v3(tangent2); + + convex = determinant_v3v3v3(tangent2, tangent, normal) < 0; + + add_v3_v3(tangent2, tangent); + normalize_v3(tangent2); + copy_v3_v3(tangent, tangent2); + } + /* Calculate a factor which determines how much the interpolated mesh is + * going to be stretched out into the direction of the tangent. + * It is currently using the difference along the tangent of the + * central point on the profile and the current center vertex position. */ + get_profile_point(bp, &bndv->profile, ns_in2, ns_in, co); + stretch = dot_v3v3(tangent, mesh_vert(vm_in, i, ns_in2, ns_in2)->co) - dot_v3v3(tangent, co); + stretch = fabsf(stretch); + /* Scale the tangent by stretch. The divide by ns_in2 comes from the Levin Paper. */ + mul_v3_fl(tangent, stretch / ns_in2); + orthogonal = bndv->is_patch_start; + } + else if (bndv->prev->is_patch_start) { + /* If this is the second edge of a patch and therefore #e is NULL, + * then e->fprev has to be used/not NULL. */ + BLI_assert(bndv->prev->elast); + BMFace *face = bndv->prev->elast->fnext; + if (face) { + copy_v3_v3(normal, face->no); + } + else { + zero_v3(normal); + } + orthogonal = true; + } + else { + /** Should only come here from make_cube_corner_adj_vmesh. */ + sub_v3_v3v3(co1, mesh_vert(vm_in, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 1)->co); + sub_v3_v3v3(co2, mesh_vert(vm_in, i, 0, 1)->co, mesh_vert(vm_in, i, 0, 2)->co); + cross_v3_v3v3(tangent, co1, co2); + /** The following constant is chosen to best match the old results. */ + normalize_v3_length(tangent, 1.5f / ns_out); + } + /** Copy corner vertex. */ copy_v3_v3(mesh_vert(vm_out, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 0)->co); + /** Copy the rest of the boundary vertices. */ for (int k = 1; k < ns_in; k++) { copy_v3_v3(co, mesh_vert(vm_in, i, 0, k)->co); - /* Smooth boundary rule. Custom profiles shouldn't be smoothed. */ - if (bp->profile_type != BEVEL_PROFILE_CUSTOM) { - float co1[3], co2[3], acc[3]; - copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co); - copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co); - - add_v3_v3v3(acc, co1, co2); - madd_v3_v3fl(acc, co, -2.0f); - madd_v3_v3fl(co, acc, -1.0f / 6.0f); - } + copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co); + copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co); + + add_v3_v3v3(acc, co1, co2); + if (bndv->is_arc_start) { + sub_v3_v3(co1, co); + sub_v3_v3(co2, co); + normalize_v3(co1); + normalize_v3(co2); + add_v3_v3v3(tangent, co1, co2); + /* This is an empirical formula to make the result look good. */ + normalize_v3(tangent); + float dot = convex ? fminf(0, dot_v3v3(tangent2, tangent)) : 1.0f; + mul_v3_fl(tangent, stretch / ns_in * dot); + } + else if (orthogonal) { + sub_v3_v3(co1, co); + cross_v3_v3v3(tangent, normal, co1); + /* This is an empirical formula to make the result look good. */ + normalize_v3_length(tangent, -bp->offset * 0.7071f / ns_in); + } + mul_v3_fl(co, 2.0f); + madd_v3_v3fl(co, acc, -0.25f); + madd_v3_v3fl(co, mesh_vert(vm_in, i, 1, k)->co, -0.5f); + add_v3_v3(co, tangent); copy_v3_v3(mesh_vert_canon(vm_out, i, 0, 2 * k)->co, co); } - } - /* Now adjust odd boundary vertices in output mesh, based on even ones. */ - BoundVert *bndv = vm_out->boundstart; - for (int i = 0; i < n_boundary; i++) { - for (int k = 1; k < ns_out; k += 2) { - get_profile_point(bp, &bndv->profile, k, ns_out, co); - - /* Smooth if using a non-custom profile. */ - if (bp->profile_type != BEVEL_PROFILE_CUSTOM) { - float co1[3], co2[3], acc[3]; - copy_v3_v3(co1, mesh_vert_canon(vm_out, i, 0, k - 1)->co); - copy_v3_v3(co2, mesh_vert_canon(vm_out, i, 0, k + 1)->co); - - add_v3_v3v3(acc, co1, co2); - madd_v3_v3fl(acc, co, -2.0f); - madd_v3_v3fl(co, acc, -1.0f / 6.0f); - } - - copy_v3_v3(mesh_vert_canon(vm_out, i, 0, k)->co, co); - } bndv = bndv->next; } vmesh_copy_equiv_verts(vm_out); @@ -4163,7 +4243,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in) /* Copy adjusted verts back into vm_in. */ for (int i = 0; i < n_boundary; i++) { for (int k = 0; k < ns_in; k++) { - copy_v3_v3(mesh_vert(vm_in, i, 0, k)->co, mesh_vert(vm_out, i, 0, 2 * k)->co); + copy_v3_v3(mesh_vert_canon(vm_in, i, 0, k)->co, mesh_vert_canon(vm_out, i, 0, 2 * k)->co); } } @@ -4248,7 +4328,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in) vmesh_copy_equiv_verts(vm_out); /* The center vertex is special. */ - gamma = sabin_gamma(n_boundary); + gamma = sabin_gamma(n_boundary) * 0.5f; beta = -gamma; /* Accumulate edge verts in co1, face verts in co2. */ float co1[3], co2[3]; diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc index e244bc377db..91883b0adee 100644 --- a/source/blender/bmesh/tools/bmesh_boolean.cc +++ b/source/blender/bmesh/tools/bmesh_boolean.cc @@ -192,12 +192,12 @@ static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out, bool keep_hidden BM_elem_flag_enable(bmv, KEEP_FLAG); } else { - new_bmvs[v] = NULL; + new_bmvs[v] = nullptr; } } for (int v : m_out.vert_index_range()) { const Vert *vertp = m_out.vert(v); - if (new_bmvs[v] == NULL) { + if (new_bmvs[v] == nullptr) { float co[3]; const double3 &d_co = vertp->co; for (int i = 0; i < 3; ++i) { diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index b9b365a3175..2f473ef2945 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -274,10 +274,14 @@ set(SRC # converter nodes nodes/COM_CombineColorNode.cc nodes/COM_CombineColorNode.h + nodes/COM_CombineXYZNode.cc + nodes/COM_CombineXYZNode.h nodes/COM_IDMaskNode.cc nodes/COM_IDMaskNode.h nodes/COM_SeparateColorNode.cc nodes/COM_SeparateColorNode.h + nodes/COM_SeparateXYZNode.cc + nodes/COM_SeparateXYZNode.h nodes/COM_MapRangeNode.cc nodes/COM_MapRangeNode.h @@ -631,10 +635,6 @@ list(APPEND SRC unset(GENSRC) unset(GENSRC_DIR) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_OPENIMAGEDENOISE) add_definitions(-DWITH_OPENIMAGEDENOISE) add_definitions(-DOIDN_STATIC_LIB) diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 6cf6c698a2f..d0b3ae74446 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -44,6 +44,7 @@ #include "COM_ColorSpillNode.h" #include "COM_ColorToBWNode.h" #include "COM_CombineColorNode.h" +#include "COM_CombineXYZNode.h" #include "COM_CompositorNode.h" #include "COM_ConvertAlphaNode.h" #include "COM_ConvertColorSpaceNode.h" @@ -96,6 +97,7 @@ #include "COM_ScaleOperation.h" #include "COM_SceneTimeNode.h" #include "COM_SeparateColorNode.h" +#include "COM_SeparateXYZNode.h" #include "COM_SetAlphaNode.h" #include "COM_SetValueOperation.h" #include "COM_SplitViewerNode.h" @@ -434,6 +436,12 @@ Node *COM_convert_bnode(bNode *b_node) case CMP_NODE_CONVERT_COLOR_SPACE: node = new ConvertColorSpaceNode(b_node); break; + case CMP_NODE_SEPARATE_XYZ: + node = new SeparateXYZNode(b_node); + break; + case CMP_NODE_COMBINE_XYZ: + node = new CombineXYZNode(b_node); + break; } return node; } diff --git a/source/blender/compositor/nodes/COM_CombineXYZNode.cc b/source/blender/compositor/nodes/COM_CombineXYZNode.cc new file mode 100644 index 00000000000..2b71b94e192 --- /dev/null +++ b/source/blender/compositor/nodes/COM_CombineXYZNode.cc @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_CombineXYZNode.h" + +#include "COM_ConvertOperation.h" + +namespace blender::compositor { + +CombineXYZNode::CombineXYZNode(bNode *editor_node) : Node(editor_node) +{ +} + +void CombineXYZNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &UNUSED(context)) const +{ + NodeInput *input_x_socket = this->get_input_socket(0); + NodeInput *input_y_socket = this->get_input_socket(1); + NodeInput *input_z_socket = this->get_input_socket(2); + NodeOutput *output_socket = this->get_output_socket(0); + + CombineChannelsOperation *operation = new CombineChannelsOperation(); + if (input_x_socket->is_linked()) { + operation->set_canvas_input_index(0); + } + else if (input_y_socket->is_linked()) { + operation->set_canvas_input_index(1); + } + else { + operation->set_canvas_input_index(2); + } + converter.add_operation(operation); + + converter.map_input_socket(input_x_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_y_socket, operation->get_input_socket(1)); + converter.map_input_socket(input_z_socket, operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket()); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineXYZNode.h b/source/blender/compositor/nodes/COM_CombineXYZNode.h new file mode 100644 index 00000000000..c3bb69b03ed --- /dev/null +++ b/source/blender/compositor/nodes/COM_CombineXYZNode.h @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +/** + * \brief SeparateXYZNode + * \ingroup Node + */ +class CombineXYZNode : public Node { + public: + CombineXYZNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index 605dc1dc84d..c360e519cf8 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -16,9 +16,12 @@ * Copyright 2018, Blender Foundation. */ -#include "COM_CryptomatteNode.h" #include "BKE_node.h" + +#include "NOD_composite.h" + #include "COM_ConvertOperation.h" +#include "COM_CryptomatteNode.h" #include "COM_MultilayerImageOperation.h" #include "COM_RenderLayersProg.h" #include "COM_SetAlphaMultiplyOperation.h" diff --git a/source/blender/compositor/nodes/COM_SeparateXYZNode.cc b/source/blender/compositor/nodes/COM_SeparateXYZNode.cc new file mode 100644 index 00000000000..749116d6217 --- /dev/null +++ b/source/blender/compositor/nodes/COM_SeparateXYZNode.cc @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_SeparateXYZNode.h" + +#include "COM_ConvertOperation.h" + +namespace blender::compositor { + +SeparateXYZNode::SeparateXYZNode(bNode *editor_node) : Node(editor_node) +{ + /* pass */ +} + +void SeparateXYZNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &UNUSED(context)) const +{ + NodeInput *vector_socket = this->get_input_socket(0); + NodeOutput *output_x_socket = this->get_output_socket(0); + NodeOutput *output_y_socket = this->get_output_socket(1); + NodeOutput *output_z_socket = this->get_output_socket(2); + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(0); + converter.add_operation(operation); + converter.map_input_socket(vector_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_x_socket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(1); + converter.add_operation(operation); + converter.map_input_socket(vector_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_y_socket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(2); + converter.add_operation(operation); + converter.map_input_socket(vector_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_z_socket, operation->get_output_socket(0)); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateXYZNode.h b/source/blender/compositor/nodes/COM_SeparateXYZNode.h new file mode 100644 index 00000000000..1efa017d9e3 --- /dev/null +++ b/source/blender/compositor/nodes/COM_SeparateXYZNode.h @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +/** + * \brief SeparateXYZNode + * \ingroup Node + */ +class SeparateXYZNode : public Node { + public: + SeparateXYZNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index f5d47478a8d..069d00b5e66 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -22,6 +22,8 @@ #include "BKE_image.h" #include "BKE_node.h" +#include "NOD_texture.h" + namespace blender::compositor { TextureBaseOperation::TextureBaseOperation() @@ -131,11 +133,9 @@ void TextureBaseOperation::execute_pixel_sampled(float output[4], retval = multitex_ext( texture_, vec, nullptr, nullptr, 0, &texres, thread_id, pool_, scene_color_manage_, false); - output[3] = texres.talpha ? texres.ta : texres.tin; + output[3] = texres.talpha ? texres.trgba[3] : texres.tin; if (retval & TEX_RGB) { - output[0] = texres.tr; - output[1] = texres.tg; - output[2] = texres.tb; + copy_v3_v3(output, texres.trgba); } else { output[0] = output[1] = output[2] = output[3]; @@ -184,11 +184,9 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, scene_color_manage_, false); - it.out[3] = tex_result.talpha ? tex_result.ta : tex_result.tin; + it.out[3] = tex_result.talpha ? tex_result.trgba[3] : tex_result.tin; if (retval & TEX_RGB) { - it.out[0] = tex_result.tr; - it.out[1] = tex_result.tg; - it.out[2] = tex_result.tb; + copy_v3_v3(it.out, tex_result.trgba); } else { it.out[0] = it.out[1] = it.out[2] = it.out[3]; diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 0461d8b63fd..8e4a7dafcd6 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -111,9 +111,6 @@ typedef enum eDepsObjectComponentType { /* Parameters Component - Default when nothing else fits * (i.e. just SDNA property setting). */ DEG_OB_COMP_PARAMETERS, - /* Generic "Proxy-Inherit" Component. - * TODO(sergey): Also for instancing of subgraphs? */ - DEG_OB_COMP_PROXY, /* Animation Component. * * TODO(sergey): merge in with parameters? */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index b2e136c3d3b..990021dc29b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -125,10 +125,6 @@ bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan) { - /* Proxies don't have BONE_SEGMENTS */ - if (ID_IS_LINKED(object) && object->proxy_from != nullptr) { - return false; - } return check_pchan_has_bbone(object, pchan); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 79d674e8415..ba1432d9ec8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -599,7 +599,7 @@ void DepsgraphNodeBuilder::build_id(ID *id) case ID_CU: case ID_LT: case ID_GD: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: build_object_data_geometry_datablock(id); @@ -726,9 +726,6 @@ void DepsgraphNodeBuilder::build_object(int base_index, eDepsNode_LinkedState_Type linked_state, bool is_visible) { - if (object->proxy != nullptr) { - object->proxy->proxy_from = object; - } const bool has_object = built_map_.checkIsBuiltAndTag(object); /* When there is already object in the dependency graph accumulate visibility an linked state @@ -819,9 +816,6 @@ void DepsgraphNodeBuilder::build_object(int base_index, (object->pd->tex != nullptr)) { build_texture(object->pd->tex); } - /* Proxy object to copy from. */ - build_object_proxy_from(object, is_visible); - build_object_proxy_group(object, is_visible); /* Object dupligroup. */ if (object->instance_collection != nullptr) { build_object_instance_collection(object, is_visible); @@ -875,22 +869,6 @@ void DepsgraphNodeBuilder::build_object_flags(int base_index, }); } -void DepsgraphNodeBuilder::build_object_proxy_from(Object *object, bool is_object_visible) -{ - if (object->proxy_from == nullptr) { - return; - } - build_object(-1, object->proxy_from, DEG_ID_LINKED_INDIRECTLY, is_object_visible); -} - -void DepsgraphNodeBuilder::build_object_proxy_group(Object *object, bool is_object_visible) -{ - if (object->proxy_group == nullptr) { - return; - } - build_object(-1, object->proxy_group, DEG_ID_LINKED_INDIRECTLY, is_object_visible); -} - void DepsgraphNodeBuilder::build_object_instance_collection(Object *object, bool is_object_visible) { if (object->instance_collection == nullptr) { @@ -916,18 +894,13 @@ void DepsgraphNodeBuilder::build_object_data(Object *object) case OB_MBALL: case OB_LATTICE: case OB_GPENCIL: - case OB_HAIR: + case OB_CURVES: case OB_POINTCLOUD: case OB_VOLUME: build_object_data_geometry(object); break; case OB_ARMATURE: - if (ID_IS_LINKED(object) && object->proxy_from != nullptr) { - build_proxy_rig(object); - } - else { - build_rig(object); - } + build_rig(object); break; case OB_LAMP: build_object_data_light(object); @@ -1183,12 +1156,6 @@ void DepsgraphNodeBuilder::build_driver_variables(ID *id, FCurve *fcurve) } build_id(dtar->id); build_driver_id_property(dtar->id, dtar->rna_path); - /* Corresponds to dtar_id_ensure_proxy_from(). */ - if ((GS(dtar->id->name) == ID_OB) && (((Object *)dtar->id)->proxy_from != nullptr)) { - Object *proxy_from = ((Object *)dtar->id)->proxy_from; - build_id(&proxy_from->id); - build_driver_id_property(&proxy_from->id, dtar->rna_path); - } } DRIVER_TARGETS_LOOPER_END; } @@ -1596,7 +1563,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata) op_node->set_as_entry(); break; } - case ID_HA: { + case ID_CV: { op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); op_node->set_as_entry(); break; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index a1db4aaf693..6e319383478 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -184,8 +184,6 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { Object *object, eDepsNode_LinkedState_Type linked_state, bool is_visible); - virtual void build_object_proxy_from(Object *object, bool is_object_visible); - virtual void build_object_proxy_group(Object *object, bool is_object_visible); virtual void build_object_instance_collection(Object *object, bool is_object_visible); virtual void build_object_from_layer(int base_index, Object *object, @@ -232,7 +230,6 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con); virtual void build_splineik_pose(Object *object, bPoseChannel *pchan, bConstraint *con); virtual void build_rig(Object *object); - virtual void build_proxy_rig(Object *object); virtual void build_armature(bArmature *armature); virtual void build_armature_bones(ListBase *bones); virtual void build_shapekeys(Key *key); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc index e8dda7b8de4..0e196450f60 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc @@ -306,72 +306,4 @@ void DepsgraphNodeBuilder::build_rig(Object *object) } } -void DepsgraphNodeBuilder::build_proxy_rig(Object *object) -{ - bArmature *armature = (bArmature *)object->data; - OperationNode *op_node; - Object *object_cow = get_cow_datablock(object); - /* Sanity check. */ - BLI_assert(object->pose != nullptr); - /* Armature. */ - build_armature(armature); - /* speed optimization for animation lookups */ - BKE_pose_channels_hash_ensure(object->pose); - if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { - BKE_pose_update_constraint_flags(object->pose); - } - op_node = add_operation_node( - &object->id, - NodeType::EVAL_POSE, - OperationCode::POSE_INIT, - [object_cow](::Depsgraph *depsgraph) { BKE_pose_eval_proxy_init(depsgraph, object_cow); }); - op_node->set_as_entry(); - - int pchan_index = 0; - LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - op_node = add_operation_node( - &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL); - op_node->set_as_entry(); - /* Bone is ready for solvers. */ - add_operation_node(&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_READY); - /* Bone is fully evaluated. */ - op_node = add_operation_node(&object->id, - NodeType::BONE, - pchan->name, - OperationCode::BONE_DONE, - [object_cow, pchan_index](::Depsgraph *depsgraph) { - BKE_pose_eval_proxy_copy_bone( - depsgraph, object_cow, pchan_index); - }); - op_node->set_as_exit(); - - /* Custom properties. */ - if (pchan->prop != nullptr) { - build_idproperties(pchan->prop); - add_operation_node( - &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan->name); - } - - /* Custom shape. */ - if (pchan->custom != nullptr) { - /* NOTE: The relation builder will ensure visibility of the custom shape object. */ - build_object(-1, pchan->custom, DEG_ID_LINKED_INDIRECTLY, false); - } - - pchan_index++; - } - op_node = add_operation_node(&object->id, - NodeType::EVAL_POSE, - OperationCode::POSE_CLEANUP, - [object_cow](::Depsgraph *depsgraph) { - BKE_pose_eval_proxy_cleanup(depsgraph, object_cow); - }); - op_node = add_operation_node( - &object->id, - NodeType::EVAL_POSE, - OperationCode::POSE_DONE, - [object_cow](::Depsgraph *depsgraph) { BKE_pose_eval_proxy_done(depsgraph, object_cow); }); - op_node->set_as_exit(); -} - } // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index cb43ef685d4..26dd7bc1363 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -555,7 +555,7 @@ void DepsgraphRelationBuilder::build_id(ID *id) case ID_MB: case ID_CU: case ID_LT: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: case ID_GD: @@ -787,9 +787,6 @@ void DepsgraphRelationBuilder::build_object(Object *object) (object->pd->tex != nullptr)) { build_texture(object->pd->tex); } - /* Proxy object to copy from. */ - build_object_proxy_from(object); - build_object_proxy_group(object); /* Object dupligroup. */ if (object->instance_collection != nullptr) { build_collection(nullptr, object, object->instance_collection); @@ -804,31 +801,6 @@ void DepsgraphRelationBuilder::build_object(Object *object) build_parameters(&object->id); } -void DepsgraphRelationBuilder::build_object_proxy_from(Object *object) -{ - if (object->proxy_from == nullptr) { - return; - } - /* Object is linked here (comes from the library). */ - build_object(object->proxy_from); - ComponentKey ob_transform_key(&object->proxy_from->id, NodeType::TRANSFORM); - ComponentKey proxy_transform_key(&object->id, NodeType::TRANSFORM); - add_relation(ob_transform_key, proxy_transform_key, "Proxy Transform"); -} - -void DepsgraphRelationBuilder::build_object_proxy_group(Object *object) -{ - if (ELEM(object->proxy_group, nullptr, object->proxy)) { - return; - } - /* Object is local here (local in .blend file, users interacts with it). */ - build_object(object->proxy_group); - OperationKey proxy_group_eval_key( - &object->proxy_group->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL); - OperationKey transform_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL); - add_relation(proxy_group_eval_key, transform_eval_key, "Proxy Group Transform"); -} - void DepsgraphRelationBuilder::build_object_from_layer_relations(Object *object) { OperationKey object_from_layer_entry_key( @@ -877,7 +849,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) case OB_MBALL: case OB_LATTICE: case OB_GPENCIL: - case OB_HAIR: + case OB_CURVES: case OB_POINTCLOUD: case OB_VOLUME: { build_object_data_geometry(object); @@ -895,12 +867,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) break; } case OB_ARMATURE: - if (ID_IS_LINKED(object) && object->proxy_from != nullptr) { - build_proxy_rig(object); - } - else { - build_rig(object); - } + build_rig(object); break; case OB_LAMP: build_object_data_light(object); @@ -1663,18 +1630,9 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu) } build_id(target_id); build_driver_id_property(target_id, dtar->rna_path); - /* Look up the proxy - matches dtar_id_ensure_proxy_from during evaluation. */ Object *object = nullptr; if (GS(target_id->name) == ID_OB) { object = (Object *)target_id; - if (object->proxy_from != nullptr) { - /* Redirect the target to the proxy, like in evaluation. */ - object = object->proxy_from; - target_id = &object->id; - /* Prepare the redirected target. */ - build_id(target_id); - build_driver_id_property(target_id, dtar->rna_path); - } } /* Special handling for directly-named bones. */ if ((dtar->flag & DTAR_FLAG_STRUCT_REF) && (object && object->type == OB_ARMATURE) && @@ -2034,7 +1992,7 @@ void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part) "Particle Texture -> Particle Reset", RELATION_FLAG_FLUSH_USER_EDIT_ONLY); add_relation(texture_key, particle_settings_eval_key, "Particle Texture -> Particle Eval"); - /* TODO(sergey): Consider moving texture space handling to an own + /* TODO(sergey): Consider moving texture space handling to its own * function. */ if (mtex->texco == TEXCO_OBJECT && mtex->object != nullptr) { ComponentKey object_key(&mtex->object->id, NodeType::TRANSFORM); @@ -2343,7 +2301,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) } break; } - case ID_HA: + case ID_CV: break; case ID_PT: break; @@ -2967,7 +2925,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) continue; } int rel_flag = (RELATION_FLAG_NO_FLUSH | RELATION_FLAG_GODMODE); - if ((ELEM(id_type, ID_ME, ID_HA, ID_PT, ID_VO) && comp_node->type == NodeType::GEOMETRY) || + if ((ELEM(id_type, ID_ME, ID_CV, ID_PT, ID_VO) && comp_node->type == NodeType::GEOMETRY) || (id_type == ID_CF && comp_node->type == NodeType::CACHE)) { rel_flag &= ~RELATION_FLAG_NO_FLUSH; } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index d699c799e40..df315176a92 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -216,8 +216,6 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { Object *object, Collection *collection); virtual void build_object(Object *object); - virtual void build_object_proxy_from(Object *object); - virtual void build_object_proxy_group(Object *object); virtual void build_object_from_layer_relations(Object *object); virtual void build_object_data(Object *object); virtual void build_object_data_camera(Object *object); @@ -273,7 +271,6 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { const bPoseChannel *rootchan, const RootPChanMap *root_map); virtual void build_rig(Object *object); - virtual void build_proxy_rig(Object *object); virtual void build_shapekeys(Key *key); virtual void build_armature(bArmature *armature); virtual void build_armature_bones(ListBase *bones); 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 856e37ee473..377ef9fc357 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -471,65 +471,4 @@ void DepsgraphRelationBuilder::build_rig(Object *object) } } -void DepsgraphRelationBuilder::build_proxy_rig(Object *object) -{ - bArmature *armature = (bArmature *)object->data; - Object *proxy_from = object->proxy_from; - build_armature(armature); - OperationKey pose_init_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_INIT); - OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE); - OperationKey pose_cleanup_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_CLEANUP); - LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - build_idproperties(pchan->prop); - OperationKey bone_local_key( - &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL); - OperationKey bone_ready_key( - &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_READY); - OperationKey bone_done_key(&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_DONE); - OperationKey from_bone_done_key( - &proxy_from->id, NodeType::BONE, pchan->name, OperationCode::BONE_DONE); - add_relation(pose_init_key, bone_local_key, "Pose Init -> Bone Local"); - add_relation(bone_local_key, bone_ready_key, "Local -> Ready"); - add_relation(bone_ready_key, bone_done_key, "Ready -> Done"); - add_relation(bone_done_key, pose_cleanup_key, "Bone Done -> Pose Cleanup"); - add_relation(bone_done_key, pose_done_key, "Bone Done -> Pose Done", RELATION_FLAG_GODMODE); - /* Make sure bone in the proxy is not done before its FROM is done. */ - if (check_pchan_has_bbone(object, pchan)) { - OperationKey from_bone_segments_key( - &proxy_from->id, NodeType::BONE, pchan->name, OperationCode::BONE_SEGMENTS); - add_relation(from_bone_segments_key, - bone_done_key, - "Bone Segments -> Bone Done", - RELATION_FLAG_GODMODE); - } - else { - add_relation(from_bone_done_key, bone_done_key, "Bone Done -> Bone Done"); - } - - /* Parent relation: even though the proxy bone itself doesn't need - * the parent bone, some users expect the parent to be ready if the - * bone itself is (e.g. for computing the local space matrix). - */ - if (pchan->parent != nullptr) { - OperationKey parent_key( - &object->id, NodeType::BONE, pchan->parent->name, OperationCode::BONE_DONE); - add_relation(parent_key, bone_done_key, "Parent Bone -> Child Bone"); - } - - if (pchan->prop != nullptr) { - OperationKey bone_parameters( - &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, pchan->name); - OperationKey from_bone_parameters( - &proxy_from->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, pchan->name); - add_relation(from_bone_parameters, bone_parameters, "Proxy Bone Parameters"); - } - - /* Custom shape. */ - if (pchan->custom != nullptr) { - build_object(pchan->custom); - add_visibility_relation(&pchan->custom->id, &armature->id); - } - } -} - } // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc index 9e62432ea54..b77b935b9f6 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc @@ -63,17 +63,6 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder { return DepsgraphNodeBuilder::need_pull_base_into_graph(base); } - void build_object_proxy_group(Object *object, bool is_visible) override - { - if (object->proxy_group == nullptr) { - return; - } - if (!filter_.contains(&object->proxy_group->id)) { - return; - } - DepsgraphNodeBuilder::build_object_proxy_group(object, is_visible); - } - protected: DepsgraphFromIDsFilter filter_; }; @@ -96,17 +85,6 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder { return DepsgraphRelationBuilder::need_pull_base_into_graph(base); } - void build_object_proxy_group(Object *object) override - { - if (object->proxy_group == nullptr) { - return; - } - if (!filter_.contains(&object->proxy_group->id)) { - return; - } - DepsgraphRelationBuilder::build_object_proxy_group(object); - } - protected: DepsgraphFromIDsFilter filter_; }; diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h index 79fcfc52446..8c89bbf1660 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h @@ -36,10 +36,7 @@ namespace deg { * visibility and other flags assigned to the objects. * All other bases (the ones which points to object which is outside of the set of IDs) are * completely ignored. - * - * - Proxy groups pointing to objects which are outside of the IDs set are also ignored. - * This way we avoid high-poly character body pulled into the dependency graph when it's coming - * from a library into an animation file and the dependency graph constructed for a proxy rig. */ + */ class FromIDsBuilderPipeline : public AbstractBuilderPipeline { public: diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc index 36120ae76d1..ef82584d671 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc @@ -99,7 +99,6 @@ static const int deg_debug_node_type_color_map[][2] = { /* Outer Types */ {NodeType::PARAMETERS, 2}, - {NodeType::PROXY, 3}, {NodeType::ANIMATION, 4}, {NodeType::TRANSFORM, 5}, {NodeType::GEOMETRY, 6}, @@ -404,7 +403,6 @@ static void deg_debug_graphviz_node(DotExportContext &ctx, case NodeType::PARAMETERS: case NodeType::ANIMATION: case NodeType::TRANSFORM: - case NodeType::PROXY: case NodeType::GEOMETRY: case NodeType::SEQUENCER: case NodeType::EVAL_POSE: diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index 7952f27507f..ae94fbcfdb8 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -72,6 +72,14 @@ void DEG_evaluate_on_refresh(Depsgraph *graph) deg_graph->frame = frame; deg_graph->ctime = ctime; } + else if (scene->id.recalc & ID_RECALC_FRAME_CHANGE) { + /* Comparing depsgraph & scene frame fails in the case of undo, + * since the undo state is stored before updates from the frame change have been applied. + * In this case reading back the undo state will behave as if no updates on frame change + * is needed as the #Depsgraph.ctime & frame will match the values in the input scene. + * Use #ID_RECALC_FRAME_CHANGE to detect that recalculation is necessary. see: T66913. */ + deg_graph->tag_time_source(); + } deg_flush_updates_and_refresh(deg_graph); } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 4bc9e0d2d14..b9ec01d729c 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -87,7 +87,7 @@ void depsgraph_geometry_tag_to_component(const ID *id, NodeType *component_type) bool is_selectable_data_id_type(const ID_Type id_type) { - return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD, ID_HA, ID_PT, ID_VO); + return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD, ID_CV, ID_PT, ID_VO); } void depsgraph_select_tag_to_component_opcode(const ID *id, @@ -211,7 +211,7 @@ void depsgraph_tag_to_component_opcode(const ID *id, case ID_RECALC_SEQUENCER_STRIPS: *component_type = NodeType::SEQUENCER; break; - case ID_RECALC_AUDIO_SEEK: + case ID_RECALC_FRAME_CHANGE: case ID_RECALC_AUDIO_FPS: case ID_RECALC_AUDIO_VOLUME: case ID_RECALC_AUDIO_MUTE: @@ -284,6 +284,7 @@ void depsgraph_tag_component(Depsgraph *graph, * here. */ if (component_node == nullptr) { if (component_type == NodeType::ANIMATION) { + id_node->is_cow_explicitly_tagged = true; depsgraph_id_tag_copy_on_write(graph, id_node, update_source); } return; @@ -301,6 +302,9 @@ void depsgraph_tag_component(Depsgraph *graph, if (component_node->need_tag_cow_before_update()) { depsgraph_id_tag_copy_on_write(graph, id_node, update_source); } + if (component_type == NodeType::COPY_ON_WRITE) { + id_node->is_cow_explicitly_tagged = true; + } } /* This is a tag compatibility with legacy code. @@ -522,12 +526,6 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph) * this. */ for (deg::IDNode *id_node : graph->id_nodes) { const ID_Type id_type = GS(id_node->id_orig->name); - if (id_type == ID_OB) { - Object *object_orig = reinterpret_cast<Object *>(id_node->id_orig); - if (object_orig->proxy != nullptr) { - object_orig->proxy->proxy_from = object_orig; - } - } if (!id_node->visible_components_mask) { /* ID has no components which affects anything visible. @@ -593,7 +591,7 @@ NodeType geometry_tag_to_component(const ID *id) case OB_LATTICE: case OB_MBALL: case OB_GPENCIL: - case OB_HAIR: + case OB_CURVES: case OB_POINTCLOUD: case OB_VOLUME: return NodeType::GEOMETRY; @@ -607,7 +605,7 @@ NodeType geometry_tag_to_component(const ID *id) case ID_CU: case ID_LT: case ID_MB: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: case ID_GR: @@ -733,8 +731,8 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "EDITORS"; case ID_RECALC_SEQUENCER_STRIPS: return "SEQUENCER_STRIPS"; - case ID_RECALC_AUDIO_SEEK: - return "AUDIO_SEEK"; + case ID_RECALC_FRAME_CHANGE: + return "FRAME_CHANGE"; case ID_RECALC_AUDIO_FPS: return "AUDIO_FPS"; case ID_RECALC_AUDIO_VOLUME: @@ -888,6 +886,7 @@ void DEG_ids_clear_recalc(Depsgraph *depsgraph, const bool backup) * correctly when there are multiple depsgraph with others still using * the recalc flag. */ id_node->is_user_modified = false; + id_node->is_cow_explicitly_tagged = false; deg_graph_clear_id_recalc_flags(id_node->id_cow); if (deg_graph->is_active) { deg_graph_clear_id_recalc_flags(id_node->id_orig); 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 b1204c9366c..b64f94568f6 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 @@ -646,8 +646,8 @@ void set_particle_system_modifiers_loaded(Object *object_cow) void reset_particle_system_edit_eval(const Depsgraph *depsgraph, Object *object_cow) { - /* Inactive (and render) dependency graphs are living in own little bubble, should not care about - * edit mode at all. */ + /* Inactive (and render) dependency graphs are living in their own little bubble, should not care + * about edit mode at all. */ if (!DEG_is_active(reinterpret_cast<const ::Depsgraph *>(depsgraph))) { return; } @@ -674,12 +674,6 @@ void update_pose_orig_pointers(const bPose *pose_orig, bPose *pose_cow) update_list_orig_pointers(&pose_orig->chanbase, &pose_cow->chanbase, &bPoseChannel::orig_pchan); } -void update_modifiers_orig_pointers(const Object *object_orig, Object *object_cow) -{ - update_list_orig_pointers( - &object_orig->modifiers, &object_cow->modifiers, &ModifierData::orig_modifier_data); -} - void update_nla_strips_orig_pointers(const ListBase *strips_orig, ListBase *strips_cow) { NlaStrip *strip_orig = reinterpret_cast<NlaStrip *>(strips_orig->first); @@ -714,25 +708,6 @@ void update_animation_data_after_copy(const ID *id_orig, ID *id_cow) update_nla_tracks_orig_pointers(&anim_data_orig->nla_tracks, &anim_data_cow->nla_tracks); } -/* Some builders (like motion path one) will ignore proxies from being built. This code makes it so - * proxy and proxy_group pointers never point to an original objects, preventing evaluation code - * from assign evaluated pointer to an original proxy->proxy_from. */ -void update_proxy_pointers_after_copy(const Depsgraph *depsgraph, - const Object *object_orig, - Object *object_cow) -{ - if (object_cow->proxy != nullptr) { - if (!deg_check_id_in_depsgraph(depsgraph, &object_orig->proxy->id)) { - object_cow->proxy = nullptr; - } - } - if (object_cow->proxy_group != nullptr) { - if (!deg_check_id_in_depsgraph(depsgraph, &object_orig->proxy_group->id)) { - object_cow->proxy_group = nullptr; - } - } -} - /* Do some special treatment of data transfer from original ID to its * CoW complementary part. * @@ -766,8 +741,6 @@ void update_id_after_copy(const Depsgraph *depsgraph, BKE_gpencil_update_orig_pointers(object_orig, object_cow); } update_particles_after_copy(depsgraph, object_orig, object_cow); - update_modifiers_orig_pointers(object_orig, object_cow); - update_proxy_pointers_after_copy(depsgraph, object_orig, object_cow); break; } case ID_SCE: { @@ -898,6 +871,29 @@ ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode if (!deg_copy_on_write_is_needed(id_orig)) { return id_cow; } + + /* When updating object data in edit-mode, don't request COW update since this will duplicate + * all object data which is unnecessary when the edit-mode data is used for calculating + * modifiers. + * + * TODO: Investigate modes besides edit-mode. */ + if (check_datablock_expanded(id_cow) && !id_node->is_cow_explicitly_tagged) { + const ID_Type id_type = GS(id_orig->name); + if (OB_DATA_SUPPORT_EDITMODE(id_type) && BKE_object_data_is_in_editmode(id_orig)) { + /* Make sure pointers in the edit mode data are updated in the copy. + * This allows depsgraph to pick up changes made in another context after it has been + * evaluated. Consider the following scenario: + * + * - ObjectA in SceneA is using Mesh. + * - ObjectB in SceneB is using Mesh (same exact datablock). + * - Depsgraph of SceneA is evaluated. + * - Depsgraph of SceneB is evaluated. + * - User enters edit mode of ObjectA in SceneA. */ + update_edit_mode_pointers(depsgraph, id_orig, id_cow); + return id_cow; + } + } + RuntimeBackup backup(depsgraph); backup.init_from_id(id_cow); deg_free_copy_on_write_datablock(id_cow); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc index 1081528ece1..10507948246 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc @@ -71,7 +71,6 @@ void ObjectRuntimeBackup::backup_modifier_runtime_data(Object *object) const SessionUUID &session_uuid = modifier_data->session_uuid; BLI_assert(BLI_session_uuid_is_generated(&session_uuid)); - BLI_assert(modifier_data->orig_modifier_data != nullptr); modifier_runtime_data.add(session_uuid, ModifierDataBackup(modifier_data)); modifier_data->runtime = nullptr; } @@ -128,7 +127,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) } } } - else if (ELEM(object->type, OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) { + else if (ELEM(object->type, OB_CURVES, OB_POINTCLOUD, OB_VOLUME)) { if (object->id.recalc & ID_RECALC_GEOMETRY) { /* Free evaluated caches. */ object->data = data_orig; @@ -150,8 +149,9 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) void ObjectRuntimeBackup::restore_modifier_runtime_data(Object *object) { LISTBASE_FOREACH (ModifierData *, modifier_data, &object->modifiers) { - BLI_assert(modifier_data->orig_modifier_data != nullptr); const SessionUUID &session_uuid = modifier_data->session_uuid; + BLI_assert(BLI_session_uuid_is_generated(&session_uuid)); + optional<ModifierDataBackup> backup = modifier_runtime_data.pop_try(session_uuid); if (backup.has_value()) { modifier_data->runtime = backup->runtime; diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc index 075bfd35ec1..1eb76d76c22 100644 --- a/source/blender/depsgraph/intern/node/deg_node.cc +++ b/source/blender/depsgraph/intern/node/deg_node.cc @@ -67,8 +67,6 @@ const char *nodeTypeAsString(NodeType type) /* **** Outer Types **** */ case NodeType::PARAMETERS: return "PARAMETERS"; - case NodeType::PROXY: - return "PROXY"; case NodeType::ANIMATION: return "ANIMATION"; case NodeType::TRANSFORM: @@ -174,7 +172,6 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type) case NodeType::BONE: case NodeType::SHADING: case NodeType::CACHE: - case NodeType::PROXY: case NodeType::SIMULATION: case NodeType::NTREE_OUTPUT: return DEG_SCENE_COMP_PARAMETERS; @@ -194,8 +191,6 @@ NodeType nodeTypeFromObjectComponent(eDepsObjectComponentType component_type) return NodeType::UNDEFINED; case DEG_OB_COMP_PARAMETERS: return NodeType::PARAMETERS; - case DEG_OB_COMP_PROXY: - return NodeType::PROXY; case DEG_OB_COMP_ANIMATION: return NodeType::ANIMATION; case DEG_OB_COMP_TRANSFORM: @@ -219,8 +214,6 @@ eDepsObjectComponentType nodeTypeToObjectComponent(NodeType type) switch (type) { case NodeType::PARAMETERS: return DEG_OB_COMP_PARAMETERS; - case NodeType::PROXY: - return DEG_OB_COMP_PROXY; case NodeType::ANIMATION: return DEG_OB_COMP_ANIMATION; case NodeType::TRANSFORM: diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h index 25bbb9a7237..72d3e375bcd 100644 --- a/source/blender/depsgraph/intern/node/deg_node.h +++ b/source/blender/depsgraph/intern/node/deg_node.h @@ -77,8 +77,6 @@ enum class NodeType { /* Parameters Component - Default when nothing else fits * (i.e. just SDNA property setting). */ PARAMETERS, - /* Generic "Proxy-Inherit" Component. */ - PROXY, /* Animation Component */ ANIMATION, /* Transform Component (Parenting/Constraints) */ diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index 4c430904e44..e14f758c739 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -339,7 +339,6 @@ DEG_COMPONENT_NODE_DEFINE(Particles, PARTICLE_SYSTEM, ID_RECALC_GEOMETRY); DEG_COMPONENT_NODE_DEFINE(ParticleSettings, PARTICLE_SETTINGS, 0); DEG_COMPONENT_NODE_DEFINE(PointCache, POINT_CACHE, 0); DEG_COMPONENT_NODE_DEFINE(Pose, EVAL_POSE, ID_RECALC_GEOMETRY); -DEG_COMPONENT_NODE_DEFINE(Proxy, PROXY, ID_RECALC_GEOMETRY); DEG_COMPONENT_NODE_DEFINE(Sequencer, SEQUENCER, 0); DEG_COMPONENT_NODE_DEFINE(Shading, SHADING, ID_RECALC_SHADING); DEG_COMPONENT_NODE_DEFINE(Transform, TRANSFORM, ID_RECALC_TRANSFORM); @@ -373,7 +372,6 @@ void deg_register_component_depsnodes() register_node_typeinfo(&DNTI_PARTICLE_SETTINGS); register_node_typeinfo(&DNTI_POINT_CACHE); register_node_typeinfo(&DNTI_IMAGE_ANIMATION); - register_node_typeinfo(&DNTI_PROXY); register_node_typeinfo(&DNTI_EVAL_POSE); register_node_typeinfo(&DNTI_SEQUENCER); register_node_typeinfo(&DNTI_SHADING); diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index 8fd28dfc497..d45d4642937 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -163,23 +163,6 @@ struct ComponentNode : public Node { DEG_COMPONENT_NODE_DECLARE; \ } -/* When updating object data in edit-mode, don't request COW update since this will duplicate - * all object data which is unnecessary when the edit-mode data is used for calculating modifiers. - * - * TODO: Investigate modes besides edit-mode. */ -#define DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_OBDATA_IN_EDIT_MODE(name) \ - struct name##ComponentNode : public ComponentNode { \ - DEG_COMPONENT_NODE_DECLARE; \ - virtual bool need_tag_cow_before_update() override \ - { \ - if (OB_DATA_SUPPORT_EDITMODE(owner->id_type) && \ - BKE_object_data_is_in_editmode(owner->id_orig)) { \ - return false; \ - } \ - return true; \ - } \ - } - #define DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(name) \ struct name##ComponentNode : public ComponentNode { \ DEG_COMPONENT_NODE_DECLARE; \ @@ -202,14 +185,13 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(Animation); DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(BatchCache); DEG_COMPONENT_NODE_DECLARE_GENERIC(Cache); DEG_COMPONENT_NODE_DECLARE_GENERIC(CopyOnWrite); -DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_OBDATA_IN_EDIT_MODE(Geometry); +DEG_COMPONENT_NODE_DECLARE_GENERIC(Geometry); DEG_COMPONENT_NODE_DECLARE_GENERIC(ImageAnimation); DEG_COMPONENT_NODE_DECLARE_GENERIC(LayerCollections); DEG_COMPONENT_NODE_DECLARE_GENERIC(Particles); DEG_COMPONENT_NODE_DECLARE_GENERIC(ParticleSettings); DEG_COMPONENT_NODE_DECLARE_GENERIC(Pose); DEG_COMPONENT_NODE_DECLARE_GENERIC(PointCache); -DEG_COMPONENT_NODE_DECLARE_GENERIC(Proxy); DEG_COMPONENT_NODE_DECLARE_GENERIC(Sequencer); DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(Shading); DEG_COMPONENT_NODE_DECLARE_GENERIC(ShadingParameters); diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h index 257e42b8e67..f18c44b6332 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.h +++ b/source/blender/depsgraph/intern/node/deg_node_id.h @@ -121,6 +121,9 @@ struct IDNode : public Node { /* Accumulated flag from operation. Is initialized and used during updates flush. */ bool is_user_modified; + /* Copy-on-Write component has been explicitly tagged for update. */ + bool is_cow_explicitly_tagged; + /* Accumulate recalc flags from multiple update passes. */ int id_cow_recalc_backup; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index f0e2e2bafd1..17feb39a072 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -45,6 +45,7 @@ set(INC ../../../intern/glew-mx ../../../intern/guardedalloc ../../../intern/opensubdiv + ../../../intern/clog # dna_type_offsets.h ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern @@ -85,9 +86,9 @@ set(SRC intern/mesh_extractors/extract_mesh_vbo_vcol.cc intern/mesh_extractors/extract_mesh_vbo_weights.cc intern/draw_cache_impl_curve.cc + intern/draw_cache_impl_curves.cc intern/draw_cache_impl_displist.c intern/draw_cache_impl_gpencil.c - intern/draw_cache_impl_hair.cc intern/draw_cache_impl_lattice.c intern/draw_cache_impl_mesh.c intern/draw_cache_impl_metaball.c @@ -154,7 +155,7 @@ set(SRC engines/workbench/workbench_materials.c engines/workbench/workbench_opaque.c engines/workbench/workbench_render.c - engines/workbench/workbench_shader.c + engines/workbench/workbench_shader.cc engines/workbench/workbench_shadow.c engines/workbench/workbench_transparent.c engines/workbench/workbench_volume.c @@ -227,11 +228,17 @@ set(SRC engines/eevee/eevee_lut.h engines/eevee/eevee_private.h engines/external/external_engine.h - engines/image/image_drawing_mode_image_space.hh + engines/image/image_batches.hh + engines/image/image_drawing_mode.hh engines/image/image_engine.h + engines/image/image_instance_data.hh + engines/image/image_partial_updater.hh engines/image/image_private.hh + engines/image/image_shader_params.hh engines/image/image_space_image.hh engines/image/image_space_node.hh + engines/image/image_space.hh + engines/image/image_wrappers.hh engines/workbench/workbench_engine.h engines/workbench/workbench_private.h engines/workbench/workbench_shader_shared.h @@ -342,7 +349,6 @@ set(GLSL_SRC engines/workbench/shaders/workbench_common_lib.glsl engines/workbench/shaders/workbench_composite_frag.glsl engines/workbench/shaders/workbench_curvature_lib.glsl - engines/workbench/shaders/workbench_data_lib.glsl engines/workbench/shaders/workbench_effect_cavity_frag.glsl engines/workbench/shaders/workbench_effect_dof_frag.glsl engines/workbench/shaders/workbench_effect_outline_frag.glsl @@ -357,7 +363,6 @@ set(GLSL_SRC engines/workbench/shaders/workbench_prepass_hair_vert.glsl engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl engines/workbench/shaders/workbench_prepass_vert.glsl - engines/workbench/shaders/workbench_shader_interface_lib.glsl engines/workbench/shaders/workbench_shadow_caps_geom.glsl engines/workbench/shaders/workbench_shadow_debug_frag.glsl engines/workbench/shaders/workbench_shadow_geom.glsl @@ -378,6 +383,7 @@ set(GLSL_SRC intern/shaders/common_hair_refine_comp.glsl intern/shaders/common_math_lib.glsl intern/shaders/common_math_geom_lib.glsl + intern/shaders/common_view_clipping_lib.glsl intern/shaders/common_view_lib.glsl intern/shaders/common_fxaa_lib.glsl intern/shaders/common_smaa_lib.glsl @@ -513,8 +519,10 @@ set(GLSL_SRC engines/overlay/shaders/wireframe_frag.glsl engines/overlay/shaders/xray_fade_frag.glsl - engines/image/shaders/engine_image_frag.glsl - engines/image/shaders/engine_image_vert.glsl + engines/image/shaders/image_engine_color_frag.glsl + engines/image/shaders/image_engine_color_vert.glsl + engines/image/shaders/image_engine_depth_frag.glsl + engines/image/shaders/image_engine_depth_vert.glsl ) set(GLSL_C) @@ -532,7 +540,7 @@ set(GLSL_SOURCE_CONTENT "") foreach(GLSL_FILE ${GLSL_SRC}) get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME) string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME}) - string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\"\)\n") + string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n") endforeach() set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_draw_source_list.h") diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 80207523a65..8f02e96b168 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -56,7 +56,7 @@ #include "BLI_math_bits.h" #include "BLI_rect.h" -#include "DNA_hair_types.h" +#include "DNA_curves_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_particle_types.h" @@ -248,25 +248,25 @@ static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedat return grp; } -static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - ParticleSystem *psys, - ModifierData *md, - Material *material) +static void eevee_cryptomatte_curves_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob, + ParticleSystem *psys, + ModifierData *md, + Material *material) { DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( vedata, sldata, ob, material, true); DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL); } -void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob) +void EEVEE_cryptomatte_object_curves_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob) { - BLI_assert(ob->type == OB_HAIR); - Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR); - eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material); + BLI_assert(ob->type == OB_CURVES); + Material *material = BKE_object_material_get_eval(ob, CURVES_MATERIAL_NR); + eevee_cryptomatte_curves_cache_populate(vedata, sldata, ob, NULL, NULL, material); } void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, @@ -291,7 +291,7 @@ void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, continue; } Material *material = BKE_object_material_get_eval(ob, part->omat); - eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material); + eevee_cryptomatte_curves_cache_populate(vedata, sldata, ob, psys, md, material); } } } diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index b453df284ed..64553acd228 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -177,7 +177,7 @@ static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, b if (hair) { EEVEE_HairMotionData *hair_step; /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */ - int psys_len = (ob->type != OB_HAIR) ? BLI_listbase_count(&ob->modifiers) : 1; + int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1; hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__); hair_step->psys_len = psys_len; diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c index 4748323d6a7..d0c0635a1fb 100644 --- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c @@ -251,7 +251,7 @@ int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata), effects->dof_coc_params[1] = -aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled)); - /* FIXME(fclem) This is broken for vertically fit sensor. */ + /* FIXME(@fclem): This is broken for vertically fit sensor. */ effects->dof_coc_params[1] *= viewport_size[0] / sensor_scaled; if ((scene_eval->eevee.flag & SCE_EEVEE_DOF_JITTER) != 0) { @@ -625,7 +625,7 @@ static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl, } if (txl->dof_reduced_color) { - /* TODO(fclem) In the future, we need to check if mip_count did not change. + /* TODO(@fclem): In the future, we need to check if mip_count did not change. * For now it's ok as we always define all mip level. */ if (res[0] != GPU_texture_width(txl->dof_reduced_color) || res[1] != GPU_texture_width(txl->dof_reduced_color)) { @@ -642,7 +642,8 @@ static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl, txl->dof_reduced_coc = GPU_texture_create_2d( "dof_reduced_coc", UNPACK2(res), mip_count, GPU_R16F, NULL); - /* TODO(fclem) Remove once we have immutable storage or when mips are generated on creation. */ + /* TODO(@fclem): Remove once we have immutable storage or when mips are generated on creation. + */ GPU_texture_generate_mipmap(txl->dof_reduced_color); GPU_texture_generate_mipmap(txl->dof_reduced_coc); } diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index fc9b8b0cde4..9b6b5c5f08d 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -125,7 +125,7 @@ void EEVEE_cache_populate(void *vedata, Object *ob) if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) { EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow); } - else if (ob->type == OB_HAIR) { + else if (ob->type == OB_CURVES) { EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); } else if (ob->type == OB_VOLUME) { diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index c51fc18a406..c7a8d2cc001 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -1077,8 +1077,8 @@ void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata, pinfo->intensity_fac = intensity; - /* find cell position on the virtual 3D texture */ - /* NOTE : Keep in sync with load_irradiance_cell() */ + /* Find cell position on the virtual 3D texture. */ + /* NOTE: Keep in sync with `load_irradiance_cell()`. */ #if defined(IRRADIANCE_SH_L2) int size[2] = {3, 3}; #elif defined(IRRADIANCE_HL2) diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index a027a29c813..b8586ee0f68 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -33,7 +33,7 @@ #include "BKE_paint.h" #include "BKE_particle.h" -#include "DNA_hair_types.h" +#include "DNA_curves_types.h" #include "DNA_modifier_types.h" #include "DNA_view3d_types.h" #include "DNA_world_types.h" @@ -925,7 +925,7 @@ void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, Object *ob, bool *cast_shadow) { - eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, HAIR_MATERIAL_NR, cast_shadow); + eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, CURVES_MATERIAL_NR, cast_shadow); } void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 996716d6def..883d2eff852 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -176,7 +176,7 @@ enum { VAR_MAT_MESH = (1 << 0), VAR_MAT_VOLUME = (1 << 1), VAR_MAT_HAIR = (1 << 2), - /* VAR_MAT_PROBE = (1 << 3), UNUSED */ + VAR_MAT_POINTCLOUD = (1 << 3), VAR_MAT_BLEND = (1 << 4), VAR_MAT_LOOKDEV = (1 << 5), VAR_MAT_HOLDOUT = (1 << 6), @@ -1385,9 +1385,9 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob); -void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob); +void EEVEE_cryptomatte_object_curves_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob); void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); /** * Register the render passes needed for cryptomatte diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index 9e7a67060da..2fd033b6745 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -244,10 +244,10 @@ void EEVEE_render_cache(void *vedata, EEVEE_cryptomatte_cache_populate(data, sldata, ob); } } - else if (ob->type == OB_HAIR) { + else if (ob->type == OB_CURVES) { EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); if (do_cryptomatte) { - EEVEE_cryptomatte_object_hair_cache_populate(data, sldata, ob); + EEVEE_cryptomatte_object_curves_cache_populate(data, sldata, ob); } } else if (ob->type == OB_VOLUME) { @@ -517,8 +517,7 @@ static void eevee_render_draw_background(EEVEE_Data *vedata) EEVEE_PassList *psl = vedata->psl; /* Prevent background to write to data buffers. - * NOTE : This also make sure the textures are bound - * to the right double buffer. */ + * NOTE: This also make sure the textures are bound to the right double buffer. */ GPU_framebuffer_ensure_config(&fbl->main_fb, {GPU_ATTACHMENT_LEAVE, GPU_ATTACHMENT_LEAVE, diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index adede7676d5..c4108969c99 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -887,10 +887,7 @@ struct GPUShader *EEVEE_shaders_volumes_integration_sh_get() datatoc_volumetric_geom_glsl, datatoc_volumetric_integration_frag_glsl, e_data.lib, - USE_VOLUME_OPTI ? "#extension GL_ARB_shader_image_load_store: enable\n" - "#extension GL_ARB_shading_language_420pack: enable\n" - "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : - SHADER_DEFINES); + USE_VOLUME_OPTI ? "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : SHADER_DEFINES); } return e_data.volumetric_integration_sh; } @@ -1357,6 +1354,9 @@ static char *eevee_get_defines(int options) if ((options & VAR_MAT_HAIR) != 0) { BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); } + if ((options & VAR_MAT_POINTCLOUD) != 0) { + BLI_dynstr_append(ds, "#define POINTCLOUD_SHADER\n"); + } if ((options & VAR_WORLD_PROBE) != 0) { BLI_dynstr_append(ds, "#define PROBE_CAPTURE\n"); } diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c index 48c24d138e6..81d9d560320 100644 --- a/source/blender/draw/engines/eevee/eevee_subsurface.c +++ b/source/blender/draw/engines/eevee/eevee_subsurface.c @@ -49,7 +49,7 @@ void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; if (effects->enabled_effects & EFFECT_SSS) { - /* NOTE : we need another stencil because the stencil buffer is on the same texture + /* NOTE: we need another stencil because the stencil buffer is on the same texture * as the depth buffer we are sampling from. This could be avoided if the stencil is * a separate texture but that needs OpenGL 4.4 or ARB_texture_stencil8. * OR OpenGL 4.3 / ARB_ES3_compatibility if using a render-buffer instead. */ diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl index 1061b2f91a2..1c7ef775ac2 100644 --- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl @@ -56,7 +56,7 @@ vec2 get_ao_noise(void) { vec2 noise = texelfetch_noise_tex(gl_FragCoord.xy).xy; /* Decorrelate noise from AA. */ - /* TODO(fclem) we should use a more general approach for more random number dimensions. */ + /* TODO(@fclem): we should use a more general approach for more random number dimensions. */ noise = fract(noise * 6.1803402007); return noise; } diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl index 4f9791ac95f..5bf20fe6979 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl @@ -43,7 +43,7 @@ void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in, inout ClosureOutputDiffuse cl_out) { float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L); - /* TODO(fclem) We could try to shadow lights that are shadowless with the ambient_occlusion + /* TODO(@fclem): We could try to shadow lights that are shadowless with the ambient_occlusion * factor here. */ cl_out.radiance += light.data.l_color * (light.data.l_diff * light.vis * light.contact_shadow * radiance); diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl index 00d265a48b0..584aacc9e19 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl @@ -63,7 +63,7 @@ ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in, /* The brdf split sum LUT is applied after the radiance accumulation. * Correct the LTC so that its energy is constant. */ - /* TODO(fclem) Optimize this so that only one scale factor is stored. */ + /* TODO(@fclem): Optimize this so that only one scale factor is stored. */ vec4 ltc_brdf = texture(utilTex, vec3(lut_uv, LTC_BRDF_LAYER)).barg; vec2 split_sum_brdf = ltc_brdf.zw; cl_eval.ltc_brdf_scale = (ltc_brdf.x + ltc_brdf.y) / (split_sum_brdf.x + split_sum_brdf.y); diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl index db9ae0f7034..f5c45d147e6 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl @@ -52,7 +52,7 @@ const float unit_ring_radius = 1.0 / float(gather_ring_count); const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5); const float large_kernel_radius = 0.5 + float(gather_ring_count); const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring); -/* NOTE(fclem) the bias is reducing issues with density change visible transition. */ +/* NOTE(@fclem): the bias is reducing issues with density change visible transition. */ const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius; const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1); const float coc_radius_error = 2.0; @@ -83,7 +83,7 @@ void dof_gather_init(float base_radius, #endif center_co = gl_FragCoord.xy + jitter_ofs * base_radius * unit_sample_radius; - /* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving + /* TODO(@fclem): Seems like the default lod selection is too big. Bias to avoid blocky moving * out of focus shapes. */ const float lod_bias = -2.0; lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0); @@ -111,7 +111,7 @@ void dof_gather_accumulator(float base_radius, * a ring. So we need to compensate for fast gather that does not check CoC intersection. */ base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius; } - /* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */ + /* TODO(@fclem): another seed? For now Cranly-Partterson rotation with golden ratio. */ noise.x = fract(noise.x + 0.61803398875); float lod, isect_mul; @@ -172,7 +172,7 @@ void dof_gather_accumulator(float base_radius, } #ifdef DOF_FOREGROUND_PASS /* Reduce issue with closer foreground over distant foreground. */ - /* TODO(fclem) this seems to not be completely correct as the issue remains. */ + /* TODO(@fclem): This seems to not be completely correct as the issue remains. */ float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) - sqr(float(ring) - 0.5 + coc_radius_error)) * sqr(base_radius * unit_sample_radius); diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl index e5b68637563..e288e1a55ea 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl @@ -66,7 +66,7 @@ float dof_hdr_color_weight(vec4 color) { /* From UE4. Very fast "luma" weighting. */ float luma = (color.g * 2.0) + (color.r + color.b); - /* TODO(fclem) Pass correct exposure. */ + /* TODO(@fclem): Pass correct exposure. */ const float exposure = 1.0; return 1.0 / (luma * exposure + 4.0); } @@ -92,7 +92,7 @@ vec4 dof_downsample_bilateral_coc_weights(vec4 cocs) { float chosen_coc = dof_coc_select(cocs); - const float scale = 4.0; /* TODO(fclem) revisit. */ + const float scale = 4.0; /* TODO(@fclem): revisit. */ /* NOTE: The difference between the cocs should be inside a abs() function, * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */ return saturate(1.0 - (chosen_coc - cocs) * scale); @@ -373,7 +373,7 @@ void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2], #if 0 const float mirroring_threshold = -layer_threshold - layer_offset; - /* TODO(fclem) Promote to parameter? dither with Noise? */ + /* TODO(@fclem): Promote to parameter? dither with Noise? */ const float mirroring_min_distance = 15.0; if (pair_data[0].coc < mirroring_threshold && (pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) { @@ -487,7 +487,8 @@ void dof_gather_accumulate_sample_ring(DofGatherData ring_data, } } -/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for slightfocus gather. */ +/* FIXME(@fclem): Seems to be wrong since it needs `ringcount + 1` as input for slightfocus gather. + */ int dof_gather_total_sample_count(const int ring_count, const int ring_density) { return (ring_count * ring_count - ring_count) * ring_density + 1; diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl index f349806d37e..59564890d7e 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl @@ -25,7 +25,8 @@ flat out float spritesize; /* Load 4 Circle of confusion values. texel_co is centered around the 4 taps. */ vec4 fetch_cocs(vec2 texel_co) { - /* TODO(fclem) The textureGather(sampler, co, comp) variant isn't here on some implementations. + /* TODO(@fclem): The `textureGather(sampler, co, comp)` variant isn't here on some + * implementations. */ #if 0 // GPU_ARB_texture_gather vec2 uvs = texel_co / vec2(textureSize(cocBuffer, 0)); diff --git a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl index 70f1e9f1e66..85f8a12aa88 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl @@ -45,7 +45,7 @@ vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float dept } } -/* TODO(fclem) port to a common place for other effects to use. */ +/* TODO(@fclem): port to a common place for other effects to use. */ bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg) { vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y))); diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl index 2f1efd588f7..f4ff28eaee4 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl @@ -31,7 +31,7 @@ void main() { vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); /* Decorrelate from AA. */ - /* TODO(fclem) we should use a more general approach for more random number dimensions. */ + /* TODO(@fclem): we should use a more general approach for more random number dimensions. */ vec2 random_px = floor(fract(rand.xy * 2.2074408460575947536) * 1.99999) - 0.5; rand.xy = fract(rand.xy * 3.2471795724474602596); diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl index ee48c468630..ba90f5ae531 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl @@ -212,7 +212,7 @@ vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float dept } } -/* TODO(fclem) port to a common place for other effects to use. */ +/* TODO(@fclem): port to a common place for other effects to use. */ bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg) { vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y))); diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index 0e342938396..cbfa9737a84 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -26,6 +26,9 @@ void main() worldNormal = cross(hairTangent, binor); vec3 world_pos = pos; +#elif defined(POINTCLOUD_SHADER) + pointcloud_get_pos_and_radius(pointPosition, pointRadius); + pointID = gl_VertexID; #else vec3 world_pos = point_object_to_world(pos); #endif diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 7d016d57c46..d7fc5e0b52a 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -42,3 +42,13 @@ IN_OUT ShaderHairInterface flat int hairStrandID; }; #endif + +#ifdef POINTCLOUD_SHADER +IN_OUT ShaderPointCloudInterface +{ + /* world space */ + float pointRadius; + float pointPosition; + flat int pointID; +}; +#endif diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 0ad1393dd70..51e9eda6cc2 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -31,6 +31,9 @@ void main() hairThickTime); worldNormal = cross(hairTangent, binor); vec3 world_pos = pos; +#elif defined(POINTCLOUD_SHADER) + pointcloud_get_pos_and_radius(pointPosition, pointRadius); + pointID = gl_VertexID; #else vec3 world_pos = point_object_to_world(pos); #endif diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 8aba1090b58..5a79dfc2663 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -472,7 +472,7 @@ GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void) GPENCIL_ViewLayerData **vldata = (GPENCIL_ViewLayerData **)DRW_view_layer_engine_data_ensure( &draw_engine_gpencil_type, gpencil_view_layer_data_free); - /* NOTE(fclem) Putting this stuff in viewlayer means it is shared by all viewports. + /* NOTE(&fclem): Putting this stuff in viewlayer means it is shared by all viewports. * For now it is ok, but in the future, it could become a problem if we implement * the caching system. */ if (*vldata == NULL) { diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 9bc340a2786..dfa0d350485 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -788,7 +788,7 @@ static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL const float clear_col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; float clear_depth = ob->is_drawmode3d ? 1.0f : 0.0f; bool inverted = false; - /* OPTI(fclem) we could optimize by only clearing if the new mask_bits does not contain all + /* OPTI(@fclem): we could optimize by only clearing if the new mask_bits does not contain all * the masks already rendered in the buffer, and drawing only the layers not already drawn. */ bool cleared = false; diff --git a/source/blender/draw/engines/image/image_batches.hh b/source/blender/draw/engines/image/image_batches.hh new file mode 100644 index 00000000000..5f82504197a --- /dev/null +++ b/source/blender/draw/engines/image/image_batches.hh @@ -0,0 +1,106 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "image_texture_info.hh" + +/** \brief Create GPUBatch for a IMAGE_ScreenSpaceTextureInfo. */ +class BatchUpdater { + TextureInfo &info; + + GPUVertFormat format = {0}; + int pos_id; + int uv_id; + + public: + BatchUpdater(TextureInfo &info) : info(info) + { + } + + void update_batch() + { + ensure_clear_batch(); + ensure_format(); + init_batch(); + } + + void discard_batch() + { + GPU_BATCH_DISCARD_SAFE(info.batch); + } + + private: + void ensure_clear_batch() + { + GPU_BATCH_CLEAR_SAFE(info.batch); + if (info.batch == nullptr) { + info.batch = GPU_batch_calloc(); + } + } + + void init_batch() + { + GPUVertBuf *vbo = create_vbo(); + GPU_batch_init_ex(info.batch, GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO); + } + + GPUVertBuf *create_vbo() + { + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, 4); + float pos[4][2]; + fill_tri_fan_from_rctf(pos, info.clipping_bounds); + float uv[4][2]; + fill_tri_fan_from_rctf(uv, info.clipping_uv_bounds); + + for (int i = 0; i < 4; i++) { + GPU_vertbuf_attr_set(vbo, pos_id, i, pos[i]); + GPU_vertbuf_attr_set(vbo, uv_id, i, uv[i]); + } + + return vbo; + } + + static void fill_tri_fan_from_rctf(float result[4][2], rctf &rect) + { + result[0][0] = rect.xmin; + result[0][1] = rect.ymin; + result[1][0] = rect.xmax; + result[1][1] = rect.ymin; + result[2][0] = rect.xmax; + result[2][1] = rect.ymax; + result[3][0] = rect.xmin; + result[3][1] = rect.ymax; + } + + void ensure_format() + { + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + pos_id = GPU_vertformat_attr_id_get(&format, "pos"); + uv_id = GPU_vertformat_attr_id_get(&format, "uv"); + } + } +}; diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh b/source/blender/draw/engines/image/image_drawing_mode.hh new file mode 100644 index 00000000000..52d57ea2ba5 --- /dev/null +++ b/source/blender/draw/engines/image/image_drawing_mode.hh @@ -0,0 +1,518 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "BKE_image_partial_update.hh" + +#include "IMB_imbuf_types.h" + +#include "BLI_float4x4.hh" +#include "BLI_math_vec_types.hh" + +#include "image_batches.hh" +#include "image_private.hh" +#include "image_wrappers.hh" + +namespace blender::draw::image_engine { + +constexpr float EPSILON_UV_BOUNDS = 0.00001f; + +/** + * \brief Screen space method using a single texture spawning the whole screen. + */ +struct OneTextureMethod { + IMAGE_InstanceData *instance_data; + + OneTextureMethod(IMAGE_InstanceData *instance_data) : instance_data(instance_data) + { + } + + /** \brief Update the texture slot uv and screen space bounds. */ + void update_screen_space_bounds(const ARegion *region) + { + /* Create a single texture that covers the visible screen space. */ + BLI_rctf_init( + &instance_data->texture_infos[0].clipping_bounds, 0, region->winx, 0, region->winy); + instance_data->texture_infos[0].visible = true; + + /* Mark the other textures as invalid. */ + for (int i = 1; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + BLI_rctf_init_minmax(&instance_data->texture_infos[i].clipping_bounds); + instance_data->texture_infos[i].visible = false; + } + } + + void update_region_uv_bounds(const ARegion *region) + { + TextureInfo &info = instance_data->texture_infos[0]; + if (!BLI_rctf_compare(&info.region_uv_bounds, ®ion->v2d.cur, EPSILON_UV_BOUNDS)) { + info.region_uv_bounds = region->v2d.cur; + info.dirty = true; + } + + /* Mark the other textures as invalid. */ + for (int i = 1; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + BLI_rctf_init_minmax(&instance_data->texture_infos[i].clipping_bounds); + } + } + + void update_screen_uv_bounds() + { + for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + update_screen_uv_bounds(instance_data->texture_infos[0]); + } + } + + void update_screen_uv_bounds(TextureInfo &info) + { + /* Although this works, computing an inverted matrix adds some precision issues and leads to + * tearing artifacts. This should be modified to use the scaling and transformation from the + * not inverted matrix.*/ + float4x4 mat(instance_data->ss_to_texture); + float4x4 mat_inv = mat.inverted(); + float3 min_uv = mat_inv * float3(0.0f, 0.0f, 0.0f); + float3 max_uv = mat_inv * float3(1.0f, 1.0f, 0.0f); + BLI_rctf_init(&info.clipping_uv_bounds, min_uv[0], max_uv[0], min_uv[1], max_uv[1]); + } +}; + +using namespace blender::bke::image::partial_update; + +template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractDrawingMode { + private: + DRWPass *create_image_pass() const + { + DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | + DRW_STATE_BLEND_ALPHA_PREMUL); + return DRW_pass_create("Image", state); + } + + DRWPass *create_depth_pass() const + { + /* Depth is needed for background overlay rendering. Near depth is used for + * transparency checker and Far depth is used for indicating the image size. */ + DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL); + return DRW_pass_create("Depth", state); + } + + void add_shgroups(const IMAGE_InstanceData *instance_data) const + { + const ShaderParameters &sh_params = instance_data->sh_params; + GPUShader *shader = IMAGE_shader_image_get(); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_data->passes.image_pass); + DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", sh_params.far_near); + DRW_shgroup_uniform_vec4_copy(shgrp, "shuffle", sh_params.shuffle); + DRW_shgroup_uniform_int_copy(shgrp, "drawFlags", sh_params.flags); + DRW_shgroup_uniform_bool_copy(shgrp, "imgPremultiplied", sh_params.use_premul_alpha); + DRW_shgroup_uniform_texture(shgrp, "depth_texture", dtxl->depth); + float image_mat[4][4]; + unit_m4(image_mat); + for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + const TextureInfo &info = instance_data->texture_infos[i]; + if (!info.visible) { + continue; + } + + DRWShadingGroup *shgrp_sub = DRW_shgroup_create_sub(shgrp); + DRW_shgroup_uniform_texture_ex(shgrp_sub, "imageTexture", info.texture, GPU_SAMPLER_DEFAULT); + DRW_shgroup_call_obmat(shgrp_sub, info.batch, image_mat); + } + } + + /** + * \brief add depth drawing calls. + * + * The depth is used to identify if the tile exist or transparent. + */ + void add_depth_shgroups(IMAGE_InstanceData &instance_data, + Image *image, + ImageUser *image_user) const + { + GPUShader *shader = IMAGE_shader_depth_get(); + DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_data.passes.depth_pass); + + float image_mat[4][4]; + unit_m4(image_mat); + + ImageUser tile_user = {0}; + if (image_user) { + tile_user = *image_user; + } + + for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + const TextureInfo &info = instance_data.texture_infos[i]; + if (!info.visible) { + continue; + } + + LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) { + const ImageTileWrapper image_tile(image_tile_ptr); + const int tile_x = image_tile.get_tile_x_offset(); + const int tile_y = image_tile.get_tile_y_offset(); + tile_user.tile = image_tile.get_tile_number(); + + /* NOTE: `BKE_image_has_ibuf` doesn't work as it fails for render results. That could be a + * bug or a feature. For now we just acquire to determine if there is a texture. */ + void *lock; + ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock); + if (tile_buffer == nullptr) { + continue; + } + BKE_image_release_ibuf(image, tile_buffer, lock); + + DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp); + float4 min_max_uv(tile_x, tile_y, tile_x + 1, tile_y + 1); + DRW_shgroup_uniform_vec4_copy(shsub, "min_max_uv", min_max_uv); + DRW_shgroup_call_obmat(shsub, info.batch, image_mat); + } + } + } + + /** + * \brief Update GPUTextures for drawing the image. + * + * GPUTextures that are marked dirty are rebuild. GPUTextures that aren't marked dirty are + * updated with changed region of the image. + */ + void update_textures(IMAGE_InstanceData &instance_data, + Image *image, + ImageUser *image_user) const + { + PartialUpdateChecker<ImageTileData> checker( + image, image_user, instance_data.partial_update.user); + PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes(); + + switch (changes.get_result_code()) { + case ePartialUpdateCollectResult::FullUpdateNeeded: + instance_data.mark_all_texture_slots_dirty(); + break; + case ePartialUpdateCollectResult::NoChangesDetected: + break; + case ePartialUpdateCollectResult::PartialChangesDetected: + /* Partial update when wrap repeat is enabled is not supported. */ + if (instance_data.flags.do_tile_drawing) { + instance_data.mark_all_texture_slots_dirty(); + } + else { + do_partial_update(changes, instance_data); + } + break; + } + do_full_update_for_dirty_textures(instance_data, image_user); + } + + void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator, + IMAGE_InstanceData &instance_data) const + { + while (iterator.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) { + /* Quick exit when tile_buffer isn't available. */ + if (iterator.tile_data.tile_buffer == nullptr) { + continue; + } + ensure_float_buffer(*iterator.tile_data.tile_buffer); + const float tile_width = static_cast<float>(iterator.tile_data.tile_buffer->x); + const float tile_height = static_cast<float>(iterator.tile_data.tile_buffer->y); + + for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + const TextureInfo &info = instance_data.texture_infos[i]; + /* Dirty images will receive a full update. No need to do a partial one now. */ + if (info.dirty) { + continue; + } + if (!info.visible) { + continue; + } + GPUTexture *texture = info.texture; + const float texture_width = GPU_texture_width(texture); + const float texture_height = GPU_texture_height(texture); + /* TODO: early bound check. */ + ImageTileWrapper tile_accessor(iterator.tile_data.tile); + float tile_offset_x = static_cast<float>(tile_accessor.get_tile_x_offset()); + float tile_offset_y = static_cast<float>(tile_accessor.get_tile_y_offset()); + rcti *changed_region_in_texel_space = &iterator.changed_region.region; + rctf changed_region_in_uv_space; + BLI_rctf_init(&changed_region_in_uv_space, + static_cast<float>(changed_region_in_texel_space->xmin) / + static_cast<float>(iterator.tile_data.tile_buffer->x) + + tile_offset_x, + static_cast<float>(changed_region_in_texel_space->xmax) / + static_cast<float>(iterator.tile_data.tile_buffer->x) + + tile_offset_x, + static_cast<float>(changed_region_in_texel_space->ymin) / + static_cast<float>(iterator.tile_data.tile_buffer->y) + + tile_offset_y, + static_cast<float>(changed_region_in_texel_space->ymax) / + static_cast<float>(iterator.tile_data.tile_buffer->y) + + tile_offset_y); + rctf changed_overlapping_region_in_uv_space; + const bool region_overlap = BLI_rctf_isect(&info.region_uv_bounds, + &changed_region_in_uv_space, + &changed_overlapping_region_in_uv_space); + if (!region_overlap) { + continue; + } + /* Convert the overlapping region to texel space and to ss_pixel space... + * TODO: first convert to ss_pixel space as integer based. and from there go back to texel + * space. But perhaps this isn't needed and we could use an extraction offset somehow. */ + rcti gpu_texture_region_to_update; + BLI_rcti_init( + &gpu_texture_region_to_update, + floor((changed_overlapping_region_in_uv_space.xmin - info.region_uv_bounds.xmin) * + texture_width / BLI_rctf_size_x(&info.region_uv_bounds)), + floor((changed_overlapping_region_in_uv_space.xmax - info.region_uv_bounds.xmin) * + texture_width / BLI_rctf_size_x(&info.region_uv_bounds)), + ceil((changed_overlapping_region_in_uv_space.ymin - info.region_uv_bounds.ymin) * + texture_height / BLI_rctf_size_y(&info.region_uv_bounds)), + ceil((changed_overlapping_region_in_uv_space.ymax - info.region_uv_bounds.ymin) * + texture_height / BLI_rctf_size_y(&info.region_uv_bounds))); + + rcti tile_region_to_extract; + BLI_rcti_init( + &tile_region_to_extract, + floor((changed_overlapping_region_in_uv_space.xmin - tile_offset_x) * tile_width), + floor((changed_overlapping_region_in_uv_space.xmax - tile_offset_x) * tile_width), + ceil((changed_overlapping_region_in_uv_space.ymin - tile_offset_y) * tile_height), + ceil((changed_overlapping_region_in_uv_space.ymax - tile_offset_y) * tile_height)); + + /* Create an image buffer with a size. + * Extract and scale into an imbuf. */ + const int texture_region_width = BLI_rcti_size_x(&gpu_texture_region_to_update); + const int texture_region_height = BLI_rcti_size_y(&gpu_texture_region_to_update); + + ImBuf extracted_buffer; + IMB_initImBuf( + &extracted_buffer, texture_region_width, texture_region_height, 32, IB_rectfloat); + + int offset = 0; + ImBuf *tile_buffer = iterator.tile_data.tile_buffer; + for (int y = gpu_texture_region_to_update.ymin; y < gpu_texture_region_to_update.ymax; + y++) { + float yf = y / (float)texture_height; + float v = info.region_uv_bounds.ymax * yf + info.region_uv_bounds.ymin * (1.0 - yf) - + tile_offset_y; + for (int x = gpu_texture_region_to_update.xmin; x < gpu_texture_region_to_update.xmax; + x++) { + float xf = x / (float)texture_width; + float u = info.region_uv_bounds.xmax * xf + info.region_uv_bounds.xmin * (1.0 - xf) - + tile_offset_x; + nearest_interpolation_color(tile_buffer, + nullptr, + &extracted_buffer.rect_float[offset * 4], + u * tile_buffer->x, + v * tile_buffer->y); + offset++; + } + } + + GPU_texture_update_sub(texture, + GPU_DATA_FLOAT, + extracted_buffer.rect_float, + gpu_texture_region_to_update.xmin, + gpu_texture_region_to_update.ymin, + 0, + extracted_buffer.x, + extracted_buffer.y, + 0); + imb_freerectImbuf_all(&extracted_buffer); + } + } + } + + void do_full_update_for_dirty_textures(IMAGE_InstanceData &instance_data, + const ImageUser *image_user) const + { + for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + TextureInfo &info = instance_data.texture_infos[i]; + if (!info.dirty) { + continue; + } + if (!info.visible) { + continue; + } + do_full_update_gpu_texture(info, instance_data, image_user); + } + } + + void do_full_update_gpu_texture(TextureInfo &info, + IMAGE_InstanceData &instance_data, + const ImageUser *image_user) const + { + + ImBuf texture_buffer; + const int texture_width = GPU_texture_width(info.texture); + const int texture_height = GPU_texture_height(info.texture); + IMB_initImBuf(&texture_buffer, texture_width, texture_height, 0, IB_rectfloat); + ImageUser tile_user = {0}; + if (image_user) { + tile_user = *image_user; + } + + void *lock; + + Image *image = instance_data.image; + LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) { + const ImageTileWrapper image_tile(image_tile_ptr); + tile_user.tile = image_tile.get_tile_number(); + + ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock); + if (tile_buffer == nullptr) { + /* Couldn't load the image buffer of the tile. */ + continue; + } + do_full_update_texture_slot(instance_data, info, texture_buffer, *tile_buffer, image_tile); + BKE_image_release_ibuf(image, tile_buffer, lock); + } + GPU_texture_update(info.texture, GPU_DATA_FLOAT, texture_buffer.rect_float); + imb_freerectImbuf_all(&texture_buffer); + } + + /** + * \brief Ensure that the float buffer of the given image buffer is available. + * + * Returns true when a float buffer was created. Somehow the VSE cache increases the ref + * counter, but might use a different mechanism for destructing the image, that doesn't free the + * rect_float as the reference-counter isn't 0. To work around this we destruct any created local + * buffers ourself. + */ + bool ensure_float_buffer(ImBuf &image_buffer) const + { + if (image_buffer.rect_float == nullptr) { + IMB_float_from_rect(&image_buffer); + return true; + } + return false; + } + + void do_full_update_texture_slot(const IMAGE_InstanceData &instance_data, + const TextureInfo &texture_info, + ImBuf &texture_buffer, + ImBuf &tile_buffer, + const ImageTileWrapper &image_tile) const + { + const int texture_width = texture_buffer.x; + const int texture_height = texture_buffer.y; + const bool float_buffer_created = ensure_float_buffer(tile_buffer); + /* TODO(jbakker): Find leak when rendering VSE and don't free here. */ + const bool do_free_float_buffer = float_buffer_created && + instance_data.image->type == IMA_TYPE_R_RESULT; + + /* IMB_transform works in a non-consistent space. This should be documented or fixed!. + * Construct a variant of the info_uv_to_texture that adds the texel space + * transformation.*/ + float uv_to_texel[4][4]; + copy_m4_m4(uv_to_texel, instance_data.ss_to_texture); + float scale[3] = {static_cast<float>(texture_width) / static_cast<float>(tile_buffer.x), + static_cast<float>(texture_height) / static_cast<float>(tile_buffer.y), + 1.0f}; + rescale_m4(uv_to_texel, scale); + uv_to_texel[3][0] += image_tile.get_tile_x_offset() / + BLI_rctf_size_x(&texture_info.region_uv_bounds); + uv_to_texel[3][1] += image_tile.get_tile_y_offset() / + BLI_rctf_size_y(&texture_info.region_uv_bounds); + uv_to_texel[3][0] *= texture_width; + uv_to_texel[3][1] *= texture_height; + invert_m4(uv_to_texel); + + rctf crop_rect; + rctf *crop_rect_ptr = nullptr; + eIMBTransformMode transform_mode; + if (instance_data.flags.do_tile_drawing) { + transform_mode = IMB_TRANSFORM_MODE_WRAP_REPEAT; + } + else { + BLI_rctf_init(&crop_rect, 0.0, tile_buffer.x, 0.0, tile_buffer.y); + crop_rect_ptr = &crop_rect; + transform_mode = IMB_TRANSFORM_MODE_CROP_SRC; + } + + IMB_transform(&tile_buffer, + &texture_buffer, + transform_mode, + IMB_FILTER_NEAREST, + uv_to_texel, + crop_rect_ptr); + + if (do_free_float_buffer) { + imb_freerectfloatImBuf(&tile_buffer); + } + } + + public: + void cache_init(IMAGE_Data *vedata) const override + { + IMAGE_InstanceData *instance_data = vedata->instance_data; + instance_data->passes.image_pass = create_image_pass(); + instance_data->passes.depth_pass = create_depth_pass(); + } + + void cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser) const override + { + const DRWContextState *draw_ctx = DRW_context_state_get(); + IMAGE_InstanceData *instance_data = vedata->instance_data; + TextureMethod method(instance_data); + + instance_data->partial_update.ensure_image(image); + instance_data->clear_dirty_flag(); + + /* Step: Find out which screen space textures are needed to draw on the screen. Remove the + * screen space textures that aren't needed. */ + const ARegion *region = draw_ctx->region; + method.update_screen_space_bounds(region); + method.update_region_uv_bounds(region); + method.update_screen_uv_bounds(); + + /* Step: Update the GPU textures based on the changes in the image. */ + instance_data->update_gpu_texture_allocations(); + update_textures(*instance_data, image, iuser); + + /* Step: Add the GPU textures to the shgroup. */ + instance_data->update_batches(); + add_depth_shgroups(*instance_data, image, iuser); + add_shgroups(instance_data); + } + + void draw_finish(IMAGE_Data *UNUSED(vedata)) const override + { + } + + void draw_scene(IMAGE_Data *vedata) const override + { + IMAGE_InstanceData *instance_data = vedata->instance_data; + + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + GPU_framebuffer_bind(dfbl->default_fb); + static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0); + + DRW_view_set_active(instance_data->view); + DRW_draw_pass(instance_data->passes.depth_pass); + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(instance_data->passes.image_pass); + DRW_view_set_active(nullptr); + GPU_framebuffer_bind(dfbl->default_fb); + } +}; // namespace clipping + +} // namespace blender::draw::image_engine diff --git a/source/blender/draw/engines/image/image_drawing_mode_image_space.hh b/source/blender/draw/engines/image/image_drawing_mode_image_space.hh deleted file mode 100644 index 26f4bc28106..00000000000 --- a/source/blender/draw/engines/image/image_drawing_mode_image_space.hh +++ /dev/null @@ -1,147 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2021, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#pragma once - -#include "image_private.hh" - -namespace blender::draw::image_engine { - -class ImageSpaceDrawingMode : public AbstractDrawingMode { - private: - DRWPass *create_image_pass() const - { - /* Write depth is needed for background overlay rendering. Near depth is used for - * transparency checker and Far depth is used for indicating the image size. */ - DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL); - return DRW_pass_create("Image", state); - } - - void add_to_shgroup(AbstractSpaceAccessor *space, - DRWShadingGroup *grp, - const Image *image, - const ImBuf *image_buffer) const - { - float image_mat[4][4]; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ARegion *region = draw_ctx->region; - space->get_image_mat(image_buffer, region, image_mat); - - GPUBatch *geom = DRW_cache_quad_get(); - - const float translate_x = image_mat[3][0]; - const float translate_y = image_mat[3][1]; - LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { - const int tile_x = ((tile->tile_number - 1001) % 10); - const int tile_y = ((tile->tile_number - 1001) / 10); - image_mat[3][0] = (float)tile_x + translate_x; - image_mat[3][1] = (float)tile_y + translate_y; - DRW_shgroup_call_obmat(grp, geom, image_mat); - } - } - - public: - void cache_init(IMAGE_Data *vedata) const override - { - IMAGE_PassList *psl = vedata->psl; - - psl->image_pass = create_image_pass(); - } - - void cache_image(AbstractSpaceAccessor *space, - IMAGE_Data *vedata, - Image *image, - ImageUser *iuser, - ImBuf *image_buffer) const override - { - IMAGE_PassList *psl = vedata->psl; - IMAGE_StorageList *stl = vedata->stl; - IMAGE_PrivateData *pd = stl->pd; - - GPUTexture *tex_tile_data = nullptr; - space->get_gpu_textures( - image, iuser, image_buffer, &pd->texture, &pd->owns_texture, &tex_tile_data); - if (pd->texture == nullptr) { - return; - } - const bool is_tiled_texture = tex_tile_data != nullptr; - - ShaderParameters sh_params; - sh_params.use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image, - image_buffer); - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene = draw_ctx->scene; - if (scene->camera && scene->camera->type == OB_CAMERA) { - Camera *camera = static_cast<Camera *>(scene->camera->data); - copy_v2_fl2(sh_params.far_near, camera->clip_end, camera->clip_start); - } - space->get_shader_parameters(sh_params, image_buffer, is_tiled_texture); - - GPUShader *shader = IMAGE_shader_image_get(is_tiled_texture); - DRWShadingGroup *shgrp = DRW_shgroup_create(shader, psl->image_pass); - if (is_tiled_texture) { - DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, GPU_SAMPLER_DEFAULT); - DRW_shgroup_uniform_texture(shgrp, "imageTileData", tex_tile_data); - } - else { - DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, GPU_SAMPLER_DEFAULT); - } - DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", sh_params.far_near); - DRW_shgroup_uniform_vec4_copy(shgrp, "color", ShaderParameters::color); - DRW_shgroup_uniform_vec4_copy(shgrp, "shuffle", sh_params.shuffle); - DRW_shgroup_uniform_int_copy(shgrp, "drawFlags", sh_params.flags); - DRW_shgroup_uniform_bool_copy(shgrp, "imgPremultiplied", sh_params.use_premul_alpha); - - add_to_shgroup(space, shgrp, image, image_buffer); - } - - void draw_finish(IMAGE_Data *vedata) const override - { - IMAGE_StorageList *stl = vedata->stl; - IMAGE_PrivateData *pd = stl->pd; - - if (pd->texture && pd->owns_texture) { - GPU_texture_free(pd->texture); - pd->owns_texture = false; - } - pd->texture = nullptr; - } - - void draw_scene(IMAGE_Data *vedata) const override - { - IMAGE_PassList *psl = vedata->psl; - IMAGE_PrivateData *pd = vedata->stl->pd; - - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - GPU_framebuffer_bind(dfbl->default_fb); - static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0); - - DRW_view_set_active(pd->view); - DRW_draw_pass(psl->image_pass); - DRW_view_set_active(nullptr); - } -}; - -} // namespace blender::draw::image_engine diff --git a/source/blender/draw/engines/image/image_engine.cc b/source/blender/draw/engines/image/image_engine.cc index be5946f34eb..201733f41a7 100644 --- a/source/blender/draw/engines/image/image_engine.cc +++ b/source/blender/draw/engines/image/image_engine.cc @@ -41,7 +41,7 @@ #include "GPU_batch.h" -#include "image_drawing_mode_image_space.hh" +#include "image_drawing_mode.hh" #include "image_engine.h" #include "image_private.hh" #include "image_space_image.hh" @@ -68,7 +68,7 @@ template< * * Useful during development to switch between drawing implementations. */ - typename DrawingMode = ImageSpaceDrawingMode> + typename DrawingMode = ScreenSpaceDrawingMode<OneTextureMethod>> class ImageEngine { private: const DRWContextState *draw_ctx; @@ -86,48 +86,58 @@ class ImageEngine { void cache_init() { - IMAGE_StorageList *stl = vedata->stl; - IMAGE_PrivateData *pd = stl->pd; - + IMAGE_InstanceData *instance_data = vedata->instance_data; drawing_mode.cache_init(vedata); - pd->view = nullptr; - if (space->has_view_override()) { - const ARegion *region = draw_ctx->region; - pd->view = space->create_view_override(region); - } + + /* Setup full screen view matrix. */ + const ARegion *region = draw_ctx->region; + float winmat[4][4], viewmat[4][4]; + orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0); + unit_m4(winmat); + instance_data->view = DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr); } void cache_populate() { - IMAGE_StorageList *stl = vedata->stl; - IMAGE_PrivateData *pd = stl->pd; + IMAGE_InstanceData *instance_data = vedata->instance_data; Main *bmain = CTX_data_main(draw_ctx->evil_C); - pd->image = space->get_image(bmain); - if (pd->image == nullptr) { + instance_data->image = space->get_image(bmain); + if (instance_data->image == nullptr) { /* Early exit, nothing to draw. */ return; } - pd->ibuf = space->acquire_image_buffer(pd->image, &pd->lock); + instance_data->flags.do_tile_drawing = instance_data->image->source != IMA_SRC_TILED && + space->use_tile_drawing(); + void *lock; + ImBuf *image_buffer = space->acquire_image_buffer(instance_data->image, &lock); + + /* Setup the matrix to go from screen UV coordinates to UV texture space coordinates. */ + float image_resolution[2] = {image_buffer ? image_buffer->x : 1024.0f, + image_buffer ? image_buffer->y : 1024.0f}; + space->init_ss_to_texture_matrix( + draw_ctx->region, image_resolution, instance_data->ss_to_texture); + + const Scene *scene = DRW_context_state_get()->scene; + instance_data->sh_params.update(space.get(), scene, instance_data->image, image_buffer); + space->release_buffer(instance_data->image, image_buffer, lock); + ImageUser *iuser = space->get_image_user(); - drawing_mode.cache_image(space.get(), vedata, pd->image, iuser, pd->ibuf); + drawing_mode.cache_image(vedata, instance_data->image, iuser); } void draw_finish() { drawing_mode.draw_finish(vedata); - IMAGE_StorageList *stl = vedata->stl; - IMAGE_PrivateData *pd = stl->pd; - space->release_buffer(pd->image, pd->ibuf, pd->lock); - pd->image = nullptr; - pd->ibuf = nullptr; + IMAGE_InstanceData *instance_data = vedata->instance_data; + instance_data->image = nullptr; } void draw_scene() { drawing_mode.draw_scene(vedata); } -}; +}; // namespace blender::draw::image_engine /* -------------------------------------------------------------------- */ /** \name Engine Callbacks @@ -135,17 +145,10 @@ class ImageEngine { static void IMAGE_engine_init(void *ved) { - IMAGE_shader_library_ensure(); IMAGE_Data *vedata = (IMAGE_Data *)ved; - IMAGE_StorageList *stl = vedata->stl; - if (!stl->pd) { - stl->pd = static_cast<IMAGE_PrivateData *>(MEM_callocN(sizeof(IMAGE_PrivateData), __func__)); + if (vedata->instance_data == nullptr) { + vedata->instance_data = MEM_new<IMAGE_InstanceData>(__func__); } - IMAGE_PrivateData *pd = stl->pd; - - pd->ibuf = nullptr; - pd->lock = nullptr; - pd->texture = nullptr; } static void IMAGE_cache_init(void *vedata) @@ -174,6 +177,12 @@ static void IMAGE_engine_free() IMAGE_shader_free(); } +static void IMAGE_instance_free(void *_instance_data) +{ + IMAGE_InstanceData *instance_data = reinterpret_cast<IMAGE_InstanceData *>(_instance_data); + MEM_delete(instance_data); +} + /** \} */ static const DrawEngineDataSize IMAGE_data_size = DRW_VIEWPORT_DATA_SIZE(IMAGE_Data); @@ -191,7 +200,7 @@ DrawEngineType draw_engine_image_type = { &IMAGE_data_size, /* vedata_size */ &IMAGE_engine_init, /* engine_init */ &IMAGE_engine_free, /* engine_free */ - nullptr, /* instance_free */ + &IMAGE_instance_free, /* instance_free */ &IMAGE_cache_init, /* cache_init */ &IMAGE_cache_populate, /* cache_populate */ nullptr, /* cache_finish */ diff --git a/source/blender/draw/engines/image/image_instance_data.hh b/source/blender/draw/engines/image/image_instance_data.hh new file mode 100644 index 00000000000..1a7a20b8b9a --- /dev/null +++ b/source/blender/draw/engines/image/image_instance_data.hh @@ -0,0 +1,119 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "image_batches.hh" +#include "image_partial_updater.hh" +#include "image_private.hh" +#include "image_shader_params.hh" +#include "image_texture_info.hh" +#include "image_wrappers.hh" + +#include "DRW_render.h" + +/** + * \brief max allowed textures to use by the ScreenSpaceDrawingMode. + * + * 4 textures are used to reduce uploading screen space textures when translating the image. + */ +constexpr int SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN = 4; + +struct IMAGE_InstanceData { + struct Image *image; + + PartialImageUpdater partial_update; + + struct DRWView *view; + ShaderParameters sh_params; + struct { + /** + * \brief should we perform tiled drawing (wrap repeat). + * + * Option is true when image is capable of tile drawing (image is not tile) and the tiled + * option is set in the space. + */ + bool do_tile_drawing : 1; + } flags; + + struct { + DRWPass *image_pass; + DRWPass *depth_pass; + } passes; + + /** \brief Transform matrix to convert a normalized screen space coordinates to texture space. */ + float ss_to_texture[4][4]; + TextureInfo texture_infos[SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN]; + + public: + void clear_dirty_flag() + { + reset_dirty_flag(false); + } + void mark_all_texture_slots_dirty() + { + reset_dirty_flag(true); + } + + void update_gpu_texture_allocations() + { + for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + TextureInfo &info = texture_infos[i]; + const bool is_allocated = info.texture != nullptr; + const bool is_visible = info.visible; + const bool should_be_freed = !is_visible && is_allocated; + const bool should_be_created = is_visible && !is_allocated; + + if (should_be_freed) { + GPU_texture_free(info.texture); + info.texture = nullptr; + } + + if (should_be_created) { + DRW_texture_ensure_fullscreen_2d( + &info.texture, GPU_RGBA16F, static_cast<DRWTextureFlag>(0)); + } + info.dirty |= should_be_created; + } + } + + void update_batches() + { + for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + TextureInfo &info = texture_infos[i]; + if (!info.dirty) { + continue; + } + BatchUpdater batch_updater(info); + batch_updater.update_batch(); + } + } + + private: + /** \brief Set dirty flag of all texture slots to the given value. */ + void reset_dirty_flag(bool new_value) + { + for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) { + texture_infos[i].dirty = new_value; + } + } +}; diff --git a/source/blender/draw/engines/image/image_partial_updater.hh b/source/blender/draw/engines/image/image_partial_updater.hh new file mode 100644 index 00000000000..f0c1db2331a --- /dev/null +++ b/source/blender/draw/engines/image/image_partial_updater.hh @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "BKE_image.h" +#include "BKE_image_partial_update.hh" + +struct PartialImageUpdater { + struct PartialUpdateUser *user; + const struct Image *image; + + /** + * \brief Ensure that there is a partial update user for the given image. + */ + void ensure_image(const Image *image) + { + if (!is_valid(image)) { + free(); + create(image); + } + } + + virtual ~PartialImageUpdater() + { + free(); + } + + private: + /** + * \brief check if the partial update user can still be used for the given image. + * + * When switching to a different image the partial update user should be recreated. + */ + bool is_valid(const Image *new_image) const + { + if (image != new_image) { + return false; + } + + return user != nullptr; + } + + void create(const Image *image) + { + BLI_assert(user == nullptr); + user = BKE_image_partial_update_create(image); + image = image; + } + + void free() + { + if (user != nullptr) { + BKE_image_partial_update_free(user); + user = nullptr; + image = nullptr; + } + } +}; diff --git a/source/blender/draw/engines/image/image_private.hh b/source/blender/draw/engines/image/image_private.hh index fc3ce72d0aa..d8f8adb7e84 100644 --- a/source/blender/draw/engines/image/image_private.hh +++ b/source/blender/draw/engines/image/image_private.hh @@ -24,6 +24,11 @@ #include <optional> +#include "BKE_image.h" + +#include "image_instance_data.hh" +#include "image_texture_info.hh" + /* Forward declarations */ extern "C" { struct GPUTexture; @@ -35,32 +40,13 @@ struct Image; namespace blender::draw::image_engine { -/* GPUViewport.storage - * Is freed every time the viewport engine changes. */ -struct IMAGE_PassList { - DRWPass *image_pass; -}; - -struct IMAGE_PrivateData { - void *lock; - struct ImBuf *ibuf; - struct Image *image; - struct DRWView *view; - - struct GPUTexture *texture; - bool owns_texture; -}; - -struct IMAGE_StorageList { - IMAGE_PrivateData *pd; -}; - struct IMAGE_Data { void *engine_type; DRWViewportEmptyList *fbl; DRWViewportEmptyList *txl; - IMAGE_PassList *psl; - IMAGE_StorageList *stl; + DRWViewportEmptyList *psl; + DRWViewportEmptyList *stl; + IMAGE_InstanceData *instance_data; }; /* Shader parameters. */ @@ -68,106 +54,6 @@ struct IMAGE_Data { #define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1) #define IMAGE_DRAW_FLAG_SHUFFLING (1 << 2) #define IMAGE_DRAW_FLAG_DEPTH (1 << 3) -#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4) -#define IMAGE_DRAW_FLAG_USE_WORLD_POS (1 << 5) - -struct ShaderParameters { - constexpr static float color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - - int flags = 0; - float shuffle[4]; - float far_near[2]; - bool use_premul_alpha = false; - - ShaderParameters() - { - copy_v4_fl(shuffle, 1.0f); - copy_v2_fl2(far_near, 100.0f, 0.0f); - } -}; - -/** - * Space accessor. - * - * Image engine is used to draw the images inside multiple spaces \see SpaceLink. - * The AbstractSpaceAccessor is an interface to communicate with a space. - */ -class AbstractSpaceAccessor { - public: - virtual ~AbstractSpaceAccessor() = default; - - /** - * Return the active image of the space. - * - * The returned image will be drawn in the space. - * - * The return value is optional. - */ - virtual Image *get_image(Main *bmain) = 0; - - /** - * Return the #ImageUser of the space. - * - * The return value is optional. - */ - virtual ImageUser *get_image_user() = 0; - - /** - * Acquire the image buffer of the image. - * - * \param image: Image to get the buffer from. Image is the same as returned from the #get_image - * member. - * \param lock: pointer to a lock object. - * \return Image buffer of the given image. - */ - virtual ImBuf *acquire_image_buffer(Image *image, void **lock) = 0; - - /** - * Release a previous locked image from #acquire_image_buffer. - */ - virtual void release_buffer(Image *image, ImBuf *image_buffer, void *lock) = 0; - - /** - * Update the r_shader_parameters with space specific settings. - * - * Only update the #ShaderParameters.flags and #ShaderParameters.shuffle. Other parameters - * are updated inside the image engine. - */ - virtual void get_shader_parameters(ShaderParameters &r_shader_parameters, - ImBuf *image_buffer, - bool is_tiled) = 0; - - /** - * Retrieve the gpu textures to draw. - */ - virtual void get_gpu_textures(Image *image, - ImageUser *iuser, - ImBuf *image_buffer, - GPUTexture **r_gpu_texture, - bool *r_owns_texture, - GPUTexture **r_tex_tile_data) = 0; - - /** - * Does this space override the view. - * When so this member should return true and the create_view_override must return the view to - * use during drawing. - */ - virtual bool has_view_override() const = 0; - - /** - * Override the view for drawing. - * Should match #has_view_override. - */ - virtual DRWView *create_view_override(const ARegion *UNUSED(region)) = 0; - - /** - * Initialize the matrix that will be used to draw the image. The matrix will be send as object - * matrix to the drawing pipeline. - */ - virtual void get_image_mat(const ImBuf *image_buffer, - const ARegion *region, - float r_mat[4][4]) const = 0; -}; // namespace blender::draw::image_engine /** * Abstract class for a drawing mode of the image engine. @@ -179,18 +65,14 @@ class AbstractDrawingMode { public: virtual ~AbstractDrawingMode() = default; virtual void cache_init(IMAGE_Data *vedata) const = 0; - virtual void cache_image(AbstractSpaceAccessor *space, - IMAGE_Data *vedata, - Image *image, - ImageUser *iuser, - ImBuf *image_buffer) const = 0; + virtual void cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser) const = 0; virtual void draw_scene(IMAGE_Data *vedata) const = 0; virtual void draw_finish(IMAGE_Data *vedata) const = 0; }; /* image_shader.c */ -GPUShader *IMAGE_shader_image_get(bool is_tiled_image); -void IMAGE_shader_library_ensure(); +GPUShader *IMAGE_shader_image_get(); +GPUShader *IMAGE_shader_depth_get(); void IMAGE_shader_free(); } // namespace blender::draw::image_engine diff --git a/source/blender/draw/engines/image/image_shader.cc b/source/blender/draw/engines/image/image_shader.cc index 1c6abf36505..952843d7dd7 100644 --- a/source/blender/draw/engines/image/image_shader.cc +++ b/source/blender/draw/engines/image/image_shader.cc @@ -29,50 +29,33 @@ #include "image_engine.h" #include "image_private.hh" -extern "C" { -extern char datatoc_common_colormanagement_lib_glsl[]; -extern char datatoc_common_globals_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; - -extern char datatoc_engine_image_frag_glsl[]; -extern char datatoc_engine_image_vert_glsl[]; -} - namespace blender::draw::image_engine { struct IMAGE_Shaders { - GPUShader *image_sh[2]; + GPUShader *image_sh; + GPUShader *depth_sh; }; static struct { IMAGE_Shaders shaders; - DRWShaderLibrary *lib; -} e_data = {{{nullptr}}}; /* Engine data */ +} e_data = {{nullptr}}; /* Engine data */ -void IMAGE_shader_library_ensure() +GPUShader *IMAGE_shader_image_get() { - if (e_data.lib == nullptr) { - e_data.lib = DRW_shader_library_create(); - /* NOTE: These need to be ordered by dependencies. */ - DRW_SHADER_LIB_ADD(e_data.lib, common_colormanagement_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_globals_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); + IMAGE_Shaders *sh_data = &e_data.shaders; + if (sh_data->image_sh == nullptr) { + sh_data->image_sh = GPU_shader_create_from_info_name("image_engine_color_shader"); } + return sh_data->image_sh; } -GPUShader *IMAGE_shader_image_get(bool is_tiled_image) +GPUShader *IMAGE_shader_depth_get() { - const int index = is_tiled_image ? 1 : 0; IMAGE_Shaders *sh_data = &e_data.shaders; - if (sh_data->image_sh[index] == nullptr) { - sh_data->image_sh[index] = DRW_shader_create_with_shaderlib( - datatoc_engine_image_vert_glsl, - nullptr, - datatoc_engine_image_frag_glsl, - e_data.lib, - is_tiled_image ? "#define TILED_IMAGE\n" : nullptr); + if (sh_data->depth_sh == nullptr) { + sh_data->depth_sh = GPU_shader_create_from_info_name("image_engine_depth_shader"); } - return sh_data->image_sh[index]; + return sh_data->depth_sh; } void IMAGE_shader_free() @@ -81,8 +64,6 @@ void IMAGE_shader_free() for (int i = 0; i < (sizeof(IMAGE_Shaders) / sizeof(GPUShader *)); i++) { DRW_SHADER_FREE_SAFE(sh_data_as_array[i]); } - - DRW_SHADER_LIB_FREE_SAFE(e_data.lib); } } // namespace blender::draw::image_engine diff --git a/source/blender/draw/engines/image/image_shader_params.hh b/source/blender/draw/engines/image/image_shader_params.hh new file mode 100644 index 00000000000..9f3da3f9e9e --- /dev/null +++ b/source/blender/draw/engines/image/image_shader_params.hh @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "DNA_camera_types.h" +#include "DNA_image_types.h" +#include "DNA_scene_types.h" + +#include "IMB_imbuf_types.h" + +#include "BKE_image.h" + +#include "BLI_math.h" + +#include "image_space.hh" + +struct ShaderParameters { + int flags = 0; + float shuffle[4]; + float far_near[2]; + bool use_premul_alpha = false; + + void update(AbstractSpaceAccessor *space, const Scene *scene, Image *image, ImBuf *image_buffer) + { + flags = 0; + copy_v4_fl(shuffle, 1.0f); + copy_v2_fl2(far_near, 100.0f, 0.0f); + + use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer); + + if (scene->camera && scene->camera->type == OB_CAMERA) { + Camera *camera = static_cast<Camera *>(scene->camera->data); + copy_v2_fl2(far_near, camera->clip_end, camera->clip_start); + } + space->get_shader_parameters(*this, image_buffer); + } +}; diff --git a/source/blender/draw/engines/image/image_space.hh b/source/blender/draw/engines/image/image_space.hh new file mode 100644 index 00000000000..fbb92ce24aa --- /dev/null +++ b/source/blender/draw/engines/image/image_space.hh @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +struct ShaderParameters; + +/** + * Space accessor. + * + * Image engine is used to draw the images inside multiple spaces \see SpaceLink. + * The AbstractSpaceAccessor is an interface to communicate with a space. + */ +class AbstractSpaceAccessor { + public: + virtual ~AbstractSpaceAccessor() = default; + + /** + * Return the active image of the space. + * + * The returned image will be drawn in the space. + * + * The return value is optional. + */ + virtual Image *get_image(Main *bmain) = 0; + + /** + * Return the #ImageUser of the space. + * + * The return value is optional. + */ + virtual ImageUser *get_image_user() = 0; + + /** + * Acquire the image buffer of the image. + * + * \param image: Image to get the buffer from. Image is the same as returned from the #get_image + * member. + * \param lock: pointer to a lock object. + * \return Image buffer of the given image. + */ + virtual ImBuf *acquire_image_buffer(Image *image, void **lock) = 0; + + /** + * Release a previous locked image from #acquire_image_buffer. + */ + virtual void release_buffer(Image *image, ImBuf *image_buffer, void *lock) = 0; + + /** + * Update the r_shader_parameters with space specific settings. + * + * Only update the #ShaderParameters.flags and #ShaderParameters.shuffle. Other parameters + * are updated inside the image engine. + */ + virtual void get_shader_parameters(ShaderParameters &r_shader_parameters, + ImBuf *image_buffer) = 0; + + /** + * Retrieve the gpu textures to draw. + */ + virtual void get_gpu_textures(Image *image, + ImageUser *iuser, + ImBuf *image_buffer, + GPUTexture **r_gpu_texture, + bool *r_owns_texture, + GPUTexture **r_tex_tile_data) = 0; + + /** \brief Is (wrap) repeat option enabled in the space. */ + virtual bool use_tile_drawing() const = 0; + + /** + * \brief Initialize r_uv_to_texture matrix to transform from normalized screen space coordinates + * (0..1) to texture space UV coordinates. + */ + virtual void init_ss_to_texture_matrix(const ARegion *region, + const float image_resolution[2], + float r_uv_to_texture[4][4]) const = 0; + +}; // namespace blender::draw::image_engine diff --git a/source/blender/draw/engines/image/image_space_image.hh b/source/blender/draw/engines/image/image_space_image.hh index 7728a963254..090481a351d 100644 --- a/source/blender/draw/engines/image/image_space_image.hh +++ b/source/blender/draw/engines/image/image_space_image.hh @@ -54,14 +54,9 @@ class SpaceImageAccessor : public AbstractSpaceAccessor { ED_space_image_release_buffer(sima, image_buffer, lock); } - void get_shader_parameters(ShaderParameters &r_shader_parameters, - ImBuf *image_buffer, - bool is_tiled) override + void get_shader_parameters(ShaderParameters &r_shader_parameters, ImBuf *image_buffer) override { const int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(image_buffer); - const bool do_repeat = (!is_tiled) && ((sima->flag & SI_DRAW_TILE) != 0); - SET_FLAG_FROM_TEST(r_shader_parameters.flags, do_repeat, IMAGE_DRAW_FLAG_DO_REPEAT); - SET_FLAG_FROM_TEST(r_shader_parameters.flags, is_tiled, IMAGE_DRAW_FLAG_USE_WORLD_POS); if ((sima_flag & SI_USE_ALPHA) != 0) { /* Show RGBA */ r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHOW_ALPHA | IMAGE_DRAW_FLAG_APPLY_ALPHA; @@ -102,15 +97,6 @@ class SpaceImageAccessor : public AbstractSpaceAccessor { } } - bool has_view_override() const override - { - return false; - } - DRWView *create_view_override(const ARegion *UNUSED(region)) override - { - return nullptr; - } - void get_gpu_textures(Image *image, ImageUser *iuser, ImBuf *image_buffer, @@ -171,11 +157,25 @@ class SpaceImageAccessor : public AbstractSpaceAccessor { } } - void get_image_mat(const ImBuf *UNUSED(image_buffer), - const ARegion *UNUSED(region), - float r_mat[4][4]) const override + bool use_tile_drawing() const override + { + return (sima->flag & SI_DRAW_TILE) != 0; + } + + void init_ss_to_texture_matrix(const ARegion *region, + const float UNUSED(image_resolution[2]), + float r_uv_to_texture[4][4]) const override { - unit_m4(r_mat); + unit_m4(r_uv_to_texture); + float scale_x = 1.0 / BLI_rctf_size_x(®ion->v2d.cur); + float scale_y = 1.0 / BLI_rctf_size_y(®ion->v2d.cur); + float translate_x = scale_x * -region->v2d.cur.xmin; + float translate_y = scale_y * -region->v2d.cur.ymin; + + r_uv_to_texture[0][0] = scale_x; + r_uv_to_texture[1][1] = scale_y; + r_uv_to_texture[3][0] = translate_x; + r_uv_to_texture[3][1] = translate_y; } }; diff --git a/source/blender/draw/engines/image/image_space_node.hh b/source/blender/draw/engines/image/image_space_node.hh index 3ca18eec742..15eef8f6499 100644 --- a/source/blender/draw/engines/image/image_space_node.hh +++ b/source/blender/draw/engines/image/image_space_node.hh @@ -54,23 +54,7 @@ class SpaceNodeAccessor : public AbstractSpaceAccessor { BKE_image_release_ibuf(image, ibuf, lock); } - bool has_view_override() const override - { - return true; - } - - DRWView *create_view_override(const ARegion *region) override - { - /* Setup a screen pixel view. The backdrop of the node editor doesn't follow the region. */ - float winmat[4][4], viewmat[4][4]; - orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0); - unit_m4(winmat); - return DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr); - } - - void get_shader_parameters(ShaderParameters &r_shader_parameters, - ImBuf *ibuf, - bool UNUSED(is_tiled)) override + void get_shader_parameters(ShaderParameters &r_shader_parameters, ImBuf *ibuf) override { if ((snode->flag & SNODE_USE_ALPHA) != 0) { /* Show RGBA */ @@ -120,18 +104,33 @@ class SpaceNodeAccessor : public AbstractSpaceAccessor { *r_tex_tile_data = nullptr; } - void get_image_mat(const ImBuf *image_buffer, - const ARegion *region, - float r_mat[4][4]) const override + bool use_tile_drawing() const override + { + return false; + } + + /** + * The backdrop of the node editor isn't drawn in screen space UV space. But is locked with the + * screen. + */ + void init_ss_to_texture_matrix(const ARegion *region, + const float image_resolution[2], + float r_uv_to_texture[4][4]) const override { - unit_m4(r_mat); - const float ibuf_width = image_buffer->x; - const float ibuf_height = image_buffer->y; - - r_mat[0][0] = ibuf_width * snode->zoom; - r_mat[1][1] = ibuf_height * snode->zoom; - r_mat[3][0] = (region->winx - snode->zoom * ibuf_width) / 2 + snode->xof; - r_mat[3][1] = (region->winy - snode->zoom * ibuf_height) / 2 + snode->yof; + unit_m4(r_uv_to_texture); + float display_resolution[2]; + mul_v2_v2fl(display_resolution, image_resolution, snode->zoom); + const float scale_x = display_resolution[0] / region->winx; + const float scale_y = display_resolution[1] / region->winy; + const float translate_x = ((region->winx - display_resolution[0]) * 0.5f + snode->xof) / + region->winx; + const float translate_y = ((region->winy - display_resolution[1]) * 0.5f + snode->yof) / + region->winy; + + r_uv_to_texture[0][0] = scale_x; + r_uv_to_texture[1][1] = scale_y; + r_uv_to_texture[3][0] = translate_x; + r_uv_to_texture[3][1] = translate_y; } }; diff --git a/source/blender/draw/engines/image/image_texture_info.hh b/source/blender/draw/engines/image/image_texture_info.hh new file mode 100644 index 00000000000..8c3b7494831 --- /dev/null +++ b/source/blender/draw/engines/image/image_texture_info.hh @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2022, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "BLI_rect.h" + +#include "GPU_batch.h" +#include "GPU_texture.h" + +struct TextureInfo { + /** + * \brief Is the texture clipped. + * + * Resources of clipped textures are freed and ignored when performing partial updates. + */ + bool visible : 1; + + /** + * \brief does this texture need a full update. + * + * When set to false the texture can be updated using a partial update. + */ + bool dirty : 1; + + /** \brief area of the texture in screen space. */ + rctf clipping_bounds; + /** \brief uv area of the texture (copy from ARegion). */ + rctf region_uv_bounds; + /** \brief uv area of the texture in screen space. */ + rctf clipping_uv_bounds; + + /** + * \brief Batch to draw the associated text on the screen. + * + * Contains a VBO with `pos` and `uv`. + * `pos` (2xF32) is relative to the origin of the space. + * `uv` (2xF32) reflect the uv bounds. + */ + GPUBatch *batch; + + /** + * \brief GPU Texture for a partial region of the image editor. + */ + GPUTexture *texture; + + ~TextureInfo() + { + if (batch != nullptr) { + GPU_batch_discard(batch); + batch = nullptr; + } + + if (texture != nullptr) { + GPU_texture_free(texture); + texture = nullptr; + } + } +}; diff --git a/source/blender/draw/engines/image/image_wrappers.hh b/source/blender/draw/engines/image/image_wrappers.hh new file mode 100644 index 00000000000..63dbbde12ef --- /dev/null +++ b/source/blender/draw/engines/image/image_wrappers.hh @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2022, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "DNA_image_types.h" + +struct ImageTileWrapper { + ImageTile *image_tile; + ImageTileWrapper(ImageTile *image_tile) : image_tile(image_tile) + { + } + + int get_tile_number() const + { + return image_tile->tile_number; + } + + int get_tile_x_offset() const + { + int tile_number = get_tile_number(); + return (tile_number - 1001) % 10; + } + + int get_tile_y_offset() const + { + int tile_number = get_tile_number(); + return (tile_number - 1001) / 10; + } +}; diff --git a/source/blender/draw/engines/image/shaders/engine_image_frag.glsl b/source/blender/draw/engines/image/shaders/engine_image_frag.glsl deleted file mode 100644 index 55a2f2a72f1..00000000000 --- a/source/blender/draw/engines/image/shaders/engine_image_frag.glsl +++ /dev/null @@ -1,94 +0,0 @@ -#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl) - -/* Keep in sync with image_engine.c */ -#define IMAGE_DRAW_FLAG_SHOW_ALPHA (1 << 0) -#define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1) -#define IMAGE_DRAW_FLAG_SHUFFLING (1 << 2) -#define IMAGE_DRAW_FLAG_DEPTH (1 << 3) -#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4) - -#ifdef TILED_IMAGE -uniform sampler2DArray imageTileArray; -uniform sampler1DArray imageTileData; -#else -uniform sampler2D imageTexture; -#endif - -uniform bool imgPremultiplied; -uniform int drawFlags; -uniform vec2 farNearDistances; -uniform vec4 color; -uniform vec4 shuffle; - -#define FAR_DISTANCE farNearDistances.x -#define NEAR_DISTANCE farNearDistances.y - -in vec2 uvs; - -out vec4 fragColor; - -#ifdef TILED_IMAGE -/* TODO(fclem): deduplicate code. */ -bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) -{ - vec2 tile_pos = floor(co.xy); - - if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) { - return false; - } - - float tile = 10.0 * tile_pos.y + tile_pos.x; - if (tile >= textureSize(map, 0).x) { - return false; - } - - /* Fetch tile information. */ - float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x; - if (tile_layer < 0.0) { - return false; - } - - vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0); - - co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer); - return true; -} -#endif - -void main() -{ - vec4 tex_color; - /* Read texture */ -#ifdef TILED_IMAGE - vec3 co = vec3(uvs, 0.0); - if (node_tex_tile_lookup(co, imageTileArray, imageTileData)) { - tex_color = texture(imageTileArray, co); - } - else { - tex_color = vec4(1.0, 0.0, 1.0, 1.0); - } -#else - vec2 uvs_clamped = ((drawFlags & IMAGE_DRAW_FLAG_DO_REPEAT) != 0) ? - fract(uvs) : - clamp(uvs, vec2(0.0), vec2(1.0)); - tex_color = texture(imageTexture, uvs_clamped); -#endif - - if ((drawFlags & IMAGE_DRAW_FLAG_APPLY_ALPHA) != 0) { - if (!imgPremultiplied) { - tex_color.rgb *= tex_color.a; - } - } - if ((drawFlags & IMAGE_DRAW_FLAG_DEPTH) != 0) { - tex_color = smoothstep(FAR_DISTANCE, NEAR_DISTANCE, tex_color); - } - - if ((drawFlags & IMAGE_DRAW_FLAG_SHUFFLING) != 0) { - tex_color = color * dot(tex_color, shuffle); - } - if ((drawFlags & IMAGE_DRAW_FLAG_SHOW_ALPHA) == 0) { - tex_color.a = 1.0; - } - - fragColor = tex_color; -} diff --git a/source/blender/draw/engines/image/shaders/engine_image_vert.glsl b/source/blender/draw/engines/image/shaders/engine_image_vert.glsl deleted file mode 100644 index da2b1907c41..00000000000 --- a/source/blender/draw/engines/image/shaders/engine_image_vert.glsl +++ /dev/null @@ -1,34 +0,0 @@ -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4) -#define IMAGE_DRAW_FLAG_USE_WORLD_POS (1 << 5) -#define IMAGE_Z_DEPTH 0.75 - -uniform int drawFlags; - -in vec3 pos; -out vec2 uvs; - -void main() -{ - /* `pos` contains the coordinates of a quad (-1..1). but we need the coordinates of an image - * plane (0..1) */ - vec3 image_pos = pos * 0.5 + 0.5; - - if ((drawFlags & IMAGE_DRAW_FLAG_DO_REPEAT) != 0) { - gl_Position = vec4(pos.xy, IMAGE_Z_DEPTH, 1.0); - uvs = point_view_to_object(image_pos).xy; - } - else { - vec3 world_pos = point_object_to_world(image_pos); - vec4 position = point_world_to_ndc(world_pos); - /* Move drawn pixels to the front. In the overlay engine the depth is used - * to detect if a transparency texture or the background color should be drawn. - * Vertices are between 0.0 and 0.2, Edges between 0.2 and 0.4 - * actual pixels are at 0.75, 1.0 is used for the background. */ - position.z = IMAGE_Z_DEPTH; - gl_Position = position; - /* UDIM texture uses the world position for tile selection. */ - uvs = ((drawFlags & IMAGE_DRAW_FLAG_USE_WORLD_POS) != 0) ? world_pos.xy : image_pos.xy; - } -} diff --git a/source/blender/draw/engines/image/shaders/image_engine_color_frag.glsl b/source/blender/draw/engines/image/shaders/image_engine_color_frag.glsl new file mode 100644 index 00000000000..0edc18836f0 --- /dev/null +++ b/source/blender/draw/engines/image/shaders/image_engine_color_frag.glsl @@ -0,0 +1,38 @@ +#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl) + +/* Keep in sync with image_engine.c */ +#define IMAGE_DRAW_FLAG_SHOW_ALPHA (1 << 0) +#define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1) +#define IMAGE_DRAW_FLAG_SHUFFLING (1 << 2) +#define IMAGE_DRAW_FLAG_DEPTH (1 << 3) + +#define FAR_DISTANCE farNearDistances.x +#define NEAR_DISTANCE farNearDistances.y + +void main() +{ + ivec2 uvs_clamped = ivec2(uv_screen); + float depth = texelFetch(depth_texture, uvs_clamped, 0).r; + if (depth == 1.0) { + discard; + } + + vec4 tex_color = texelFetch(imageTexture, uvs_clamped, 0); + + if ((drawFlags & IMAGE_DRAW_FLAG_APPLY_ALPHA) != 0) { + if (!imgPremultiplied) { + tex_color.rgb *= tex_color.a; + } + } + if ((drawFlags & IMAGE_DRAW_FLAG_DEPTH) != 0) { + tex_color = smoothstep(FAR_DISTANCE, NEAR_DISTANCE, tex_color); + } + + if ((drawFlags & IMAGE_DRAW_FLAG_SHUFFLING) != 0) { + tex_color = vec4(dot(tex_color, shuffle)); + } + if ((drawFlags & IMAGE_DRAW_FLAG_SHOW_ALPHA) == 0) { + tex_color.a = 1.0; + } + fragColor = tex_color; +} diff --git a/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl b/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl new file mode 100644 index 00000000000..fb72a132613 --- /dev/null +++ b/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl @@ -0,0 +1,11 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + vec3 image_pos = vec3(pos, 0.0); + uv_screen = image_pos.xy; + + vec3 world_pos = point_object_to_world(image_pos); + vec4 position = point_world_to_ndc(world_pos); + gl_Position = position; +} diff --git a/source/blender/draw/engines/image/shaders/image_engine_depth_frag.glsl b/source/blender/draw/engines/image/shaders/image_engine_depth_frag.glsl new file mode 100644 index 00000000000..88610fb97fd --- /dev/null +++ b/source/blender/draw/engines/image/shaders/image_engine_depth_frag.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl) + +#define Z_DEPTH_BORDER 1.0 +#define Z_DEPTH_IMAGE 0.75 + +bool is_border(vec2 uv) +{ + return (uv.x < min_max_uv.x || uv.y < min_max_uv.y || uv.x >= min_max_uv.z || + uv.y >= min_max_uv.w); +} + +void main() +{ + bool border = is_border(uv_image); + gl_FragDepth = border ? Z_DEPTH_BORDER : Z_DEPTH_IMAGE; +} diff --git a/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl b/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl new file mode 100644 index 00000000000..3181a85ff55 --- /dev/null +++ b/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl @@ -0,0 +1,11 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + vec3 image_pos = vec3(pos, 0.0); + uv_image = uv; + + vec3 world_pos = point_object_to_world(image_pos); + vec4 position = point_world_to_ndc(world_pos); + gl_Position = position; +} diff --git a/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh b/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh new file mode 100644 index 00000000000..8b671686a5c --- /dev/null +++ b/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh @@ -0,0 +1,30 @@ +#include "gpu_shader_create_info.hh" + +GPU_SHADER_INTERFACE_INFO(image_engine_color_iface, "").smooth(Type::VEC2, "uv_screen"); + +GPU_SHADER_CREATE_INFO(image_engine_color_shader) + .vertex_in(0, Type::VEC2, "pos") + .vertex_out(image_engine_color_iface) + .fragment_out(0, Type::VEC4, "fragColor") + .push_constant(Type::VEC4, "shuffle") + .push_constant(Type::VEC2, "farNearDistances") + .push_constant(Type::INT, "drawFlags") + .push_constant(Type::BOOL, "imgPremultiplied") + .sampler(0, ImageType::FLOAT_2D, "imageTexture") + .sampler(1, ImageType::DEPTH_2D, "depth_texture") + .vertex_source("image_engine_color_vert.glsl") + .fragment_source("image_engine_color_frag.glsl") + .additional_info("draw_modelmat") + .do_static_compilation(true); + +GPU_SHADER_INTERFACE_INFO(image_engine_depth_iface, "").smooth(Type::VEC2, "uv_image"); + +GPU_SHADER_CREATE_INFO(image_engine_depth_shader) + .vertex_in(0, Type::VEC2, "pos") + .vertex_in(1, Type::VEC2, "uv") + .vertex_out(image_engine_depth_iface) + .push_constant(Type::VEC4, "min_max_uv") + .vertex_source("image_engine_depth_vert.glsl") + .fragment_source("image_engine_depth_frag.glsl") + .additional_info("draw_modelmat") + .do_static_compilation(true); diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index 668a1255843..ccf8f9e0c36 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -191,20 +191,17 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata) sh = OVERLAY_shader_armature_sphere(false); grp = DRW_shgroup_create(sh, armature_ps); - DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f); cb->solid.point_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_point_get()); grp = DRW_shgroup_create(sh, armature_ps); DRW_shgroup_state_disable(grp, DRW_STATE_WRITE_DEPTH); DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); - DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_float_copy(grp, "alpha", wire_alpha * 0.4f); cb->transp.point_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_point_get()); sh = OVERLAY_shader_armature_shape(false); grp = DRW_shgroup_create(sh, armature_ps); - DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f); cb->solid.custom_fill = grp; cb->solid.box_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_box_get()); @@ -213,7 +210,6 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata) grp = DRW_shgroup_create(sh, armature_ps); DRW_shgroup_state_disable(grp, DRW_STATE_WRITE_DEPTH); DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); - DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_float_copy(grp, "alpha", wire_alpha * 0.6f); cb->transp.custom_fill = grp; cb->transp.box_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_box_get()); @@ -335,7 +331,6 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata) sh = OVERLAY_shader_armature_envelope(false); grp = DRW_shgroup_create(sh, armature_ps); DRW_shgroup_state_enable(grp, DRW_STATE_CULL_BACK); - DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_bool_copy(grp, "isDistance", false); DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f); cb->solid.envelope_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_envelope_solid_get()); @@ -371,7 +366,6 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata) sh = OVERLAY_shader_armature_envelope(false); grp = DRW_shgroup_create(sh, armature_transp_ps); - DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f); DRW_shgroup_uniform_bool_copy(grp, "isDistance", true); DRW_shgroup_state_enable(grp, DRW_STATE_CULL_FRONT); @@ -667,7 +661,7 @@ static void drw_shgroup_bone_custom_solid(ArmatureDrawContext *ctx, /* TODO(fclem): arg... less than ideal but we never iter on this object * to assure batch cache is valid. */ - DRW_mesh_batch_cache_validate(mesh); + DRW_mesh_batch_cache_validate(custom, mesh); struct GPUBatch *surf = DRW_mesh_batch_cache_get_surface(mesh); struct GPUBatch *edges = DRW_mesh_batch_cache_get_edge_detection(mesh, NULL); @@ -715,7 +709,7 @@ static void drw_shgroup_bone_custom_wire(ArmatureDrawContext *ctx, } /* TODO(fclem): arg... less than ideal but we never iter on this object * to assure batch cache is valid. */ - DRW_mesh_batch_cache_validate(mesh); + DRW_mesh_batch_cache_validate(custom, mesh); struct GPUBatch *geom = DRW_mesh_batch_cache_get_all_edges(mesh); if (geom) { @@ -2084,7 +2078,9 @@ static void draw_armature_edit(ArmatureDrawContext *ctx) boneflag &= ~BONE_DRAW_LOCKED_WEIGHT; - draw_bone_relations(ctx, eBone, NULL, arm, boneflag, constflag); + if (!is_select) { + draw_bone_relations(ctx, eBone, NULL, arm, boneflag, constflag); + } if (arm->drawtype == ARM_ENVELOPE) { draw_bone_update_disp_matrix_default(eBone, NULL); @@ -2107,12 +2103,14 @@ static void draw_armature_edit(ArmatureDrawContext *ctx) draw_bone_octahedral(ctx, eBone, NULL, arm, boneflag, constflag, select_id); } - if (show_text && (arm->flag & ARM_DRAWNAMES)) { - draw_bone_name(ctx, eBone, NULL, arm, boneflag); - } + if (!is_select) { + if (show_text && (arm->flag & ARM_DRAWNAMES)) { + draw_bone_name(ctx, eBone, NULL, arm, boneflag); + } - if (arm->flag & ARM_DRAWAXES) { - draw_axes(ctx, eBone, NULL, arm); + if (arm->flag & ARM_DRAWAXES) { + draw_axes(ctx, eBone, NULL, arm); + } } } } @@ -2221,7 +2219,9 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) boneflag &= ~BONE_DRAW_LOCKED_WEIGHT; } - draw_bone_relations(ctx, NULL, pchan, arm, boneflag, constflag); + if (!is_pose_select) { + draw_bone_relations(ctx, NULL, pchan, arm, boneflag, constflag); + } if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) { draw_bone_update_disp_matrix_custom(pchan); @@ -2248,16 +2248,19 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) draw_bone_octahedral(ctx, NULL, pchan, arm, boneflag, constflag, select_id); } - if (draw_dofs) { - draw_bone_degrees_of_freedom(ctx, pchan); - } + /* These aren't included in the selection. */ + if (!is_pose_select) { + if (draw_dofs) { + draw_bone_degrees_of_freedom(ctx, pchan); + } - if (show_text && (arm->flag & ARM_DRAWNAMES)) { - draw_bone_name(ctx, NULL, pchan, arm, boneflag); - } + if (show_text && (arm->flag & ARM_DRAWNAMES)) { + draw_bone_name(ctx, NULL, pchan, arm, boneflag); + } - if (arm->flag & ARM_DRAWAXES) { - draw_axes(ctx, NULL, pchan, arm); + if (arm->flag & ARM_DRAWAXES) { + draw_axes(ctx, NULL, pchan, arm); + } } } } @@ -2365,9 +2368,6 @@ static bool POSE_is_driven_by_active_armature(Object *ob) if (ob_arm) { const DRWContextState *draw_ctx = DRW_context_state_get(); bool is_active = OVERLAY_armature_is_pose_mode(ob_arm, draw_ctx); - if (!is_active && ob_arm->proxy_from) { - is_active = OVERLAY_armature_is_pose_mode(ob_arm->proxy_from, draw_ctx); - } return is_active; } diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index 3a2871249a2..ad929cc0835 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -28,6 +28,7 @@ #include "BKE_customdata.h" #include "BKE_editmesh.h" +#include "BKE_object.h" #include "draw_cache_impl.h" #include "draw_manager_text.h" @@ -229,7 +230,10 @@ static void overlay_edit_mesh_add_ob_to_pass(OVERLAY_PrivateData *pd, Object *ob Mesh *me = (Mesh *)ob->data; BMEditMesh *embm = me->edit_mesh; if (embm) { - has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final); + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob); + Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob); + + has_edit_mesh_cage = editmesh_eval_cage && (editmesh_eval_cage != editmesh_eval_final); has_skin_roots = CustomData_get_offset(&embm->bm->vdata, CD_MVERT_SKIN) != -1; } diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c index 983df1ceac8..f51df908fbf 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.c +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c @@ -412,7 +412,7 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata) draw_ctx->view_layer, NULL, &objects_len, draw_ctx->object_mode); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *object_eval = DEG_get_evaluated_object(draw_ctx->depsgraph, objects[ob_index]); - DRW_mesh_batch_cache_validate((Mesh *)object_eval->data); + DRW_mesh_batch_cache_validate(object_eval, (Mesh *)object_eval->data); overlay_edit_uv_cache_populate(vedata, object_eval); } MEM_freeN(objects); @@ -441,22 +441,22 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob) if (has_active_edit_uvmap) { if (pd->edit_uv.do_uv_overlay) { - geom = DRW_mesh_batch_cache_get_edituv_edges(ob->data); + geom = DRW_mesh_batch_cache_get_edituv_edges(ob, ob->data); if (geom) { DRW_shgroup_call_obmat(pd->edit_uv_edges_grp, geom, NULL); } - geom = DRW_mesh_batch_cache_get_edituv_verts(ob->data); + geom = DRW_mesh_batch_cache_get_edituv_verts(ob, ob->data); if (geom) { DRW_shgroup_call_obmat(pd->edit_uv_verts_grp, geom, NULL); } if (pd->edit_uv.do_faces) { - geom = DRW_mesh_batch_cache_get_edituv_faces(ob->data); + geom = DRW_mesh_batch_cache_get_edituv_faces(ob, ob->data); if (geom) { DRW_shgroup_call_obmat(pd->edit_uv_faces_grp, geom, NULL); } } if (pd->edit_uv.do_face_dots) { - geom = DRW_mesh_batch_cache_get_edituv_facedots(ob->data); + geom = DRW_mesh_batch_cache_get_edituv_facedots(ob, ob->data); if (geom) { DRW_shgroup_call_obmat(pd->edit_uv_face_dots_grp, geom, NULL); } @@ -465,14 +465,14 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob) if (pd->edit_uv.do_uv_stretching_overlay) { if (pd->edit_uv.draw_type == SI_UVDT_STRETCH_ANGLE) { - geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(me); + geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(ob, me); } else /* SI_UVDT_STRETCH_AREA */ { OVERLAY_StretchingAreaTotals *totals = MEM_mallocN(sizeof(OVERLAY_StretchingAreaTotals), __func__); BLI_addtail(&pd->edit_uv.totals, totals); geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_area( - me, &totals->total_area, &totals->total_area_uv); + ob, me, &totals->total_area, &totals->total_area_uv); } if (geom) { DRW_shgroup_call_obmat(pd->edit_uv_stretching_grp, geom, NULL); @@ -482,7 +482,7 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob) if (draw_shadows && (has_active_object_uvmap || has_active_edit_uvmap)) { if (pd->edit_uv.do_uv_shadow_overlay) { - geom = DRW_mesh_batch_cache_get_uv_edges(ob->data); + geom = DRW_mesh_batch_cache_get_uv_edges(ob, ob->data); if (geom) { DRW_shgroup_call_obmat(pd->edit_uv_shadow_edges_grp, geom, NULL); } diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 12db2bd02cf..54e8ef80854 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -262,7 +262,7 @@ static bool overlay_object_is_edit_mode(const OVERLAY_PrivateData *pd, const Obj return pd->ctx_mode == CTX_MODE_EDIT_METABALL; case OB_FONT: return pd->ctx_mode == CTX_MODE_EDIT_TEXT; - case OB_HAIR: + case OB_CURVES: case OB_POINTCLOUD: case OB_VOLUME: /* No edit mode yet. */ @@ -316,7 +316,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) OB_MBALL, OB_FONT, OB_GPENCIL, - OB_HAIR, + OB_CURVES, OB_POINTCLOUD, OB_VOLUME); const bool draw_surface = (ob->dt >= OB_WIRE) && (renderable || (ob->dt == OB_WIRE)); diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index a2362cd8850..de0003625a2 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -484,7 +484,7 @@ static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, cons texcosize = mb->size; break; } - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: { /* No user defined texture space support. */ diff --git a/source/blender/draw/engines/overlay/overlay_fade.c b/source/blender/draw/engines/overlay/overlay_fade.c index 0971021f1c0..557a8976ff7 100644 --- a/source/blender/draw/engines/overlay/overlay_fade.c +++ b/source/blender/draw/engines/overlay/overlay_fade.c @@ -43,7 +43,6 @@ void OVERLAY_fade_cache_init(OVERLAY_Data *vedata) GPUShader *sh = OVERLAY_shader_uniform_color(); pd->fade_grp[i] = DRW_shgroup_create(sh, psl->fade_ps[i]); - DRW_shgroup_uniform_block(pd->fade_grp[i], "globalsBlock", G_draw.block_ubo); const DRWContextState *draw_ctx = DRW_context_state_get(); float color[4]; diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index 9899dce5df6..4c09349c35d 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -385,7 +385,7 @@ GPUShader *OVERLAY_shader_armature_sphere(bool use_outline) const DRWContextState *draw_ctx = DRW_context_state_get(); const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; - const char extensions[] = "#extension GL_ARB_conservative_depth : enable\n"; + const char extensions[] = ""; if (use_outline && !sh_data->armature_sphere_outline) { sh_data->armature_sphere_outline = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, diff --git a/source/blender/draw/engines/overlay/overlay_volume.c b/source/blender/draw/engines/overlay/overlay_volume.c index ad0ccf1c7c4..b13351984a3 100644 --- a/source/blender/draw/engines/overlay/overlay_volume.c +++ b/source/blender/draw/engines/overlay/overlay_volume.c @@ -37,7 +37,6 @@ void OVERLAY_volume_cache_init(OVERLAY_Data *vedata) GPUShader *sh = OVERLAY_shader_depth_only(); DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->volume_ps); pd->volume_selection_surface_grp = grp; - DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); } else { psl->volume_ps = NULL; diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index 449130c4c5b..1eb8fc981cf 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -185,10 +185,11 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, Mesh *me = ob->data; if (is_edit_mode) { BLI_assert(me->edit_mesh); - BMEditMesh *embm = me->edit_mesh; - has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final); - if (embm->mesh_eval_final) { - me = embm->mesh_eval_final; + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob); + Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob); + has_edit_mesh_cage = editmesh_eval_cage && (editmesh_eval_cage != editmesh_eval_final); + if (editmesh_eval_final) { + me = editmesh_eval_final; } } is_mesh_verts_only = me->totedge == 0 && me->totvert > 0; diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl index f7792dc0371..8f90c1acbbb 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl @@ -40,7 +40,7 @@ void main() } else if (lineStyle == OVERLAY_UV_LINE_STYLE_DASH) { if (fract(line_distance / dashLength) < 0.5) { - inner_color = mix(vec4(1.0), colorEdgeSelect, selectionFac_f); + inner_color = mix(vec4(vec3(0.35), 1.0), colorEdgeSelect, selectionFac_f); } } else if (lineStyle == OVERLAY_UV_LINE_STYLE_BLACK) { diff --git a/source/blender/draw/engines/select/select_draw_utils.c b/source/blender/draw/engines/select/select_draw_utils.c index e9930dbdb30..2801f2d7720 100644 --- a/source/blender/draw/engines/select/select_draw_utils.c +++ b/source/blender/draw/engines/select/select_draw_utils.c @@ -49,7 +49,7 @@ void select_id_object_min_max(Object *obj, float r_min[3], float r_max[3]) BoundBox *bb; BMEditMesh *em = BKE_editmesh_from_object(obj); if (em) { - bb = BKE_editmesh_cage_boundbox_get(em); + bb = BKE_editmesh_cage_boundbox_get(obj, em); } else { bb = BKE_object_boundbox_get(obj); diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_composite_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_composite_info.hh new file mode 100644 index 00000000000..e93f241ad3c --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_composite_info.hh @@ -0,0 +1,41 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Base Composite + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_composite) + .sampler(0, ImageType::FLOAT_2D, "normalBuffer", Frequency::PASS) + .sampler(1, ImageType::FLOAT_2D, "materialBuffer", Frequency::PASS) + .uniform_buf(4, "WorldData", "world_data", Frequency::PASS) + .push_constant(Type::BOOL, "forceShadowing") + .fragment_out(0, Type::VEC4, "fragColor") + .typedef_source("workbench_shader_shared.h") + .fragment_source("workbench_composite_frag.glsl") + .additional_info("draw_fullscreen", "draw_view"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lighting Type + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_composite_studio) + .define("V3D_LIGHTING_STUDIO") + .additional_info("workbench_composite") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(workbench_composite_matcap) + .define("V3D_LIGHTING_MATCAP") + .sampler(2, ImageType::FLOAT_2D, "matcap_diffuse_tx", Frequency::PASS) + .sampler(3, ImageType::FLOAT_2D, "matcap_specular_tx", Frequency::PASS) + .additional_info("workbench_composite") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(workbench_composite_flat) + .define("V3D_LIGHTING_FLAT") + .additional_info("workbench_composite") + .do_static_compilation(true); + +/** \} */ diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh new file mode 100644 index 00000000000..3e0124546b0 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh @@ -0,0 +1,64 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name TAA + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_taa) + .sampler(0, ImageType::FLOAT_2D, "colorBuffer") + .push_constant(Type::FLOAT, "samplesWeights", 9) + .fragment_out(0, Type::VEC4, "fragColor") + .fragment_source("workbench_effect_taa_frag.glsl") + .additional_info("draw_fullscreen") + .do_static_compilation(true); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name SMAA + * \{ */ + +GPU_SHADER_INTERFACE_INFO(workbench_smaa_iface, "") + .smooth(Type::VEC2, "uvs") + .smooth(Type::VEC2, "pixcoord") + .smooth(Type::VEC4, "offset[3]"); + +GPU_SHADER_CREATE_INFO(workbench_smaa) + .define("SMAA_GLSL_3") + .define("SMAA_RT_METRICS", "viewportMetrics") + .define("SMAA_PRESET_HIGH") + .define("SMAA_LUMA_WEIGHT", "float4(1.0, 1.0, 1.0, 1.0)") + .define("SMAA_NO_DISCARD") + .vertex_out(workbench_smaa_iface) + .push_constant(Type::VEC4, "viewportMetrics") + .vertex_source("workbench_effect_smaa_vert.glsl") + .fragment_source("workbench_effect_smaa_frag.glsl"); + +GPU_SHADER_CREATE_INFO(workbench_smaa_stage_0) + .define("SMAA_STAGE", "0") + .sampler(0, ImageType::FLOAT_2D, "colorTex") + .fragment_out(0, Type::VEC2, "out_edges") + .additional_info("workbench_smaa") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(workbench_smaa_stage_1) + .define("SMAA_STAGE", "1") + .sampler(0, ImageType::FLOAT_2D, "edgesTex") + .sampler(1, ImageType::FLOAT_2D, "areaTex") + .sampler(2, ImageType::FLOAT_2D, "searchTex") + .fragment_out(0, Type::VEC4, "out_weights") + .additional_info("workbench_smaa") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(workbench_smaa_stage_2) + .define("SMAA_STAGE", "2") + .sampler(0, ImageType::FLOAT_2D, "colorTex") + .sampler(1, ImageType::FLOAT_2D, "blendTex") + .push_constant(Type::FLOAT, "mixFactor") + .push_constant(Type::FLOAT, "taaAccumulatedWeight") + .fragment_out(0, Type::VEC4, "out_color") + .additional_info("workbench_smaa") + .do_static_compilation(true); + +/** \} */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh index 31e5f5e7641..29468d6002a 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_info.hh +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh @@ -3,10 +3,9 @@ GPU_SHADER_CREATE_INFO(workbench_effect_cavity_common) .fragment_out(0, Type::VEC4, "fragColor") - .sampler(0, ImageType::FLOAT_2D, "depthBuffer") - .sampler(1, ImageType::FLOAT_2D, "normalBuffer") - .sampler(2, ImageType::UINT_2D, "objectIdBuffer") - .uniform_buf(3, "vec4", "samples_coords[512]") + .sampler(0, ImageType::FLOAT_2D, "normalBuffer") + .uniform_buf(4, "WorldData", "world_data", Frequency::PASS) + .typedef_source("workbench_shader_shared.h") .fragment_source("workbench_effect_cavity_frag.glsl") .additional_info("draw_fullscreen") .additional_info("draw_view"); @@ -14,15 +13,23 @@ GPU_SHADER_CREATE_INFO(workbench_effect_cavity_common) GPU_SHADER_CREATE_INFO(workbench_effect_cavity) .do_static_compilation(true) .define("USE_CAVITY") + .uniform_buf(3, "vec4", "samples_coords[512]") + .sampler(1, ImageType::DEPTH_2D, "depthBuffer") + .sampler(2, ImageType::FLOAT_2D, "cavityJitter") .additional_info("workbench_effect_cavity_common"); GPU_SHADER_CREATE_INFO(workbench_effect_curvature) .do_static_compilation(true) .define("USE_CURVATURE") + .sampler(1, ImageType::UINT_2D, "objectIdBuffer") .additional_info("workbench_effect_cavity_common"); GPU_SHADER_CREATE_INFO(workbench_effect_cavity_curvature) .do_static_compilation(true) .define("USE_CAVITY") .define("USE_CURVATURE") + .uniform_buf(3, "vec4", "samples_coords[512]") + .sampler(1, ImageType::DEPTH_2D, "depthBuffer") + .sampler(2, ImageType::FLOAT_2D, "cavityJitter") + .sampler(3, ImageType::UINT_2D, "objectIdBuffer") .additional_info("workbench_effect_cavity_common"); diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh new file mode 100644 index 00000000000..252a7d4f3a3 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh @@ -0,0 +1,55 @@ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(workbench_effect_dof) + /* TODO(fclem): Split resources per stage. */ + .sampler(0, ImageType::FLOAT_2D, "inputCocTex") + .sampler(1, ImageType::FLOAT_2D, "maxCocTilesTex") + .sampler(2, ImageType::FLOAT_2D, "sceneColorTex") + .sampler(3, ImageType::FLOAT_2D, "sceneDepthTex") + .sampler(4, ImageType::FLOAT_2D, "backgroundTex") + .sampler(5, ImageType::FLOAT_2D, "halfResColorTex") + .sampler(6, ImageType::FLOAT_2D, "blurTex") + .sampler(7, ImageType::FLOAT_2D, "noiseTex") + .push_constant(Type::VEC2, "invertedViewportSize") + .push_constant(Type::VEC2, "nearFar") + .push_constant(Type::VEC3, "dofParams") + .push_constant(Type::FLOAT, "noiseOffset") + .fragment_source("workbench_effect_dof_frag.glsl") + .additional_info("draw_fullscreen") + .additional_info("draw_view"); + +GPU_SHADER_CREATE_INFO(workbench_effect_dof_prepare) + .define("PREPARE") + .fragment_out(0, Type::VEC4, "halfResColor") + .fragment_out(1, Type::VEC2, "normalizedCoc") + .additional_info("workbench_effect_dof") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(workbench_effect_dof_downsample) + .define("DOWNSAMPLE") + .fragment_out(0, Type::VEC4, "outColor") + .fragment_out(1, Type::VEC2, "outCocs") + .additional_info("workbench_effect_dof") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(workbench_effect_dof_blur1) + .define("BLUR1") + .define("NUM_SAMPLES", "49") + .uniform_buf(1, "vec4", "samples[49]") + .fragment_out(0, Type::VEC4, "blurColor") + .additional_info("workbench_effect_dof") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(workbench_effect_dof_blur2) + .define("BLUR2") + .fragment_out(0, Type::VEC4, "finalColor") + .additional_info("workbench_effect_dof") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(workbench_effect_dof_resolve) + .define("RESOLVE") + .fragment_out(0, Type::VEC4, "finalColorAdd", DualBlend::SRC_0) + .fragment_out(0, Type::VEC4, "finalColorMul", DualBlend::SRC_1) + .additional_info("workbench_effect_dof") + .do_static_compilation(true);
\ No newline at end of file diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh new file mode 100644 index 00000000000..3849fe57a25 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh @@ -0,0 +1,11 @@ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(workbench_effect_outline) + .typedef_source("workbench_shader_shared.h") + .fragment_source("workbench_effect_outline_frag.glsl") + .sampler(0, ImageType::UINT_2D, "objectIdBuffer") + .uniform_buf(4, "WorldData", "world_data", Frequency::PASS) + .fragment_out(0, Type::VEC4, "fragColor") + .additional_info("draw_fullscreen") + .do_static_compilation(true); diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh new file mode 100644 index 00000000000..78403c292f0 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh @@ -0,0 +1,9 @@ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(workbench_merge_infront) + .fragment_out(0, Type::VEC4, "fragColor") + .sampler(0, ImageType::DEPTH_2D, "depthBuffer") + .fragment_source("workbench_merge_infront_frag.glsl") + .additional_info("draw_fullscreen") + .do_static_compilation(true); diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh new file mode 100644 index 00000000000..29eadc8048a --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh @@ -0,0 +1,149 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Object Type + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_mesh) + .vertex_in(0, Type::VEC3, "pos") + .vertex_in(1, Type::VEC3, "nor") + .vertex_in(2, Type::VEC4, "ac") + .vertex_in(3, Type::VEC2, "au") + .vertex_source("workbench_prepass_vert.glsl") + .additional_info("draw_mesh") + .additional_info("draw_resource_handle"); + +GPU_SHADER_CREATE_INFO(workbench_hair) + .sampler(0, ImageType::FLOAT_BUFFER, "ac", Frequency::BATCH) + .sampler(1, ImageType::FLOAT_BUFFER, "au", Frequency::BATCH) + .vertex_source("workbench_prepass_hair_vert.glsl") + .additional_info("draw_hair") + .additional_info("draw_resource_handle"); + +GPU_SHADER_CREATE_INFO(workbench_pointcloud) + .vertex_source("workbench_prepass_pointcloud_vert.glsl") + .additional_info("draw_pointcloud") + .additional_info("draw_resource_handle"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture Type + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_texture_none).define("TEXTURE_NONE"); + +GPU_SHADER_CREATE_INFO(workbench_texture_single) + .sampler(2, ImageType::FLOAT_2D, "imageTexture", Frequency::BATCH) + .push_constant(Type::BOOL, "imagePremult") + .push_constant(Type::FLOAT, "imageTransparencyCutoff") + .define("V3D_SHADING_TEXTURE_COLOR"); + +GPU_SHADER_CREATE_INFO(workbench_texture_tile) + .sampler(2, ImageType::FLOAT_2D_ARRAY, "imageTileArray", Frequency::BATCH) + .sampler(3, ImageType::FLOAT_1D_ARRAY, "imageTileData", Frequency::BATCH) + .push_constant(Type::BOOL, "imagePremult") + .push_constant(Type::FLOAT, "imageTransparencyCutoff") + .define("V3D_SHADING_TEXTURE_COLOR") + .define("TEXTURE_IMAGE_ARRAY"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lighting Type (only for transparent) + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_lighting_flat).define("V3D_LIGHTING_FLAT"); +GPU_SHADER_CREATE_INFO(workbench_lighting_studio).define("V3D_LIGHTING_STUDIO"); +GPU_SHADER_CREATE_INFO(workbench_lighting_matcap) + .define("V3D_LIGHTING_MATCAP") + .sampler(4, ImageType::FLOAT_2D, "matcap_diffuse_tx", Frequency::PASS) + .sampler(5, ImageType::FLOAT_2D, "matcap_specular_tx", Frequency::PASS); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Interface + * \{ */ + +GPU_SHADER_INTERFACE_INFO(workbench_material_iface, "") + .smooth(Type::VEC3, "normal_interp") + .smooth(Type::VEC3, "color_interp") + .smooth(Type::FLOAT, "alpha_interp") + .smooth(Type::VEC2, "uv_interp") + .flat(Type::INT, "object_id") + .flat(Type::FLOAT, "roughness") + .flat(Type::FLOAT, "metallic"); + +GPU_SHADER_CREATE_INFO(workbench_material) + .uniform_buf(4, "WorldData", "world_data", Frequency::PASS) + .uniform_buf(5, "vec4", "materials_data[4096]", Frequency::PASS) + .push_constant(Type::INT, "materialIndex") + .push_constant(Type::BOOL, "useMatcap") + .vertex_out(workbench_material_iface); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pipeline Type + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_transparent_accum) + /* Note: Blending will be skipped on objectId because output is a + non-normalized integer buffer. */ + .fragment_out(0, Type::VEC4, "transparentAccum") + .fragment_out(1, Type::VEC4, "revealageAccum") + .fragment_out(2, Type::UINT, "objectId") + .push_constant(Type::BOOL, "forceShadowing") + .typedef_source("workbench_shader_shared.h") + .fragment_source("workbench_transparent_accum_frag.glsl"); + +GPU_SHADER_CREATE_INFO(workbench_opaque) + .fragment_out(0, Type::VEC4, "materialData") + .fragment_out(1, Type::VEC2, "normalData") + .fragment_out(2, Type::UINT, "objectId") + .typedef_source("workbench_shader_shared.h") + .fragment_source("workbench_prepass_frag.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Variations Declaration + * \{ */ + +#define WORKBENCH_FINAL_VARIATION(name, ...) \ + GPU_SHADER_CREATE_INFO(name).additional_info(__VA_ARGS__).do_static_compilation(true); + +#define WORKBENCH_CLIPPING_VARIATIONS(prefix, ...) \ + WORKBENCH_FINAL_VARIATION(prefix##_clip, "drw_clipped", __VA_ARGS__) \ + WORKBENCH_FINAL_VARIATION(prefix##_no_clip, __VA_ARGS__) + +#define WORKBENCH_TEXTURE_VARIATIONS(prefix, ...) \ + WORKBENCH_CLIPPING_VARIATIONS(prefix##_tex_none, "workbench_texture_none", __VA_ARGS__) \ + WORKBENCH_CLIPPING_VARIATIONS(prefix##_tex_single, "workbench_texture_single", __VA_ARGS__) \ + WORKBENCH_CLIPPING_VARIATIONS(prefix##_tex_tile, "workbench_texture_tile", __VA_ARGS__) + +#define WORKBENCH_DATATYPE_VARIATIONS(prefix, ...) \ + WORKBENCH_TEXTURE_VARIATIONS(prefix##_mesh, "workbench_mesh", __VA_ARGS__) \ + WORKBENCH_TEXTURE_VARIATIONS(prefix##_hair, "workbench_hair", __VA_ARGS__) \ + WORKBENCH_TEXTURE_VARIATIONS(prefix##_ptcloud, "workbench_pointcloud", __VA_ARGS__) + +#define WORKBENCH_PIPELINE_VARIATIONS(prefix, ...) \ + WORKBENCH_DATATYPE_VARIATIONS(prefix##_transp_studio, \ + "workbench_transparent_accum", \ + "workbench_lighting_studio", \ + __VA_ARGS__) \ + WORKBENCH_DATATYPE_VARIATIONS(prefix##_transp_matcap, \ + "workbench_transparent_accum", \ + "workbench_lighting_matcap", \ + __VA_ARGS__) \ + WORKBENCH_DATATYPE_VARIATIONS(prefix##_transp_flat, \ + "workbench_transparent_accum", \ + "workbench_lighting_flat", \ + __VA_ARGS__) \ + WORKBENCH_DATATYPE_VARIATIONS(prefix##_opaque, "workbench_opaque", __VA_ARGS__) + +WORKBENCH_PIPELINE_VARIATIONS(workbench, "workbench_material"); + +/** \} */ diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_shadow_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_shadow_info.hh new file mode 100644 index 00000000000..c26d3c3aaf8 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_shadow_info.hh @@ -0,0 +1,98 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Common + * \{ */ + +GPU_SHADER_INTERFACE_INFO(workbench_shadow_iface, "vData") + .smooth(Type::VEC3, "pos") + .smooth(Type::VEC4, "frontPosition") + .smooth(Type::VEC4, "backPosition"); + +GPU_SHADER_CREATE_INFO(workbench_shadow_common) + .vertex_in(0, Type::VEC3, "pos") + .vertex_out(workbench_shadow_iface) + .push_constant(Type::FLOAT, "lightDistance") + .push_constant(Type::VEC3, "lightDirection") + .vertex_source("workbench_shadow_vert.glsl") + .additional_info("draw_mesh"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Manifold Type + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_shadow_manifold) + .geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::TRIANGLE_STRIP, 4, 1) + .geometry_source("workbench_shadow_geom.glsl"); + +GPU_SHADER_CREATE_INFO(workbench_shadow_no_manifold) + .geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::TRIANGLE_STRIP, 4, 2) + .geometry_source("workbench_shadow_geom.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Caps Type + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_shadow_caps) + .geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3, 2) + .geometry_source("workbench_shadow_caps_geom.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug Type + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_shadow_no_debug) + .fragment_source("gpu_shader_depth_only_frag.glsl"); + +GPU_SHADER_CREATE_INFO(workbench_shadow_debug) + .fragment_out(0, Type::VEC4, "materialData") + .fragment_out(1, Type::VEC4, "normalData") + .fragment_out(2, Type::UINT, "objectId") + .fragment_source("workbench_shadow_debug_frag.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Variations Declaration + * \{ */ + +#define WORKBENCH_SHADOW_VARIATIONS(suffix, ...) \ + GPU_SHADER_CREATE_INFO(workbench_shadow_pass_manifold_no_caps##suffix) \ + .define("SHADOW_PASS") \ + .additional_info("workbench_shadow_common", "workbench_shadow_manifold", __VA_ARGS__) \ + .do_static_compilation(true); \ + GPU_SHADER_CREATE_INFO(workbench_shadow_pass_no_manifold_no_caps##suffix) \ + .define("SHADOW_PASS") \ + .define("DOUBLE_MANIFOLD") \ + .additional_info("workbench_shadow_common", "workbench_shadow_no_manifold", __VA_ARGS__) \ + .do_static_compilation(true); \ + GPU_SHADER_CREATE_INFO(workbench_shadow_fail_manifold_caps##suffix) \ + .define("SHADOW_FAIL") \ + .additional_info("workbench_shadow_common", "workbench_shadow_caps", __VA_ARGS__) \ + .do_static_compilation(true); \ + GPU_SHADER_CREATE_INFO(workbench_shadow_fail_manifold_no_caps##suffix) \ + .define("SHADOW_FAIL") \ + .additional_info("workbench_shadow_common", "workbench_shadow_manifold", __VA_ARGS__) \ + .do_static_compilation(true); \ + GPU_SHADER_CREATE_INFO(workbench_shadow_fail_no_manifold_caps##suffix) \ + .define("SHADOW_FAIL") \ + .define("DOUBLE_MANIFOLD") \ + .additional_info("workbench_shadow_common", "workbench_shadow_caps", __VA_ARGS__) \ + .do_static_compilation(true); \ + GPU_SHADER_CREATE_INFO(workbench_shadow_fail_no_manifold_no_caps##suffix) \ + .define("SHADOW_FAIL") \ + .define("DOUBLE_MANIFOLD") \ + .additional_info("workbench_shadow_common", "workbench_shadow_no_manifold", __VA_ARGS__) \ + .do_static_compilation(true); + +WORKBENCH_SHADOW_VARIATIONS(, "workbench_shadow_no_debug") +WORKBENCH_SHADOW_VARIATIONS(_debug, "workbench_shadow_debug") + +/** \} */ diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh new file mode 100644 index 00000000000..e5b7bc8e2a7 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh @@ -0,0 +1,10 @@ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(workbench_transparent_resolve) + .fragment_out(0, Type::VEC4, "fragColor") + .sampler(0, ImageType::FLOAT_2D, "transparentAccum") + .sampler(1, ImageType::FLOAT_2D, "transparentRevealage") + .fragment_source("workbench_transparent_resolve_frag.glsl") + .additional_info("draw_fullscreen") + .do_static_compilation(true); diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh new file mode 100644 index 00000000000..dd9492481ec --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh @@ -0,0 +1,114 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Volume shader base + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_volume) + .vertex_in(0, Type::VEC3, "pos") + .fragment_out(0, Type::VEC4, "fragColor") + .sampler(0, ImageType::DEPTH_2D, "depthBuffer") + .sampler(1, ImageType::FLOAT_3D, "densityTexture") + .push_constant(Type::INT, "samplesLen") + .push_constant(Type::FLOAT, "noiseOfs") + .push_constant(Type::FLOAT, "stepLength") + .push_constant(Type::FLOAT, "densityScale") + .vertex_source("workbench_volume_vert.glsl") + .fragment_source("workbench_volume_frag.glsl") + .additional_info("draw_object_infos"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Smoke variation + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_volume_smoke) + .define("VOLUME_SMOKE") + .sampler(2, ImageType::FLOAT_3D, "flameTexture") + .sampler(3, ImageType::FLOAT_1D, "flameColorTexture") + .additional_info("draw_mesh", "draw_resource_id_varying"); + +GPU_SHADER_CREATE_INFO(workbench_volume_object) + .define("VOLUME_OBJECT") + .push_constant(Type::MAT4, "volumeTextureToObject") + /* FIXME(fclem): This overflow the push_constant limit. */ + .push_constant(Type::MAT4, "volumeObjectToTexture") + .additional_info("draw_volume", "draw_resource_id_varying"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Band variation + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_volume_coba) + .define("USE_COBA") + .sampler(4, ImageType::UINT_3D, "flagTexture") + .sampler(5, ImageType::FLOAT_1D, "transferTexture") + .push_constant(Type::BOOL, "showPhi") + .push_constant(Type::BOOL, "showFlags") + .push_constant(Type::BOOL, "showPressure") + .push_constant(Type::FLOAT, "gridScale"); + +GPU_SHADER_CREATE_INFO(workbench_volume_no_coba) + .sampler(4, ImageType::FLOAT_3D, "shadowTexture") + .sampler(5, ImageType::UINT_2D, "transferTexture") + .push_constant(Type::VEC3, "activeColor"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sampling variation + * \{ */ + +GPU_SHADER_CREATE_INFO(workbench_volume_linear).define("USE_TRILINEAR"); +GPU_SHADER_CREATE_INFO(workbench_volume_cubic).define("USE_TRICUBIC"); +GPU_SHADER_CREATE_INFO(workbench_volume_closest).define("USE_CLOSEST"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Slice variation + * \{ */ + +GPU_SHADER_INTERFACE_INFO(workbench_volume_iface, "").smooth(Type::VEC3, "localPos"); + +GPU_SHADER_CREATE_INFO(workbench_volume_slice) + .define("VOLUME_SLICE") + .vertex_in(1, Type::VEC3, "uvs") + .vertex_out(workbench_volume_iface) + .push_constant(Type::INT, "sliceAxis") /* -1 is no slice. */ + .push_constant(Type::FLOAT, "slicePosition"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Variations Declaration + * \{ */ + +#define WORKBENCH_VOLUME_SLICE_VARIATIONS(prefix, ...) \ + GPU_SHADER_CREATE_INFO(prefix##_slice) \ + .additional_info("workbench_volume_slice", __VA_ARGS__) \ + .do_static_compilation(true); \ + GPU_SHADER_CREATE_INFO(prefix##_no_slice) \ + .additional_info(__VA_ARGS__) \ + .do_static_compilation(true); + +#define WORKBENCH_VOLUME_COBA_VARIATIONS(prefix, ...) \ + WORKBENCH_VOLUME_SLICE_VARIATIONS(prefix##_coba, "workbench_volume_coba", __VA_ARGS__) \ + WORKBENCH_VOLUME_SLICE_VARIATIONS(prefix##_no_coba, "workbench_volume_no_coba", __VA_ARGS__) + +#define WORKBENCH_VOLUME_INTERP_VARIATIONS(prefix, ...) \ + WORKBENCH_VOLUME_COBA_VARIATIONS(prefix##_linear, "workbench_volume_linear", __VA_ARGS__) \ + WORKBENCH_VOLUME_COBA_VARIATIONS(prefix##_cubic, "workbench_volume_cubic", __VA_ARGS__) \ + WORKBENCH_VOLUME_COBA_VARIATIONS(prefix##_closest, "workbench_volume_closest", __VA_ARGS__) + +#define WORKBENCH_VOLUME_SMOKE_VARIATIONS(prefix, ...) \ + WORKBENCH_VOLUME_INTERP_VARIATIONS(prefix##_smoke, "workbench_volume_smoke", __VA_ARGS__) \ + WORKBENCH_VOLUME_INTERP_VARIATIONS(prefix##_object, "workbench_volume_object", __VA_ARGS__) + +WORKBENCH_VOLUME_SMOKE_VARIATIONS(workbench_volume, "workbench_volume") + +/** \} */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl index 9038aae533b..880f17b0c9d 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl @@ -1,18 +1,12 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) #pragma BLENDER_REQUIRE(workbench_common_lib.glsl) -layout(std140) uniform samples_block -{ - vec4 samples_coords[512]; -}; - -uniform sampler2D cavityJitter; - /* From The Alchemy screen-space ambient obscurance algorithm * http://graphics.cs.williams.edu/papers/AlchemyHPG11/VV11AlchemyAO.pdf */ +#ifdef USE_CAVITY + void cavity_compute(vec2 screenco, sampler2D depthBuffer, sampler2D normalBuffer, @@ -98,3 +92,5 @@ void cavity_compute(vec2 screenco, cavities = clamp(cavities * world_data.cavity_valley_factor, 0.0, 1.0); edges = edges * world_data.cavity_ridge_factor; } + +#endif /* USE_CAVITY */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl index c5b2ce0fd99..5e43fe27f38 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl @@ -4,13 +4,6 @@ #pragma BLENDER_REQUIRE(workbench_matcap_lib.glsl) #pragma BLENDER_REQUIRE(workbench_world_light_lib.glsl) -uniform sampler2D materialBuffer; -uniform sampler2D normalBuffer; - -in vec4 uvcoordsvar; - -out vec4 fragColor; - void main() { /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ @@ -27,7 +20,7 @@ void main() /* When using matcaps, mat_data.a is the back-face sign. */ N = (mat_data.a > 0.0) ? N : -N; - fragColor.rgb = get_matcap_lighting(base_color, N, I); + fragColor.rgb = get_matcap_lighting(matcap_diffuse_tx, matcap_specular_tx, base_color, N, I); #endif #ifdef V3D_LIGHTING_STUDIO @@ -38,7 +31,7 @@ void main() fragColor.rgb = base_color; #endif - fragColor.rgb *= get_shadow(N); + fragColor.rgb *= get_shadow(N, forceShadowing); fragColor.a = 1.0; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl index a4d81393dbc..a6f7a1f522a 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl @@ -1,5 +1,7 @@ -#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) + +#ifdef USE_CURVATURE float curvature_soft_clamp(float curvature, float control) { @@ -45,3 +47,5 @@ void curvature_compute(vec2 uv, curvature = 2.0 * curvature_soft_clamp(normal_diff, world_data.curvature_ridge); } } + +#endif /* USE_CURVATURE */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl deleted file mode 100644 index c784c8b2db9..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl +++ /dev/null @@ -1,48 +0,0 @@ - -#ifndef WORKBENCH_SHADER_SHARED_H -struct LightData { - vec4 direction; - vec4 specular_color; - vec4 diffuse_color_wrap; /* rgb: diffuse col a: wrapped lighting factor */ -}; - -struct WorldData { - vec4 viewport_size; - vec4 object_outline_color; - vec4 shadow_direction_vs; - float shadow_focus; - float shadow_shift; - float shadow_mul; - float shadow_add; - /* - 16 bytes alignment - */ - LightData lights[4]; - vec4 ambient_color; - - int cavity_sample_start; - int cavity_sample_end; - float cavity_sample_count_inv; - float cavity_jitter_scale; - - float cavity_valley_factor; - float cavity_ridge_factor; - float cavity_attenuation; - float cavity_distance; - - float curvature_ridge; - float curvature_valley; - float ui_scale; - float _pad0; - - int matcap_orientation; - bool use_specular; - int _pad1; - int _pad2; -}; - -# define viewport_size_inv viewport_size.zw - -layout(std140) uniform world_block -{ - WorldData world_data; -}; -#endif diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl index 0b571040df5..59222b588a0 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl @@ -4,18 +4,6 @@ #pragma BLENDER_REQUIRE(workbench_cavity_lib.glsl) #pragma BLENDER_REQUIRE(workbench_curvature_lib.glsl) -#ifndef DRW_SHADER_SHARED_H - -uniform sampler2D depthBuffer; -uniform sampler2D normalBuffer; -uniform usampler2D objectIdBuffer; - -in vec4 uvcoordsvar; - -out vec4 fragColor; - -#endif - void main() { float cavity = 0.0, edges = 0.0, curvature = 0.0; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl index c4580e6ffc3..e9525ce9de0 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl @@ -7,19 +7,6 @@ * Converted and adapted from HLSL to GLSL by Clément Foucault */ -uniform vec2 invertedViewportSize; -uniform vec2 nearFar; -uniform vec3 dofParams; -uniform float noiseOffset; -uniform sampler2D inputCocTex; -uniform sampler2D maxCocTilesTex; -uniform sampler2D sceneColorTex; -uniform sampler2D sceneDepthTex; -uniform sampler2D backgroundTex; -uniform sampler2D halfResColorTex; -uniform sampler2D blurTex; -uniform sampler2D noiseTex; - #define dof_aperturesize dofParams.x #define dof_distance dofParams.y #define dof_invsensorsize dofParams.z @@ -53,9 +40,6 @@ float decode_signed_coc(vec2 cocs) */ #ifdef PREPARE -layout(location = 0) out vec4 halfResColor; -layout(location = 1) out vec2 normalizedCoc; - void main() { ivec4 texel = ivec4(gl_FragCoord.xyxy) * 2 + ivec4(0, 0, 1, 1); @@ -99,9 +83,6 @@ void main() */ #ifdef DOWNSAMPLE -layout(location = 0) out vec4 outColor; -layout(location = 1) out vec2 outCocs; - void main() { vec4 texel = vec4(gl_FragCoord.xyxy) * 2.0 + vec4(0.0, 0.0, 1.0, 1.0); @@ -216,14 +197,6 @@ void main() * Outputs vertical blur and combined blur in MRT */ #ifdef BLUR1 -layout(location = 0) out vec4 blurColor; - -# define NUM_SAMPLES 49 - -layout(std140) uniform dofSamplesBlock -{ - vec4 samples[NUM_SAMPLES]; -}; vec2 get_random_vector(float offset) { @@ -308,7 +281,6 @@ void main() * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef BLUR2 -out vec4 finalColor; void main() { @@ -385,9 +357,6 @@ void main() */ #ifdef RESOLVE -layout(location = 0, index = 0) out vec4 finalColorAdd; -layout(location = 0, index = 1) out vec4 finalColorMul; - void main() { /* Fullscreen pass */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl index fb6fdb93462..5d74933abf4 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl @@ -1,12 +1,4 @@ -#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) - -uniform usampler2D objectIdBuffer; - -in vec4 uvcoordsvar; - -out vec4 fragColor; - void main() { vec3 offset = vec3(world_data.viewport_size_inv, 0.0) * world_data.ui_scale; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl index 9797a5e3301..8b9e3f968ea 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl @@ -1,50 +1,35 @@ -uniform sampler2D edgesTex; -uniform sampler2D areaTex; -uniform sampler2D searchTex; -uniform sampler2D blendTex; -uniform sampler2D colorTex; -uniform float mixFactor; -uniform float taaAccumulatedWeight; - -in vec2 uvs; -in vec2 pixcoord; -in vec4 offset[3]; - -#if SMAA_STAGE == 0 -out vec2 fragColor; -#else -out vec4 fragColor; -#endif +#pragma BLENDER_REQUIRE(common_smaa_lib.glsl) void main() { #if SMAA_STAGE == 0 /* Detect edges in color and revealage buffer. */ - fragColor = SMAALumaEdgeDetectionPS(uvs, offset, colorTex); + out_edges = SMAALumaEdgeDetectionPS(uvs, offset, colorTex); /* Discard if there is no edge. */ - if (dot(fragColor, float2(1.0, 1.0)) == 0.0) { + if (dot(out_edges, float2(1.0, 1.0)) == 0.0) { discard; } #elif SMAA_STAGE == 1 - fragColor = SMAABlendingWeightCalculationPS( + out_weights = SMAABlendingWeightCalculationPS( uvs, pixcoord, offset, edgesTex, areaTex, searchTex, vec4(0)); #elif SMAA_STAGE == 2 - fragColor = vec4(0.0); + out_color = vec4(0.0); if (mixFactor > 0.0) { - fragColor += SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex) * mixFactor; + out_color += SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex) * mixFactor; } if (mixFactor < 1.0) { - fragColor += texture(colorTex, uvs) * (1.0 - mixFactor); + out_color += texture(colorTex, uvs) * (1.0 - mixFactor); } - fragColor /= taaAccumulatedWeight; - fragColor = exp2(fragColor) - 0.5; + out_color /= taaAccumulatedWeight; + /* Exit log2 space used for Antialiasing. */ + out_color = exp2(out_color) - 0.5; /* Avoid float precision issue. */ - if (fragColor.a > 0.999) { - fragColor.a = 1.0; + if (out_color.a > 0.999) { + out_color.a = 1.0; } #endif } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl index 07734d19972..b76433a23e5 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl @@ -1,7 +1,5 @@ -out vec2 uvs; -out vec2 pixcoord; -out vec4 offset[3]; +#pragma BLENDER_REQUIRE(common_smaa_lib.glsl) void main() { diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl index d021e4696f7..0c4dee11756 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl @@ -1,9 +1,4 @@ -uniform sampler2D colorBuffer; -uniform float samplesWeights[9]; - -out vec4 fragColor; - void main() { vec2 texel_size = 1.0 / vec2(textureSize(colorBuffer, 0)); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl index 49e3f57ab2e..78782bdc777 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl @@ -25,15 +25,6 @@ bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) return true; } -#ifndef WORKBENCH_SHADER_SHARED_H -uniform sampler2DArray imageTileArray; -uniform sampler1DArray imageTileData; -uniform sampler2D imageTexture; - -uniform float imageTransparencyCutoff = 0.1; -uniform bool imagePremult; -#endif - vec3 workbench_image_color(vec2 uvs) { #ifdef V3D_SHADING_TEXTURE_COLOR diff --git a/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl index 2d18cc1b014..a0cec54251d 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl @@ -1,6 +1,4 @@ -#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) - vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) { /* Quick creation of an orthonormal basis */ @@ -15,16 +13,14 @@ vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) return matcap_uv * 0.496 + 0.5; } -uniform sampler2D matcapDiffuseImage; -uniform sampler2D matcapSpecularImage; - -vec3 get_matcap_lighting(vec3 base_color, vec3 N, vec3 I) +vec3 get_matcap_lighting( + sampler2D diffuse_matcap, sampler2D specular_matcap, vec3 base_color, vec3 N, vec3 I) { bool flipped = world_data.matcap_orientation != 0; vec2 uv = matcap_uv_compute(I, N, flipped); - vec3 diffuse = textureLod(matcapDiffuseImage, uv, 0.0).rgb; - vec3 specular = textureLod(matcapSpecularImage, uv, 0.0).rgb; + vec3 diffuse = textureLod(diffuse_matcap, uv, 0.0).rgb; + vec3 specular = textureLod(specular_matcap, uv, 0.0).rgb; return diffuse * base_color + specular * float(world_data.use_specular); } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl index 1d8950e34b3..b6dc26ecc65 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl @@ -1,17 +1,9 @@ -layout(std140) uniform material_block -{ - vec4 mat_data[4096]; -}; - -/* If set to -1, the resource handle is used instead. */ -uniform int materialIndex; - void workbench_material_data_get( int handle, out vec3 color, out float alpha, out float roughness, out float metallic) { handle = (materialIndex != -1) ? materialIndex : handle; - vec4 data = mat_data[uint(handle) & 0xFFFu]; + vec4 data = materials_data[uint(handle) & 0xFFFu]; color = data.rgb; uint encoded_data = floatBitsToUint(data.w); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl index 856654549ca..ae564435258 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl @@ -1,10 +1,4 @@ -uniform sampler2D depthBuffer; - -in vec4 uvcoordsvar; - -out vec4 fragColor; - void main() { float depth = texture(depthBuffer, uvcoordsvar.st).r; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl index 7a3ebc4035e..1b20171b3ff 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl @@ -1,22 +1,13 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) #pragma BLENDER_REQUIRE(workbench_common_lib.glsl) #pragma BLENDER_REQUIRE(workbench_image_lib.glsl) -#ifndef WORKBENCH_SHADER_SHARED_H -layout(location = 0) out vec4 materialData; -layout(location = 1) out vec2 normalData; -layout(location = 2) out uint objectId; -#endif - -uniform bool useMatcap = false; - void main() { normalData = workbench_normal_encode(gl_FrontFacing, normal_interp); - materialData = vec4(color_interp, packed_rough_metal); + materialData = vec4(color_interp, workbench_float_pair_encode(roughness, metallic)); objectId = uint(object_id); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl index c3faa3957ef..65b9f4de4b6 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl @@ -1,15 +1,10 @@ #pragma BLENDER_REQUIRE(common_hair_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) #pragma BLENDER_REQUIRE(workbench_common_lib.glsl) #pragma BLENDER_REQUIRE(workbench_material_lib.glsl) #pragma BLENDER_REQUIRE(workbench_image_lib.glsl) -#ifndef WORKBENCH_SHADER_SHARED_H -uniform samplerBuffer ac; /* active color layer */ -uniform samplerBuffer au; /* active texture layer */ -#endif - /* From http://libnoise.sourceforge.net/noisegen/index.html */ float integer_noise(int n) { @@ -65,19 +60,12 @@ void main() float hair_rand = integer_noise(hair_get_strand_id()); vec3 nor = workbench_hair_random_normal(tan, binor, hair_rand); -#ifdef USE_WORLD_CLIP_PLANES - world_clip_planes_calc_clip_distance(world_pos); -#endif + view_clipping_distances(world_pos); uv_interp = hair_get_customdata_vec2(au); normal_interp = normalize(normal_world_to_view(nor)); -#ifndef WORKBENCH_SHADER_SHARED_H -# ifdef OPAQUE_MATERIAL - float metallic, roughness; -# endif -#endif workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic); if (materialIndex == 0) { @@ -90,9 +78,5 @@ void main() workbench_hair_random_material(hair_rand, color_interp, roughness, metallic); -#ifdef OPAQUE_MATERIAL - packed_rough_metal = workbench_float_pair_encode(roughness, metallic); -#endif - object_id = int(uint(resource_handle) & 0xFFFFu) + 1; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_info.hh b/source/blender/draw/engines/workbench/shaders/workbench_prepass_info.hh deleted file mode 100644 index 1dd706c9460..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_info.hh +++ /dev/null @@ -1,136 +0,0 @@ - -#include "gpu_shader_create_info.hh" - -/* -------------------------------------------------------------------- */ -/** \name Object Type - * \{ */ - -GPU_SHADER_CREATE_INFO(workbench_mesh) - .vertex_in(0, Type::VEC3, "pos") - .vertex_in(1, Type::VEC3, "nor") - .vertex_in(2, Type::VEC4, "ac") - .vertex_in(3, Type::VEC2, "au") - .vertex_source("workbench_prepass_vert.glsl") - .additional_info("draw_mesh"); - -GPU_SHADER_CREATE_INFO(workbench_hair) - .sampler(0, ImageType::FLOAT_BUFFER, "ac", Frequency::BATCH) - .sampler(1, ImageType::FLOAT_BUFFER, "au", Frequency::BATCH) - .vertex_source("workbench_prepass_hair_vert.glsl") - .additional_info("draw_hair"); - -GPU_SHADER_CREATE_INFO(workbench_pointcloud) - .vertex_source("workbench_prepass_pointcloud_vert.glsl") - .additional_info("draw_pointcloud"); - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Texture Type - * \{ */ - -GPU_SHADER_CREATE_INFO(workbench_texture_none).define("TEXTURE_NONE"); - -GPU_SHADER_CREATE_INFO(workbench_texture_single) - .sampler(2, ImageType::FLOAT_2D, "imageTexture", Frequency::BATCH) - .push_constant(1, Type::BOOL, "imagePremult") - .push_constant(2, Type::FLOAT, "imageTransparencyCutoff") - .define("V3D_SHADING_TEXTURE_COLOR"); - -GPU_SHADER_CREATE_INFO(workbench_texture_tile) - .sampler(2, ImageType::FLOAT_2D_ARRAY, "imageTileArray", Frequency::BATCH) - .sampler(3, ImageType::FLOAT_1D_ARRAY, "imageTileData", Frequency::BATCH) - .push_constant(1, Type::BOOL, "imagePremult") - .push_constant(2, Type::FLOAT, "imageTransparencyCutoff") - .define("TEXTURE_IMAGE_ARRAY"); - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Lighting Type - * \{ */ - -GPU_SHADER_CREATE_INFO(workbench_lighting_studio).define("V3D_LIGHTING_STUDIO"); -GPU_SHADER_CREATE_INFO(workbench_lighting_matcap).define("V3D_LIGHTING_MATCAP"); -GPU_SHADER_CREATE_INFO(workbench_lighting_flat).define("V3D_LIGHTING_FLAT"); - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Material Interface - * \{ */ - -GPU_SHADER_INTERFACE_INFO(workbench_material_iface, "") - .smooth(Type::VEC3, "normal_interp") - .smooth(Type::VEC3, "color_interp") - .smooth(Type::FLOAT, "alpha_interp") - .smooth(Type::VEC2, "uv_interp") - .flat(Type::INT, "object_id") - .flat(Type::FLOAT, "roughness") - .flat(Type::FLOAT, "metallic"); - -GPU_SHADER_CREATE_INFO(workbench_material).vertex_out(workbench_material_iface); - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Pipeline Type - * \{ */ - -GPU_SHADER_CREATE_INFO(workbench_transparent_accum) - /* Note: Blending will be skipped on objectId because output is a - non-normalized integer buffer. */ - .fragment_out(0, Type::VEC4, "transparentAccum") - .fragment_out(1, Type::VEC4, "revealageAccum") - .fragment_out(2, Type::UINT, "objectId") - .uniform_buf(4, "WorldData", "world_data", Frequency::PASS) - .typedef_source("workbench_shader_shared.h") - .fragment_source("workbench_transparent_accum_frag.glsl") - .additional_info("workbench_material"); - -GPU_SHADER_CREATE_INFO(workbench_opaque) - .fragment_out(0, Type::VEC4, "materialData") - .fragment_out(1, Type::VEC2, "normalData") - .fragment_out(2, Type::UINT, "objectId") - .uniform_buf(4, "WorldData", "world_data", Frequency::PASS) - .typedef_source("workbench_shader_shared.h") - .fragment_source("workbench_prepass_frag.glsl") - .additional_info("workbench_material"); - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Variations Declaration - * \{ */ - -#define WORKBENCH_SURFACETYPE_VARIATIONS(prefix, ...) \ - GPU_SHADER_CREATE_INFO(prefix##_mesh) \ - .additional_info("workbench_mesh", __VA_ARGS__) \ - .do_static_compilation(true); \ - GPU_SHADER_CREATE_INFO(prefix##_hair) \ - .additional_info("workbench_hair", __VA_ARGS__) \ - .do_static_compilation(true); \ - GPU_SHADER_CREATE_INFO(prefix##_ptcloud) \ - .additional_info("workbench_pointcloud", __VA_ARGS__) \ - .do_static_compilation(true); - -#define WORKBENCH_PIPELINE_VARIATIONS(prefix, ...) \ - WORKBENCH_SURFACETYPE_VARIATIONS(prefix##_transp_studio, \ - "workbench_transparent_accum", \ - "workbench_lighting_studio", \ - __VA_ARGS__) \ - WORKBENCH_SURFACETYPE_VARIATIONS(prefix##_transp_matcap, \ - "workbench_transparent_accum", \ - "workbench_lighting_matcap", \ - __VA_ARGS__) \ - WORKBENCH_SURFACETYPE_VARIATIONS(prefix##_transp_flat, \ - "workbench_transparent_accum", \ - "workbench_lighting_flat", \ - __VA_ARGS__) \ - WORKBENCH_SURFACETYPE_VARIATIONS(prefix##_opaque, "workbench_opaque", __VA_ARGS__) - -WORKBENCH_PIPELINE_VARIATIONS(workbench_tex_none, "workbench_texture_none") -WORKBENCH_PIPELINE_VARIATIONS(workbench_tex_single, "workbench_texture_single") -WORKBENCH_PIPELINE_VARIATIONS(workbench_tex_tile, "workbench_texture_tile") - -/** \} */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl index 8efe10b8236..911d6f5b036 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl @@ -1,7 +1,7 @@ +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_pointcloud_lib.glsl) -#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) #pragma BLENDER_REQUIRE(workbench_common_lib.glsl) #pragma BLENDER_REQUIRE(workbench_material_lib.glsl) #pragma BLENDER_REQUIRE(workbench_image_lib.glsl) @@ -15,26 +15,15 @@ void main() gl_Position = point_world_to_ndc(world_pos); -#ifdef USE_WORLD_CLIP_PLANES - world_clip_planes_calc_clip_distance(world_pos); -#endif + view_clipping_distances(world_pos); uv_interp = vec2(0.0); -#ifndef WORKBENCH_SHADER_SHARED_H -# ifdef OPAQUE_MATERIAL - float metallic, roughness; -# endif -#endif workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic); if (materialIndex == 0) { color_interp = vec3(1.0); } -#ifdef OPAQUE_MATERIAL - packed_rough_metal = workbench_float_pair_encode(roughness, metallic); -#endif - object_id = int(uint(resource_handle) & 0xFFFFu) + 1; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl index 1f6a8a63944..3a63b141c5f 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl @@ -1,44 +1,26 @@ +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) #pragma BLENDER_REQUIRE(workbench_common_lib.glsl) #pragma BLENDER_REQUIRE(workbench_material_lib.glsl) #pragma BLENDER_REQUIRE(workbench_image_lib.glsl) -#ifndef WORKBENCH_SHADER_SHARED_H -in vec3 pos; -in vec3 nor; -in vec4 ac; /* active color */ -in vec2 au; /* active texture layer */ -#endif - void main() { vec3 world_pos = point_object_to_world(pos); gl_Position = point_world_to_ndc(world_pos); -#ifdef USE_WORLD_CLIP_PLANES - world_clip_planes_calc_clip_distance(world_pos); -#endif + view_clipping_distances(world_pos); uv_interp = au; normal_interp = normalize(normal_object_to_view(nor)); -#ifndef WORKBENCH_SHADER_SHARED_H -# ifdef OPAQUE_MATERIAL - float metallic, roughness; -# endif -#endif workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic); if (materialIndex == 0) { color_interp = ac.rgb; } -#ifdef OPAQUE_MATERIAL - packed_rough_metal = workbench_float_pair_encode(roughness, metallic); -#endif - object_id = int(uint(resource_handle) & 0xFFFFu) + 1; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl deleted file mode 100644 index 178e61c8a8d..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl +++ /dev/null @@ -1,17 +0,0 @@ - -#ifndef WORKBENCH_SHADER_SHARED_H -IN_OUT ShaderStageInterface -{ - vec3 normal_interp; - vec3 color_interp; - float alpha_interp; - vec2 uv_interp; -# ifdef TRANSPARENT_MATERIAL - flat float roughness; - flat float metallic; -# else - flat float packed_rough_metal; -# endif - flat int object_id; -}; -#endif diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl index 09bafb8ff11..4a7b1522426 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl @@ -1,41 +1,7 @@ -#extension GL_ARB_gpu_shader5 : enable - -#ifdef GL_ARB_gpu_shader5 +#ifdef GPU_ARB_gpu_shader5 # define USE_INVOC_EXT #endif -#ifdef DOUBLE_MANIFOLD -# ifdef USE_INVOC_EXT -# define invoc_len 2 -# else -# define vert_len 6 -# endif -#else -# ifdef USE_INVOC_EXT -# define invoc_len 2 -# else -# define vert_len 6 -# endif -#endif - -#ifdef USE_INVOC_EXT -layout(triangles, invocations = invoc_len) in; -layout(triangle_strip, max_vertices = 3) out; -#else -layout(triangles) in; -layout(triangle_strip, max_vertices = vert_len) out; -#endif - -uniform vec3 lightDirection = vec3(0.57, 0.57, -0.57); - -in VertexData -{ - vec3 pos; /* local position */ - vec4 frontPosition; /* final ndc position */ - vec4 backPosition; -} -vData[]; - vec4 get_pos(int v, bool backface) { return (backface) ? vData[v].backPosition : vData[v].frontPosition; @@ -76,18 +42,19 @@ void main() /* In case of non manifold geom, we only increase/decrease * the stencil buffer by one but do every faces as they were facing the light. */ bool invert = backface; + const bool is_manifold = false; #else const bool invert = false; - if (!backface) { + const bool is_manifold = true; #endif + + if (!is_manifold || !backface) { #ifdef USE_INVOC_EXT - bool do_front = (gl_InvocationID & 1) == 0; - emit_cap(do_front, invert); + bool do_front = (gl_InvocationID & 1) == 0; + emit_cap(do_front, invert); #else emit_cap(true, invert); emit_cap(false, invert); #endif -#ifndef DOUBLE_MANIFOLD -} -#endif + } } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl index 6fa76510e6e..c9977a8d91a 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl @@ -1,10 +1,4 @@ -out vec4 fragColor; - -layout(location = 0) out vec4 materialData; -layout(location = 1) out vec4 normalData; -layout(location = 2) out uint objectId; - void main() { const float a = 0.25; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl index 2c9190bfcf4..9902884fc12 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl @@ -1,41 +1,7 @@ -#extension GL_ARB_gpu_shader5 : enable - -#ifdef GL_ARB_gpu_shader5 +#ifdef GPU_ARB_gpu_shader5 # define USE_INVOC_EXT #endif -#ifdef DOUBLE_MANIFOLD -# ifdef USE_INVOC_EXT -# define invoc_len 2 -# else -# define vert_len 8 -# endif -#else -# ifdef USE_INVOC_EXT -# define invoc_len 1 -# else -# define vert_len 4 -# endif -#endif - -#ifdef USE_INVOC_EXT -layout(lines_adjacency, invocations = invoc_len) in; -layout(triangle_strip, max_vertices = 4) out; -#else -layout(lines_adjacency) in; -layout(triangle_strip, max_vertices = vert_len) out; -#endif - -uniform vec3 lightDirection = vec3(0.57, 0.57, -0.57); - -in VertexData -{ - vec3 pos; /* local position */ - vec4 frontPosition; /* final ndc position */ - vec4 backPosition; -} -vData[]; - #define DEGENERATE_TRIS_WORKAROUND #define DEGENERATE_TRIS_AREA_THRESHOLD 4e-17 diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl index e07f87525e2..a220434ec45 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl @@ -1,17 +1,5 @@ -#define INFINITE 1000.0 -uniform vec3 lightDirection = vec3(0.57, 0.57, -0.57); -uniform float lightDistance = 1e4; - -in vec3 pos; - -out VertexData -{ - vec3 pos; /* local position */ - vec4 frontPosition; /* final ndc position */ - vec4 backPosition; -} -vData; +#pragma BLENDER_REQUIRE(common_view_lib.glsl) void main() { diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl index 0062cbe17a2..9c0f93c67d9 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl @@ -1,21 +1,10 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) #pragma BLENDER_REQUIRE(workbench_common_lib.glsl) #pragma BLENDER_REQUIRE(workbench_image_lib.glsl) #pragma BLENDER_REQUIRE(workbench_matcap_lib.glsl) #pragma BLENDER_REQUIRE(workbench_world_light_lib.glsl) -#ifndef WORKBENCH_SHADER_SHARED_H -/* Revealage is actually stored in transparentAccum alpha channel. - * This is a workaround to older hardware not having separate blend equation per render target. */ -layout(location = 0) out vec4 transparentAccum; -layout(location = 1) out vec4 revealageAccum; - -/* NOTE: Blending will be skipped on objectId because output is a non-normalized integer buffer. */ -layout(location = 2) out uint objectId; -#endif - /* Special function only to be used with calculate_transparent_weight(). */ float linear_zdepth(float depth, vec4 viewvecs[2], mat4 proj_mat) { @@ -69,7 +58,7 @@ void main() #endif #ifdef V3D_LIGHTING_MATCAP - vec3 shaded_color = get_matcap_lighting(color, N, I); + vec3 shaded_color = get_matcap_lighting(matcap_diffuse_tx, matcap_specular_tx, color, N, I); #endif #ifdef V3D_LIGHTING_STUDIO @@ -80,7 +69,7 @@ void main() vec3 shaded_color = color; #endif - shaded_color *= get_shadow(N); + shaded_color *= get_shadow(N, forceShadowing); /* Listing 4 */ float weight = calculate_transparent_weight() * alpha_interp; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl index d985737a35b..a2c45d2f8e3 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl @@ -1,11 +1,4 @@ -uniform sampler2D transparentAccum; -uniform sampler2D transparentRevealage; - -in vec4 uvcoordsvar; - -out vec4 fragColor; - /* Based on : * McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of * Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122–141, 2013 diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 48102b4dcca..076f6e80104 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -1,40 +1,8 @@ #pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl) -#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) #pragma BLENDER_REQUIRE(workbench_common_lib.glsl) -uniform sampler2D depthBuffer; - -uniform sampler3D densityTexture; -uniform sampler3D shadowTexture; -uniform sampler3D flameTexture; -uniform usampler3D flagTexture; -uniform sampler1D flameColorTexture; -uniform sampler1D transferTexture; -uniform mat4 volumeObjectToTexture; - -uniform int samplesLen = 256; -uniform float noiseOfs = 0.0; -uniform float stepLength; /* Step length in local space. */ -uniform float densityScale; /* Simple Opacity multiplicator. */ -uniform float gridScale; /* Multiplicator for grid scaling. */ -uniform vec3 activeColor; - -uniform float slicePosition; -uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */ - -uniform bool showPhi = false; -uniform bool showFlags = false; -uniform bool showPressure = false; - -#ifdef VOLUME_SLICE -in vec3 localPos; -#endif - -out vec4 fragColor; - float phase_function_isotropic() { return 1.0 / (4.0 * M_PI); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl index 7327a92e04f..d2b12f41421 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl @@ -1,22 +1,8 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl) - -uniform float slicePosition; -uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */ - -uniform mat4 volumeTextureToObject; - -in vec3 pos; RESOURCE_ID_VARYING -#ifdef VOLUME_SLICE -in vec3 uvs; - -out vec3 localPos; -#endif - void main() { #ifdef VOLUME_SLICE diff --git a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl index 41ef516ee4d..531ed461057 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl @@ -1,6 +1,4 @@ -#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) - /* [Drobot2014a] Low Level Optimizations for GCN */ vec4 fast_rcp(vec4 v) { @@ -120,12 +118,10 @@ vec3 get_world_lighting(vec3 base_color, float roughness, float metallic, vec3 N return diffuse_light + specular_light; } -uniform bool forceShadowing = false; - -float get_shadow(vec3 N) +float get_shadow(vec3 N, bool force_shadowing) { float light_factor = -dot(N, world_data.shadow_direction_vs.xyz); float shadow_mix = smoothstep(world_data.shadow_shift, world_data.shadow_focus, light_factor); - shadow_mix *= forceShadowing ? 0.0 : world_data.shadow_mul; + shadow_mix *= force_shadowing ? 0.0 : world_data.shadow_mul; return shadow_mix + world_data.shadow_add; } diff --git a/source/blender/draw/engines/workbench/workbench_effect_cavity.c b/source/blender/draw/engines/workbench/workbench_effect_cavity.c index b294b9a62ca..e58c88c5dcc 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_cavity.c +++ b/source/blender/draw/engines/workbench/workbench_effect_cavity.c @@ -164,10 +164,10 @@ void workbench_cavity_cache_init(WORKBENCH_Data *data) grp = DRW_shgroup_create(sh, psl->cavity_ps); DRW_shgroup_uniform_texture(grp, "normalBuffer", wpd->normal_buffer_tx); - DRW_shgroup_uniform_block(grp, "samples_block", wpd->vldata->cavity_sample_ubo); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); if (SSAO_ENABLED(wpd)) { + DRW_shgroup_uniform_block(grp, "samples_coords", wpd->vldata->cavity_sample_ubo); DRW_shgroup_uniform_texture(grp, "depthBuffer", dtxl->depth); DRW_shgroup_uniform_texture(grp, "cavityJitter", wpd->vldata->cavity_jitter_tx); } diff --git a/source/blender/draw/engines/workbench/workbench_effect_dof.c b/source/blender/draw/engines/workbench/workbench_effect_dof.c index 2261790226a..b5f43251a43 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_dof.c +++ b/source/blender/draw/engines/workbench/workbench_effect_dof.c @@ -328,7 +328,7 @@ void workbench_dof_cache_init(WORKBENCH_Data *vedata) float offset = wpd->taa_sample / (float)max_ii(1, wpd->taa_sample_len); DRWShadingGroup *grp = DRW_shgroup_create(blur1_sh, psl->dof_blur1_ps); - DRW_shgroup_uniform_block(grp, "dofSamplesBlock", wpd->vldata->dof_sample_ubo); + DRW_shgroup_uniform_block(grp, "samples", wpd->vldata->dof_sample_ubo); DRW_shgroup_uniform_texture(grp, "noiseTex", wpd->vldata->cavity_jitter_tx); DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx); DRW_shgroup_uniform_texture(grp, "halfResColorTex", txl->dof_source_tx); diff --git a/source/blender/draw/engines/workbench/workbench_effect_outline.c b/source/blender/draw/engines/workbench/workbench_effect_outline.c index d1bc6b6c435..4f716c5a7bc 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_outline.c +++ b/source/blender/draw/engines/workbench/workbench_effect_outline.c @@ -46,7 +46,7 @@ void workbench_outline_cache_init(WORKBENCH_Data *data) grp = DRW_shgroup_create(sh, psl->outline_ps); DRW_shgroup_uniform_texture(grp, "objectIdBuffer", wpd->object_id_tx); DRW_shgroup_uniform_texture(grp, "depthBuffer", dtxl->depth); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } else { diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 5bc2c53e253..8773c78e82f 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -28,13 +28,14 @@ #include "BLI_alloca.h" +#include "BKE_editmesh.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" +#include "DNA_curves_types.h" #include "DNA_fluid_types.h" -#include "DNA_hair_types.h" #include "DNA_image_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" @@ -51,8 +52,6 @@ void workbench_engine_init(void *ved) WORKBENCH_StorageList *stl = vedata->stl; WORKBENCH_TextureList *txl = vedata->txl; - workbench_shader_library_ensure(); - workbench_private_data_alloc(stl); WORKBENCH_PrivateData *wpd = stl->wpd; workbench_private_data_init(wpd); @@ -241,6 +240,26 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd, DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL); } +static const CustomData *workbench_mesh_get_loop_custom_data(const Mesh *mesh) +{ + if (mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + BLI_assert(mesh->edit_mesh != NULL); + BLI_assert(mesh->edit_mesh->bm != NULL); + return &mesh->edit_mesh->bm->ldata; + } + return &mesh->ldata; +} + +static const CustomData *workbench_mesh_get_vert_custom_data(const Mesh *mesh) +{ + if (mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + BLI_assert(mesh->edit_mesh != NULL); + BLI_assert(mesh->edit_mesh->bm != NULL); + return &mesh->edit_mesh->bm->vdata; + } + return &mesh->vdata; +} + /** * Decide what color-type to draw the object with. * In some cases it can be overwritten by #workbench_material_setup(). @@ -253,6 +272,8 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, { eV3DShadingColorType color_type = wpd->shading.color_type; const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; + const CustomData *ldata = (me == NULL) ? NULL : workbench_mesh_get_loop_custom_data(me); + const CustomData *vdata = (me == NULL) ? NULL : workbench_mesh_get_vert_custom_data(me); const DRWContextState *draw_ctx = DRW_context_state_get(); const bool is_active = (ob == draw_ctx->obact); @@ -266,19 +287,19 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, if (ob->dt < OB_TEXTURE) { color_type = V3D_SHADING_MATERIAL_COLOR; } - else if ((me == NULL) || (me->mloopuv == NULL)) { + else if ((me == NULL) || !CustomData_has_layer(ldata, CD_MLOOPUV)) { /* Disable color mode if data layer is unavailable. */ color_type = V3D_SHADING_MATERIAL_COLOR; } } else if (color_type == V3D_SHADING_VERTEX_COLOR) { if (U.experimental.use_sculpt_vertex_colors) { - if ((me == NULL) || !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) { + if ((me == NULL) || !CustomData_has_layer(vdata, CD_PROP_COLOR)) { color_type = V3D_SHADING_OBJECT_COLOR; } } else { - if ((me == NULL) || !CustomData_has_layer(&me->ldata, CD_MLOOPCOL)) { + if ((me == NULL) || !CustomData_has_layer(ldata, CD_MLOOPCOL)) { color_type = V3D_SHADING_OBJECT_COLOR; } } @@ -293,13 +314,13 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, if (!is_sculpt_pbvh && !is_render) { /* Force texture or vertex mode if object is in paint mode. */ - if (is_texpaint_mode && me && me->mloopuv) { + if (is_texpaint_mode && me && CustomData_has_layer(ldata, CD_MLOOPUV)) { color_type = V3D_SHADING_TEXTURE_COLOR; if (r_texpaint_mode) { *r_texpaint_mode = true; } } - else if (is_vertpaint_mode && me && me->mloopcol) { + else if (is_vertpaint_mode && me && CustomData_has_layer(ldata, CD_MLOOPCOL)) { color_type = V3D_SHADING_VERTEX_COLOR; } } @@ -398,9 +419,9 @@ void workbench_cache_populate(void *ved, Object *ob) workbench_shadow_cache_populate(vedata, ob, has_transp_mat); } } - else if (ob->type == OB_HAIR) { + else if (ob->type == OB_CURVES) { int color_type = workbench_color_type_get(wpd, ob, NULL, NULL, NULL); - workbench_cache_hair_populate(wpd, ob, NULL, NULL, color_type, false, HAIR_MATERIAL_NR); + workbench_cache_hair_populate(wpd, ob, NULL, NULL, color_type, false, CURVES_MATERIAL_NR); } else if (ob->type == OB_VOLUME) { if (wpd->shading.type != OB_WIRE) { diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index c24cab009c7..1b12e29617c 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -210,7 +210,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd, DRWShadingGroup *grp = prepass->common_shgrp; *grp_mat = grp = DRW_shgroup_create_sub(grp); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", mat_id); return grp; } @@ -234,7 +234,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd, DRWShadingGroup **grp = &wpd->prepass[transp][infront][datatype].common_shgrp; if (resource_changed) { *grp = DRW_shgroup_create_sub(*grp); - DRW_shgroup_uniform_block(*grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(*grp, "materials_data", wpd->material_ubo_curr); } if (r_transp && transp) { *r_transp = true; @@ -293,5 +293,6 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd, DRW_shgroup_uniform_texture_ex(grp, "imageTexture", tex, sampler); } DRW_shgroup_uniform_bool_copy(grp, "imagePremult", (ima && ima->alpha_mode == IMA_ALPHA_PREMUL)); + DRW_shgroup_uniform_float_copy(grp, "imageTransparencyCutoff", 0.1f); return grp; } diff --git a/source/blender/draw/engines/workbench/workbench_opaque.c b/source/blender/draw/engines/workbench/workbench_opaque.c index 9fdefed019f..e4534d923ab 100644 --- a/source/blender/draw/engines/workbench/workbench_opaque.c +++ b/source/blender/draw/engines/workbench/workbench_opaque.c @@ -88,26 +88,26 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_opaque_get(wpd, data); wpd->prepass[opaque][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1); DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); wpd->prepass[opaque][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); sh = workbench_shader_opaque_image_get(wpd, data, false); wpd->prepass[opaque][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); sh = workbench_shader_opaque_image_get(wpd, data, true); wpd->prepass[opaque][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); } @@ -121,7 +121,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_composite_get(wpd); grp = DRW_shgroup_create(sh, psl->composite_ps); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_texture(grp, "materialBuffer", wpd->material_buffer_tx); DRW_shgroup_uniform_texture(grp, "normalBuffer", wpd->normal_buffer_tx); DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", false); @@ -135,8 +135,8 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) struct GPUTexture *spec_tx = wpd->studio_light->matcap_specular.gputexture; const bool use_spec = workbench_is_specular_highlight_enabled(wpd); spec_tx = (use_spec && spec_tx) ? spec_tx : diff_tx; - DRW_shgroup_uniform_texture(grp, "matcapDiffuseImage", diff_tx); - DRW_shgroup_uniform_texture(grp, "matcapSpecularImage", spec_tx); + DRW_shgroup_uniform_texture(grp, "matcap_diffuse_tx", diff_tx); + DRW_shgroup_uniform_texture(grp, "matcap_specular_tx", spec_tx); } DRW_shgroup_call_procedural_triangles(grp, NULL, 1); diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index fb4600316ec..2cf96a8e139 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -459,7 +459,6 @@ void workbench_shader_depth_of_field_get(GPUShader **prepare_sh, GPUShader **blur2_sh, GPUShader **resolve_sh); -void workbench_shader_library_ensure(void); void workbench_shader_free(void); /* workbench_effect_antialiasing.c */ diff --git a/source/blender/draw/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c deleted file mode 100644 index ad610a6a885..00000000000 --- a/source/blender/draw/engines/workbench/workbench_shader.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2020, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "DRW_render.h" - -#include "BLI_dynstr.h" -#include "BLI_string_utils.h" - -#include "workbench_engine.h" -#include "workbench_private.h" - -extern char datatoc_common_math_lib_glsl[]; -extern char datatoc_common_math_geom_lib_glsl[]; -extern char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_pointcloud_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_common_smaa_lib_glsl[]; - -extern char datatoc_workbench_prepass_vert_glsl[]; -extern char datatoc_workbench_prepass_hair_vert_glsl[]; -extern char datatoc_workbench_prepass_pointcloud_vert_glsl[]; -extern char datatoc_workbench_prepass_frag_glsl[]; - -extern char datatoc_workbench_effect_cavity_frag_glsl[]; -extern char datatoc_workbench_effect_outline_frag_glsl[]; -extern char datatoc_workbench_effect_dof_frag_glsl[]; -extern char datatoc_workbench_effect_taa_frag_glsl[]; -extern char datatoc_workbench_effect_smaa_frag_glsl[]; -extern char datatoc_workbench_effect_smaa_vert_glsl[]; - -extern char datatoc_workbench_composite_frag_glsl[]; - -extern char datatoc_workbench_transparent_accum_frag_glsl[]; -extern char datatoc_workbench_transparent_resolve_frag_glsl[]; - -extern char datatoc_workbench_merge_infront_frag_glsl[]; - -extern char datatoc_workbench_shadow_vert_glsl[]; -extern char datatoc_workbench_shadow_geom_glsl[]; -extern char datatoc_workbench_shadow_caps_geom_glsl[]; -extern char datatoc_workbench_shadow_debug_frag_glsl[]; - -extern char datatoc_workbench_volume_vert_glsl[]; -extern char datatoc_workbench_volume_frag_glsl[]; - -extern char datatoc_workbench_cavity_lib_glsl[]; -extern char datatoc_workbench_common_lib_glsl[]; -extern char datatoc_workbench_curvature_lib_glsl[]; -extern char datatoc_workbench_data_lib_glsl[]; -extern char datatoc_workbench_image_lib_glsl[]; -extern char datatoc_workbench_matcap_lib_glsl[]; -extern char datatoc_workbench_material_lib_glsl[]; -extern char datatoc_workbench_shader_interface_lib_glsl[]; -extern char datatoc_workbench_world_light_lib_glsl[]; - -extern char datatoc_gpu_shader_depth_only_frag_glsl[]; -extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; - -/* Maximum number of variations. */ -#define MAX_LIGHTING 3 -#define MAX_COLOR 3 - -enum { - VOLUME_SH_SLICE = 0, - VOLUME_SH_COBA, - VOLUME_SH_CUBIC, -}; - -#define VOLUME_SH_MAX (1 << (VOLUME_SH_CUBIC + 1)) - -static struct { - struct GPUShader *opaque_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX][MAX_COLOR]; - struct GPUShader *transp_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX] - [MAX_LIGHTING][MAX_COLOR]; - - struct GPUShader *opaque_composite_sh[MAX_LIGHTING]; - struct GPUShader *oit_resolve_sh; - struct GPUShader *outline_sh; - struct GPUShader *merge_infront_sh; - - struct GPUShader *shadow_depth_pass_sh[2]; - struct GPUShader *shadow_depth_fail_sh[2][2]; - - struct GPUShader *cavity_sh[2][2]; - - struct GPUShader *dof_prepare_sh; - struct GPUShader *dof_downsample_sh; - struct GPUShader *dof_blur1_sh; - struct GPUShader *dof_blur2_sh; - struct GPUShader *dof_resolve_sh; - - struct GPUShader *aa_accum_sh; - struct GPUShader *smaa_sh[3]; - - struct GPUShader *volume_sh[2][2][3][2]; - - struct DRWShaderLibrary *lib; -} e_data = {{{{NULL}}}}; - -void workbench_shader_library_ensure(void) -{ - if (e_data.lib == NULL) { - e_data.lib = DRW_shader_library_create(); - /* NOTE: These need to be ordered by dependencies. */ - DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_pointcloud_lib); - DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_shader_interface_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_common_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_image_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_material_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_data_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_matcap_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_cavity_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_curvature_lib); - DRW_SHADER_LIB_ADD(e_data.lib, workbench_world_light_lib); - } -} - -static char *workbench_build_defines( - WORKBENCH_PrivateData *wpd, bool textured, bool tiled, bool cavity, bool curvature) -{ - char *str = NULL; - - DynStr *ds = BLI_dynstr_new(); - - if (wpd && wpd->shading.light == V3D_LIGHTING_STUDIO) { - BLI_dynstr_append(ds, "#define V3D_LIGHTING_STUDIO\n"); - } - else if (wpd && wpd->shading.light == V3D_LIGHTING_MATCAP) { - BLI_dynstr_append(ds, "#define V3D_LIGHTING_MATCAP\n"); - } - else { - BLI_dynstr_append(ds, "#define V3D_LIGHTING_FLAT\n"); - } - - if (NORMAL_ENCODING_ENABLED()) { - BLI_dynstr_append(ds, "#define WORKBENCH_ENCODE_NORMALS\n"); - } - - if (textured) { - BLI_dynstr_append(ds, "#define V3D_SHADING_TEXTURE_COLOR\n"); - } - if (tiled) { - BLI_dynstr_append(ds, "#define TEXTURE_IMAGE_ARRAY\n"); - } - if (cavity) { - BLI_dynstr_append(ds, "#define USE_CAVITY\n"); - } - if (curvature) { - BLI_dynstr_append(ds, "#define USE_CURVATURE\n"); - } - - str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -static int workbench_color_index(WORKBENCH_PrivateData *UNUSED(wpd), bool textured, bool tiled) -{ - BLI_assert(2 < MAX_COLOR); - return (textured) ? (tiled ? 2 : 1) : 0; -} - -static GPUShader *workbench_shader_get_ex(WORKBENCH_PrivateData *wpd, - bool transp, - eWORKBENCH_DataType datatype, - bool textured, - bool tiled) -{ - int color = workbench_color_index(wpd, textured, tiled); - int light = wpd->shading.light; - BLI_assert(light < MAX_LIGHTING); - struct GPUShader **shader = - (transp) ? &e_data.transp_prepass_sh_cache[wpd->sh_cfg][datatype][light][color] : - &e_data.opaque_prepass_sh_cache[wpd->sh_cfg][datatype][color]; - - if (*shader == NULL) { - char *defines = workbench_build_defines(wpd, textured, tiled, false, false); - - char *frag_file = transp ? datatoc_workbench_transparent_accum_frag_glsl : - datatoc_workbench_prepass_frag_glsl; - char *frag_src = DRW_shader_library_create_shader_string(e_data.lib, frag_file); - - char *vert_file = (datatype == WORKBENCH_DATATYPE_HAIR) ? - datatoc_workbench_prepass_hair_vert_glsl : - ((datatype == WORKBENCH_DATATYPE_POINTCLOUD) ? - datatoc_workbench_prepass_pointcloud_vert_glsl : - datatoc_workbench_prepass_vert_glsl); - char *vert_src = DRW_shader_library_create_shader_string(e_data.lib, vert_file); - - const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[wpd->sh_cfg]; - - *shader = GPU_shader_create_from_arrays({ - .vert = (const char *[]){sh_cfg_data->lib, vert_src, NULL}, - .frag = (const char *[]){frag_src, NULL}, - .defs = (const char *[]){sh_cfg_data->def, - defines, - transp ? "#define TRANSPARENT_MATERIAL\n" : - "#define OPAQUE_MATERIAL\n", - (datatype == WORKBENCH_DATATYPE_POINTCLOUD) ? - "#define UNIFORM_RESOURCE_ID\n" - "#define INSTANCED_ATTR\n" : - NULL, - NULL}, - }); - - MEM_freeN(defines); - MEM_freeN(frag_src); - MEM_freeN(vert_src); - } - return *shader; -} - -GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, eWORKBENCH_DataType datatype) -{ - return workbench_shader_get_ex(wpd, false, datatype, false, false); -} - -GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd, - eWORKBENCH_DataType datatype, - bool tiled) -{ - return workbench_shader_get_ex(wpd, false, datatype, true, tiled); -} - -GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, - eWORKBENCH_DataType datatype) -{ - return workbench_shader_get_ex(wpd, true, datatype, false, false); -} - -GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd, - eWORKBENCH_DataType datatype, - bool tiled) -{ - return workbench_shader_get_ex(wpd, true, datatype, true, tiled); -} - -GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd) -{ - int light = wpd->shading.light; - struct GPUShader **shader = &e_data.opaque_composite_sh[light]; - BLI_assert(light < MAX_LIGHTING); - - if (*shader == NULL) { - char *defines = workbench_build_defines(wpd, false, false, false, false); - char *frag = DRW_shader_library_create_shader_string(e_data.lib, - datatoc_workbench_composite_frag_glsl); - - *shader = DRW_shader_create_fullscreen(frag, defines); - - MEM_freeN(defines); - MEM_freeN(frag); - } - return *shader; -} - -GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *UNUSED(wpd)) -{ - if (e_data.merge_infront_sh == NULL) { - char *frag = DRW_shader_library_create_shader_string( - e_data.lib, datatoc_workbench_merge_infront_frag_glsl); - - e_data.merge_infront_sh = DRW_shader_create_fullscreen(frag, NULL); - - MEM_freeN(frag); - } - return e_data.merge_infront_sh; -} - -GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *wpd) -{ - if (e_data.oit_resolve_sh == NULL) { - char *defines = workbench_build_defines(wpd, false, false, false, false); - - e_data.oit_resolve_sh = DRW_shader_create_fullscreen( - datatoc_workbench_transparent_resolve_frag_glsl, defines); - - MEM_freeN(defines); - } - return e_data.oit_resolve_sh; -} - -static GPUShader *workbench_shader_shadow_pass_get_ex(bool depth_pass, bool manifold, bool cap) -{ - struct GPUShader **shader = (depth_pass) ? &e_data.shadow_depth_pass_sh[manifold] : - &e_data.shadow_depth_fail_sh[manifold][cap]; - - if (*shader == NULL) { -#if DEBUG_SHADOW_VOLUME - const char *shadow_frag = datatoc_workbench_shadow_debug_frag_glsl; -#else - const char *shadow_frag = datatoc_gpu_shader_depth_only_frag_glsl; -#endif - - *shader = GPU_shader_create_from_arrays({ - .vert = (const char *[]){datatoc_common_view_lib_glsl, - datatoc_workbench_shadow_vert_glsl, - NULL}, - .geom = (const char *[]){(cap) ? datatoc_workbench_shadow_caps_geom_glsl : - datatoc_workbench_shadow_geom_glsl, - NULL}, - .frag = (const char *[]){shadow_frag, NULL}, - .defs = (const char *[]){(depth_pass) ? "#define SHADOW_PASS\n" : "#define SHADOW_FAIL\n", - (manifold) ? "" : "#define DOUBLE_MANIFOLD\n", - NULL}, - }); - } - return *shader; -} - -GPUShader *workbench_shader_shadow_pass_get(bool manifold) -{ - return workbench_shader_shadow_pass_get_ex(true, manifold, false); -} - -GPUShader *workbench_shader_shadow_fail_get(bool manifold, bool cap) -{ - return workbench_shader_shadow_pass_get_ex(false, manifold, cap); -} - -GPUShader *workbench_shader_cavity_get(bool cavity, bool curvature) -{ - BLI_assert(cavity || curvature); - struct GPUShader **shader = &e_data.cavity_sh[cavity][curvature]; - - if (*shader == NULL) { - char *defines = workbench_build_defines(NULL, false, false, cavity, curvature); - char *frag = DRW_shader_library_create_shader_string( - e_data.lib, datatoc_workbench_effect_cavity_frag_glsl); - - *shader = DRW_shader_create_fullscreen(frag, defines); - - MEM_freeN(defines); - MEM_freeN(frag); - } - return *shader; -} - -GPUShader *workbench_shader_outline_get(void) -{ - if (e_data.outline_sh == NULL) { - char *frag = DRW_shader_library_create_shader_string( - e_data.lib, datatoc_workbench_effect_outline_frag_glsl); - - e_data.outline_sh = DRW_shader_create_fullscreen(frag, NULL); - - MEM_freeN(frag); - } - return e_data.outline_sh; -} - -void workbench_shader_depth_of_field_get(GPUShader **prepare_sh, - GPUShader **downsample_sh, - GPUShader **blur1_sh, - GPUShader **blur2_sh, - GPUShader **resolve_sh) -{ - if (e_data.dof_prepare_sh == NULL) { - e_data.dof_prepare_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define PREPARE\n"); - e_data.dof_downsample_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DOWNSAMPLE\n"); -#if 0 /* TODO(fclem): finish COC min_max optimization */ - e_data.dof_flatten_v_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define FLATTEN_VERTICAL\n"); - e_data.dof_flatten_h_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define FLATTEN_HORIZONTAL\n"); - e_data.dof_dilate_v_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DILATE_VERTICAL\n"); - e_data.dof_dilate_h_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DILATE_HORIZONTAL\n"); -#endif - e_data.dof_blur1_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define BLUR1\n"); - e_data.dof_blur2_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define BLUR2\n"); - e_data.dof_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define RESOLVE\n"); - } - - *prepare_sh = e_data.dof_prepare_sh; - *downsample_sh = e_data.dof_downsample_sh; - *blur1_sh = e_data.dof_blur1_sh; - *blur2_sh = e_data.dof_blur2_sh; - *resolve_sh = e_data.dof_resolve_sh; -} - -GPUShader *workbench_shader_antialiasing_accumulation_get(void) -{ - if (e_data.aa_accum_sh == NULL) { - char *frag = DRW_shader_library_create_shader_string(e_data.lib, - datatoc_workbench_effect_taa_frag_glsl); - - e_data.aa_accum_sh = DRW_shader_create_fullscreen(frag, NULL); - - MEM_freeN(frag); - } - return e_data.aa_accum_sh; -} - -GPUShader *workbench_shader_antialiasing_get(int stage) -{ - BLI_assert(stage < 3); - if (!e_data.smaa_sh[stage]) { - char stage_define[32]; - BLI_snprintf(stage_define, sizeof(stage_define), "#define SMAA_STAGE %d\n", stage); - - e_data.smaa_sh[stage] = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){ - "#define SMAA_INCLUDE_VS 1\n", - "#define SMAA_INCLUDE_PS 0\n", - "uniform vec4 viewportMetrics;\n", - datatoc_common_smaa_lib_glsl, - datatoc_workbench_effect_smaa_vert_glsl, - NULL, - }, - .frag = - (const char *[]){ - "#define SMAA_INCLUDE_VS 0\n", - "#define SMAA_INCLUDE_PS 1\n", - "uniform vec4 viewportMetrics;\n", - datatoc_common_smaa_lib_glsl, - datatoc_workbench_effect_smaa_frag_glsl, - NULL, - }, - .defs = - (const char *[]){ - "#define SMAA_GLSL_3\n", - "#define SMAA_RT_METRICS viewportMetrics\n", - "#define SMAA_PRESET_HIGH\n", - "#define SMAA_LUMA_WEIGHT float4(1.0, 1.0, 1.0, 1.0)\n", - "#define SMAA_NO_DISCARD\n", - stage_define, - NULL, - }, - }); - } - return e_data.smaa_sh[stage]; -} - -GPUShader *workbench_shader_volume_get(bool slice, - bool coba, - eWORKBENCH_VolumeInterpType interp_type, - bool smoke) -{ - GPUShader **shader = &e_data.volume_sh[slice][coba][interp_type][smoke]; - - if (*shader == NULL) { - DynStr *ds = BLI_dynstr_new(); - - if (slice) { - BLI_dynstr_append(ds, "#define VOLUME_SLICE\n"); - } - if (coba) { - BLI_dynstr_append(ds, "#define USE_COBA\n"); - } - switch (interp_type) { - case WORKBENCH_VOLUME_INTERP_LINEAR: - BLI_dynstr_append(ds, "#define USE_TRILINEAR\n"); - break; - case WORKBENCH_VOLUME_INTERP_CUBIC: - BLI_dynstr_append(ds, "#define USE_TRICUBIC\n"); - break; - case WORKBENCH_VOLUME_INTERP_CLOSEST: - BLI_dynstr_append(ds, "#define USE_CLOSEST\n"); - break; - } - if (smoke) { - BLI_dynstr_append(ds, "#define VOLUME_SMOKE\n"); - } - - char *defines = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - char *vert = DRW_shader_library_create_shader_string(e_data.lib, - datatoc_workbench_volume_vert_glsl); - char *frag = DRW_shader_library_create_shader_string(e_data.lib, - datatoc_workbench_volume_frag_glsl); - - *shader = DRW_shader_create(vert, NULL, frag, defines); - - MEM_freeN(vert); - MEM_freeN(frag); - MEM_freeN(defines); - } - return *shader; -} - -void workbench_shader_free(void) -{ - for (int j = 0; j < sizeof(e_data.opaque_prepass_sh_cache) / sizeof(void *); j++) { - struct GPUShader **sh_array = &e_data.opaque_prepass_sh_cache[0][0][0]; - DRW_SHADER_FREE_SAFE(sh_array[j]); - } - for (int j = 0; j < sizeof(e_data.transp_prepass_sh_cache) / sizeof(void *); j++) { - struct GPUShader **sh_array = &e_data.transp_prepass_sh_cache[0][0][0][0]; - DRW_SHADER_FREE_SAFE(sh_array[j]); - } - for (int j = 0; j < ARRAY_SIZE(e_data.opaque_composite_sh); j++) { - struct GPUShader **sh_array = &e_data.opaque_composite_sh[0]; - DRW_SHADER_FREE_SAFE(sh_array[j]); - } - for (int j = 0; j < ARRAY_SIZE(e_data.shadow_depth_pass_sh); j++) { - struct GPUShader **sh_array = &e_data.shadow_depth_pass_sh[0]; - DRW_SHADER_FREE_SAFE(sh_array[j]); - } - for (int j = 0; j < sizeof(e_data.shadow_depth_fail_sh) / sizeof(void *); j++) { - struct GPUShader **sh_array = &e_data.shadow_depth_fail_sh[0][0]; - DRW_SHADER_FREE_SAFE(sh_array[j]); - } - for (int j = 0; j < sizeof(e_data.cavity_sh) / sizeof(void *); j++) { - struct GPUShader **sh_array = &e_data.cavity_sh[0][0]; - DRW_SHADER_FREE_SAFE(sh_array[j]); - } - for (int j = 0; j < ARRAY_SIZE(e_data.smaa_sh); j++) { - struct GPUShader **sh_array = &e_data.smaa_sh[0]; - DRW_SHADER_FREE_SAFE(sh_array[j]); - } - for (int j = 0; j < sizeof(e_data.volume_sh) / sizeof(void *); j++) { - struct GPUShader **sh_array = &e_data.volume_sh[0][0][0][0]; - DRW_SHADER_FREE_SAFE(sh_array[j]); - } - - DRW_SHADER_FREE_SAFE(e_data.oit_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data.outline_sh); - DRW_SHADER_FREE_SAFE(e_data.merge_infront_sh); - - DRW_SHADER_FREE_SAFE(e_data.dof_prepare_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_blur1_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_blur2_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh); - - DRW_SHADER_FREE_SAFE(e_data.aa_accum_sh); - - DRW_SHADER_LIB_FREE_SAFE(e_data.lib); -} diff --git a/source/blender/draw/engines/workbench/workbench_shader.cc b/source/blender/draw/engines/workbench/workbench_shader.cc new file mode 100644 index 00000000000..bbc0bc02b03 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_shader.cc @@ -0,0 +1,398 @@ +/* + * 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 <string> + +#include "workbench_engine.h" +#include "workbench_private.h" + +/* Maximum number of variations. */ +#define MAX_LIGHTING 3 + +enum eWORKBENCH_TextureType { + TEXTURE_SH_NONE = 0, + TEXTURE_SH_SINGLE, + TEXTURE_SH_TILED, + TEXTURE_SH_MAX, +}; + +static struct { + struct GPUShader + *opaque_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX][TEXTURE_SH_MAX]; + struct GPUShader *transp_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX] + [MAX_LIGHTING][TEXTURE_SH_MAX]; + + struct GPUShader *opaque_composite_sh[MAX_LIGHTING]; + struct GPUShader *oit_resolve_sh; + struct GPUShader *outline_sh; + struct GPUShader *merge_infront_sh; + + struct GPUShader *shadow_depth_pass_sh[2]; + struct GPUShader *shadow_depth_fail_sh[2][2]; + + struct GPUShader *cavity_sh[2][2]; + + struct GPUShader *dof_prepare_sh; + struct GPUShader *dof_downsample_sh; + struct GPUShader *dof_blur1_sh; + struct GPUShader *dof_blur2_sh; + struct GPUShader *dof_resolve_sh; + + struct GPUShader *aa_accum_sh; + struct GPUShader *smaa_sh[3]; + + struct GPUShader *volume_sh[2][2][3][2]; + +} e_data = {{{{nullptr}}}}; + +/* -------------------------------------------------------------------- */ +/** \name Conversions + * \{ */ + +static const char *workbench_lighting_mode_to_str(int light) +{ + switch (light) { + default: + BLI_assert_msg(0, "Error: Unknown lighting mode."); + ATTR_FALLTHROUGH; + case V3D_LIGHTING_STUDIO: + return "_studio"; + case V3D_LIGHTING_MATCAP: + return "_matcap"; + case V3D_LIGHTING_FLAT: + return "_flat"; + return ""; + } +} + +static const char *workbench_datatype_mode_to_str(eWORKBENCH_DataType datatype) +{ + switch (datatype) { + default: + BLI_assert_msg(0, "Error: Unknown data mode."); + ATTR_FALLTHROUGH; + case WORKBENCH_DATATYPE_MESH: + return "_mesh"; + case WORKBENCH_DATATYPE_HAIR: + return "_hair"; + case WORKBENCH_DATATYPE_POINTCLOUD: + return "_ptcloud"; + } +} + +static const char *workbench_volume_interp_to_str(eWORKBENCH_VolumeInterpType interp_type) +{ + switch (interp_type) { + default: + BLI_assert_msg(0, "Error: Unknown lighting mode."); + ATTR_FALLTHROUGH; + case WORKBENCH_VOLUME_INTERP_LINEAR: + return "_linear"; + case WORKBENCH_VOLUME_INTERP_CUBIC: + return "_cubic"; + case WORKBENCH_VOLUME_INTERP_CLOSEST: + return "_closest"; + } +} + +static const char *workbench_texture_type_to_str(eWORKBENCH_TextureType tex_type) +{ + switch (tex_type) { + default: + BLI_assert_msg(0, "Error: Unknown texture mode."); + ATTR_FALLTHROUGH; + case TEXTURE_SH_NONE: + return "_tex_none"; + case TEXTURE_SH_TILED: + return "_tex_tile"; + case TEXTURE_SH_SINGLE: + return "_tex_single"; + } +} + +static eWORKBENCH_TextureType workbench_texture_type_get(bool textured, bool tiled) +{ + return textured ? (tiled ? TEXTURE_SH_TILED : TEXTURE_SH_SINGLE) : TEXTURE_SH_NONE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shader request + * \{ */ + +static GPUShader *workbench_shader_get_ex(WORKBENCH_PrivateData *wpd, + bool transp, + eWORKBENCH_DataType datatype, + bool textured, + bool tiled) +{ + eWORKBENCH_TextureType tex_type = workbench_texture_type_get(textured, tiled); + int light = wpd->shading.light; + BLI_assert(light < MAX_LIGHTING); + struct GPUShader **shader = + (transp) ? &e_data.transp_prepass_sh_cache[wpd->sh_cfg][datatype][light][tex_type] : + &e_data.opaque_prepass_sh_cache[wpd->sh_cfg][datatype][tex_type]; + + if (*shader == nullptr) { + std::string create_info_name = "workbench"; + create_info_name += (transp) ? "_transp" : "_opaque"; + if (transp) { + create_info_name += workbench_lighting_mode_to_str(light); + } + create_info_name += workbench_datatype_mode_to_str(datatype); + create_info_name += workbench_texture_type_to_str(tex_type); + create_info_name += (wpd->sh_cfg == GPU_SHADER_CFG_CLIPPED) ? "_clip" : "_no_clip"; + + *shader = GPU_shader_create_from_info_name(create_info_name.c_str()); + } + return *shader; +} + +GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, eWORKBENCH_DataType datatype) +{ + return workbench_shader_get_ex(wpd, false, datatype, false, false); +} + +GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd, + eWORKBENCH_DataType datatype, + bool tiled) +{ + return workbench_shader_get_ex(wpd, false, datatype, true, tiled); +} + +GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, + eWORKBENCH_DataType datatype) +{ + return workbench_shader_get_ex(wpd, true, datatype, false, false); +} + +GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd, + eWORKBENCH_DataType datatype, + bool tiled) +{ + return workbench_shader_get_ex(wpd, true, datatype, true, tiled); +} + +GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd) +{ + int light = wpd->shading.light; + struct GPUShader **shader = &e_data.opaque_composite_sh[light]; + BLI_assert(light < MAX_LIGHTING); + + if (*shader == nullptr) { + std::string create_info_name = "workbench_composite"; + create_info_name += workbench_lighting_mode_to_str(light); + *shader = GPU_shader_create_from_info_name(create_info_name.c_str()); + } + return *shader; +} + +GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *UNUSED(wpd)) +{ + if (e_data.merge_infront_sh == nullptr) { + e_data.merge_infront_sh = GPU_shader_create_from_info_name("workbench_merge_infront"); + } + return e_data.merge_infront_sh; +} + +GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *UNUSED(wpd)) +{ + if (e_data.oit_resolve_sh == nullptr) { + e_data.oit_resolve_sh = GPU_shader_create_from_info_name("workbench_transparent_resolve"); + } + return e_data.oit_resolve_sh; +} + +static GPUShader *workbench_shader_shadow_pass_get_ex(bool depth_pass, bool manifold, bool cap) +{ + struct GPUShader **shader = (depth_pass) ? &e_data.shadow_depth_pass_sh[manifold] : + &e_data.shadow_depth_fail_sh[manifold][cap]; + + if (*shader == nullptr) { + std::string create_info_name = "workbench_shadow"; + create_info_name += (depth_pass) ? "_pass" : "_fail"; + create_info_name += (manifold) ? "_manifold" : "_no_manifold"; + create_info_name += (cap) ? "_caps" : "_no_caps"; +#if DEBUG_SHADOW_VOLUME + create_info_name += "_debug"; +#endif + *shader = GPU_shader_create_from_info_name(create_info_name.c_str()); + } + return *shader; +} + +GPUShader *workbench_shader_shadow_pass_get(bool manifold) +{ + return workbench_shader_shadow_pass_get_ex(true, manifold, false); +} + +GPUShader *workbench_shader_shadow_fail_get(bool manifold, bool cap) +{ + return workbench_shader_shadow_pass_get_ex(false, manifold, cap); +} + +GPUShader *workbench_shader_cavity_get(bool cavity, bool curvature) +{ + BLI_assert(cavity || curvature); + struct GPUShader **shader = &e_data.cavity_sh[cavity][curvature]; + + if (*shader == nullptr) { + std::string create_info_name = "workbench_effect"; + create_info_name += (cavity) ? "_cavity" : ""; + create_info_name += (curvature) ? "_curvature" : ""; + *shader = GPU_shader_create_from_info_name(create_info_name.c_str()); + } + return *shader; +} + +GPUShader *workbench_shader_outline_get(void) +{ + if (e_data.outline_sh == nullptr) { + e_data.outline_sh = GPU_shader_create_from_info_name("workbench_effect_outline"); + } + return e_data.outline_sh; +} + +void workbench_shader_depth_of_field_get(GPUShader **prepare_sh, + GPUShader **downsample_sh, + GPUShader **blur1_sh, + GPUShader **blur2_sh, + GPUShader **resolve_sh) +{ + if (e_data.dof_prepare_sh == nullptr) { + e_data.dof_prepare_sh = GPU_shader_create_from_info_name("workbench_effect_dof_prepare"); + e_data.dof_downsample_sh = GPU_shader_create_from_info_name("workbench_effect_dof_downsample"); +#if 0 /* TODO(fclem): finish COC min_max optimization */ + e_data.dof_flatten_v_sh = GPU_shader_create_from_info_name("workbench_effect_dof_flatten_v"); + e_data.dof_flatten_h_sh = GPU_shader_create_from_info_name("workbench_effect_dof_flatten_h"); + e_data.dof_dilate_v_sh = GPU_shader_create_from_info_name("workbench_effect_dof_dilate_v"); + e_data.dof_dilate_h_sh = GPU_shader_create_from_info_name("workbench_effect_dof_dilate_h"); +#endif + e_data.dof_blur1_sh = GPU_shader_create_from_info_name("workbench_effect_dof_blur1"); + e_data.dof_blur2_sh = GPU_shader_create_from_info_name("workbench_effect_dof_blur2"); + e_data.dof_resolve_sh = GPU_shader_create_from_info_name("workbench_effect_dof_resolve"); + } + + *prepare_sh = e_data.dof_prepare_sh; + *downsample_sh = e_data.dof_downsample_sh; + *blur1_sh = e_data.dof_blur1_sh; + *blur2_sh = e_data.dof_blur2_sh; + *resolve_sh = e_data.dof_resolve_sh; +} + +GPUShader *workbench_shader_antialiasing_accumulation_get(void) +{ + if (e_data.aa_accum_sh == nullptr) { + e_data.aa_accum_sh = GPU_shader_create_from_info_name("workbench_taa"); + } + return e_data.aa_accum_sh; +} + +GPUShader *workbench_shader_antialiasing_get(int stage) +{ + BLI_assert(stage < 3); + GPUShader **shader = &e_data.smaa_sh[stage]; + + if (*shader == nullptr) { + std::string create_info_name = "workbench_smaa_stage_"; + create_info_name += std::to_string(stage); + *shader = GPU_shader_create_from_info_name(create_info_name.c_str()); + } + return e_data.smaa_sh[stage]; +} + +GPUShader *workbench_shader_volume_get(bool slice, + bool coba, + eWORKBENCH_VolumeInterpType interp_type, + bool smoke) +{ + GPUShader **shader = &e_data.volume_sh[slice][coba][interp_type][smoke]; + + if (*shader == nullptr) { + std::string create_info_name = "workbench_volume"; + create_info_name += (smoke) ? "_smoke" : "_object"; + create_info_name += workbench_volume_interp_to_str(interp_type); + create_info_name += (coba) ? "_coba" : "_no_coba"; + create_info_name += (slice) ? "_slice" : "_no_slice"; + *shader = GPU_shader_create_from_info_name(create_info_name.c_str()); + } + return *shader; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cleanup + * \{ */ + +void workbench_shader_free(void) +{ + for (int j = 0; j < sizeof(e_data.opaque_prepass_sh_cache) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.opaque_prepass_sh_cache[0][0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.transp_prepass_sh_cache) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.transp_prepass_sh_cache[0][0][0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < ARRAY_SIZE(e_data.opaque_composite_sh); j++) { + struct GPUShader **sh_array = &e_data.opaque_composite_sh[0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < ARRAY_SIZE(e_data.shadow_depth_pass_sh); j++) { + struct GPUShader **sh_array = &e_data.shadow_depth_pass_sh[0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.shadow_depth_fail_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.shadow_depth_fail_sh[0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.cavity_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.cavity_sh[0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < ARRAY_SIZE(e_data.smaa_sh); j++) { + struct GPUShader **sh_array = &e_data.smaa_sh[0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.volume_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.volume_sh[0][0][0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + + DRW_SHADER_FREE_SAFE(e_data.oit_resolve_sh); + DRW_SHADER_FREE_SAFE(e_data.outline_sh); + DRW_SHADER_FREE_SAFE(e_data.merge_infront_sh); + + DRW_SHADER_FREE_SAFE(e_data.dof_prepare_sh); + DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh); + DRW_SHADER_FREE_SAFE(e_data.dof_blur1_sh); + DRW_SHADER_FREE_SAFE(e_data.dof_blur2_sh); + DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh); + + DRW_SHADER_FREE_SAFE(e_data.aa_accum_sh); +} + +/** \} */ diff --git a/source/blender/draw/engines/workbench/workbench_shader_shared.h b/source/blender/draw/engines/workbench/workbench_shader_shared.h index 42d38e54d9a..c63760a634d 100644 --- a/source/blender/draw/engines/workbench/workbench_shader_shared.h +++ b/source/blender/draw/engines/workbench/workbench_shader_shared.h @@ -1,6 +1,6 @@ #ifndef GPU_SHADER -# include "gpu_shader_shared_utils.h" +# include "GPU_shader_shared_utils.h" #endif #define WORKBENCH_SHADER_SHARED_H @@ -45,4 +45,3 @@ struct WorldData { }; #define viewport_size_inv viewport_size.zw -#define packed_rough_metal roughness diff --git a/source/blender/draw/engines/workbench/workbench_transparent.c b/source/blender/draw/engines/workbench/workbench_transparent.c index 3be005b43d0..2f9b85acf31 100644 --- a/source/blender/draw/engines/workbench/workbench_transparent.c +++ b/source/blender/draw/engines/workbench/workbench_transparent.c @@ -65,7 +65,7 @@ void workbench_transparent_engine_init(WORKBENCH_Data *data) static void workbench_transparent_lighting_uniforms(WORKBENCH_PrivateData *wpd, DRWShadingGroup *grp) { - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", false); if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { @@ -76,8 +76,8 @@ static void workbench_transparent_lighting_uniforms(WORKBENCH_PrivateData *wpd, struct GPUTexture *spec_tx = wpd->studio_light->matcap_specular.gputexture; const bool use_spec = workbench_is_specular_highlight_enabled(wpd); spec_tx = (use_spec && spec_tx) ? spec_tx : diff_tx; - DRW_shgroup_uniform_texture(grp, "matcapDiffuseImage", diff_tx); - DRW_shgroup_uniform_texture(grp, "matcapSpecularImage", spec_tx); + DRW_shgroup_uniform_texture(grp, "matcap_diffuse_tx", diff_tx); + DRW_shgroup_uniform_texture(grp, "matcap_specular_tx", spec_tx); } } @@ -111,25 +111,25 @@ void workbench_transparent_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_transparent_get(wpd, data); wpd->prepass[transp][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1); workbench_transparent_lighting_uniforms(wpd, grp); wpd->prepass[transp][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */ sh = workbench_shader_transparent_image_get(wpd, data, false); wpd->prepass[transp][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ workbench_transparent_lighting_uniforms(wpd, grp); sh = workbench_shader_transparent_image_get(wpd, data, true); wpd->prepass[transp][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass); - DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ workbench_transparent_lighting_uniforms(wpd, grp); } diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index daeadce3059..9632060d69c 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -129,7 +129,7 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, float step_length = max_ff(1e-16f, dim[axis] * 0.05f); grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_float_copy(grp, "slicePosition", fds->slice_depth); DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis); DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length); @@ -148,7 +148,7 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, step_length = len_v3(dim); grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slice); DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length); DRW_shgroup_uniform_float_copy(grp, "noiseOfs", noise_ofs); @@ -272,7 +272,7 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata, const float slice_position = volume->display.slice_depth; grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_float_copy(grp, "slicePosition", slice_position); DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis); DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length); @@ -299,7 +299,7 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata, /* Set uniforms. */ grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slice); DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length); DRW_shgroup_uniform_float_copy(grp, "noiseOfs", noise_ofs); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index 7a9bdb377fe..d4491223c10 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -72,10 +72,7 @@ #include "draw_texture_pool.h" -#include "BLI_float4.hh" -#include "BLI_int2.hh" -#include "BLI_int3.hh" -#include "BLI_int4.hh" +#include "BLI_math_vec_types.hh" #include "BLI_span.hh" #include "BLI_utildefines.h" #include "BLI_utility_mixins.hh" @@ -105,7 +102,8 @@ class DataBuffer { T *data_ = nullptr; int64_t len_ = len; - BLI_STATIC_ASSERT((sizeof(T) % 16) == 0, "Type need to be aligned to size of float4."); + BLI_STATIC_ASSERT(((sizeof(T) * len) % 16) == 0, + "Buffer size need to be aligned to size of float4."); public: /** @@ -288,14 +286,18 @@ template< /** The number of values that can be stored in this uniform buffer. */ int64_t len /** True if the buffer only resides on GPU memory and cannot be accessed. */ - /* TODO(fclem): Currently unsupported. */ + /* TODO(@fclem): Currently unsupported. */ /* bool device_only = false */> class UniformArrayBuffer : public detail::UniformCommon<T, len, false> { public: UniformArrayBuffer() { - /* TODO(fclem) We should map memory instead. */ - this->data_ = MEM_mallocN_aligned(this->name_); + /* TODO(@fclem): We should map memory instead. */ + this->data_ = (T *)MEM_mallocN_aligned(len * sizeof(T), 16, this->name_); + } + ~UniformArrayBuffer() + { + MEM_freeN(this->data_); } }; @@ -303,13 +305,13 @@ template< /** Type of the values stored in this uniform buffer. */ typename T /** True if the buffer only resides on GPU memory and cannot be accessed. */ - /* TODO(fclem): Currently unsupported. */ + /* TODO(@fclem): Currently unsupported. */ /* bool device_only = false */> class UniformBuffer : public T, public detail::UniformCommon<T, 1, false> { public: UniformBuffer() { - /* TODO(fclem) How could we map this? */ + /* TODO(@fclem): How could we map this? */ this->data_ = static_cast<T *>(this); } @@ -484,7 +486,7 @@ class Texture : NonCopyable { * Ensure the texture has the correct properties. Recreating it if needed. * Return true if a texture has been created. */ - bool ensure_2d(eGPUTextureFormat format, const int2 &extent, float *data = nullptr, int mips = 1) + bool ensure_2d(eGPUTextureFormat format, int2 extent, float *data = nullptr, int mips = 1) { return ensure_impl(UNPACK2(extent), 0, mips, format, data, false, false); } @@ -493,11 +495,8 @@ class Texture : NonCopyable { * Ensure the texture has the correct properties. Recreating it if needed. * Return true if a texture has been created. */ - bool ensure_2d_array(eGPUTextureFormat format, - const int2 &extent, - int layers, - float *data = nullptr, - int mips = 1) + bool ensure_2d_array( + eGPUTextureFormat format, int2 extent, int layers, float *data = nullptr, int mips = 1) { return ensure_impl(UNPACK2(extent), layers, mips, format, data, true, false); } @@ -506,7 +505,7 @@ class Texture : NonCopyable { * Ensure the texture has the correct properties. Recreating it if needed. * Return true if a texture has been created. */ - bool ensure_3d(eGPUTextureFormat format, const int3 &extent, float *data = nullptr, int mips = 1) + bool ensure_3d(eGPUTextureFormat format, int3 extent, float *data = nullptr, int mips = 1) { return ensure_impl(UNPACK3(extent), mips, format, data, false, false); } @@ -599,14 +598,6 @@ class Texture : NonCopyable { /** * Clear the entirety of the texture using one pixel worth of data. */ - void clear(uchar4 values) - { - GPU_texture_clear(tx_, GPU_DATA_UBYTE, &values[0]); - } - - /** - * Clear the entirety of the texture using one pixel worth of data. - */ void clear(int4 values) { GPU_texture_clear(tx_, GPU_DATA_INT, &values[0]); @@ -634,6 +625,15 @@ class Texture : NonCopyable { GPU_TEXTURE_FREE_SAFE(tx_); } + /** + * Swap the content of the two textures. + */ + static void swap(Texture &a, Texture &b) + { + SWAP(GPUTexture *, a.tx_, b.tx_); + SWAP(const char *, a.name_, b.name_); + } + private: bool ensure_impl(int w, int h = 0, @@ -645,7 +645,7 @@ class Texture : NonCopyable { bool cubemap = false) { - /* TODO(fclem) In the future, we need to check if mip_count did not change. + /* TODO(@fclem): In the future, we need to check if mip_count did not change. * For now it's ok as we always define all MIP level. */ if (tx_) { int3 size = this->size(); @@ -657,7 +657,7 @@ class Texture : NonCopyable { if (tx_ == nullptr) { tx_ = create(w, h, d, mips, format, data, layered, cubemap); if (mips > 1) { - /* TODO(fclem) Remove once we have immutable storage or when mips are + /* TODO(@fclem): Remove once we have immutable storage or when mips are * generated on creation. */ GPU_texture_generate_mipmap(tx_); } @@ -678,20 +678,20 @@ class Texture : NonCopyable { if (h == 0) { return GPU_texture_create_1d(name_, w, mips, format, data); } - else if (d == 0) { + else if (cubemap) { if (layered) { - return GPU_texture_create_1d_array(name_, w, h, mips, format, data); + return GPU_texture_create_cube_array(name_, w, d, mips, format, data); } else { - return GPU_texture_create_2d(name_, w, h, mips, format, data); + return GPU_texture_create_cube(name_, w, mips, format, data); } } - else if (cubemap) { + else if (d == 0) { if (layered) { - return GPU_texture_create_cube_array(name_, w, d, mips, format, data); + return GPU_texture_create_1d_array(name_, w, h, mips, format, data); } else { - return GPU_texture_create_cube(name_, w, mips, format, data); + return GPU_texture_create_2d(name_, w, h, mips, format, data); } } else { @@ -713,7 +713,7 @@ class TextureFromPool : public Texture, NonMovable { TextureFromPool(const char *name = "gpu::Texture") : Texture(name){}; /* Always use `release()` after rendering. */ - void acquire(int w, int h, eGPUTextureFormat format, void *owner_) + void acquire(int2 extent, eGPUTextureFormat format, void *owner_) { if (this->tx_ == nullptr) { if (tx_tmp_saved_ != nullptr) { @@ -721,7 +721,7 @@ class TextureFromPool : public Texture, NonMovable { return; } DrawEngineType *owner = (DrawEngineType *)owner_; - this->tx_ = DRW_texture_pool_query_2d(w, h, format, owner); + this->tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), format, owner); } } @@ -750,11 +750,6 @@ class TextureFromPool : public Texture, NonMovable { bool ensure_cube_array(int, int, int, eGPUTextureFormat, float *) = delete; void filter_mode(bool) = delete; void free() = delete; - /** - * Forbid the use of DRW_shgroup_uniform_texture. - * Use DRW_shgroup_uniform_texture_ref instead. - */ - operator GPUTexture *() const = delete; }; /** \} */ @@ -805,6 +800,15 @@ class Framebuffer : NonCopyable { { return fb_; } + + /** + * Swap the content of the two framebuffer. + */ + static void swap(Framebuffer &a, Framebuffer &b) + { + SWAP(GPUFrameBuffer *, a.fb_, b.fb_); + SWAP(const char *, a.name_, b.name_); + } }; /** \} */ diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index b16caf49209..d531d8ad9f8 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -66,6 +66,15 @@ extern "C" { #endif +/* Uncomment to track unused resource bindings. */ +// #define DRW_UNUSED_RESOURCE_TRACKING + +#ifdef DRW_UNUSED_RESOURCE_TRACKING +# define DRW_DEBUG_FILE_LINE_ARGS , const char *file, int line +#else +# define DRW_DEBUG_FILE_LINE_ARGS +#endif + struct GPUBatch; struct GPUMaterial; struct GPUShader; @@ -293,7 +302,9 @@ DRWShaderLibrary *DRW_shader_library_create(void); /** * \warning Each library must be added after all its dependencies. */ -void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name); +void DRW_shader_library_add_file(DRWShaderLibrary *lib, + const char *lib_code, + const char *lib_name); #define DRW_SHADER_LIB_ADD(lib, lib_name) \ DRW_shader_library_add_file(lib, datatoc_##lib_name##_glsl, STRINGIFY(lib_name) ".glsl") @@ -466,6 +477,10 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, int groups_x_len, int groups_y_len, int groups_z_len); +/** + * \warning this keeps the ref to groups_ref until it actually dispatch. + */ +void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]); void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count); void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count); @@ -532,6 +547,11 @@ void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup, void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask); /** + * Issue a barrier command. + */ +void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type); + +/** * Issue a clear command. */ void DRW_shgroup_clear_framebuffer(DRWShadingGroup *shgroup, @@ -557,12 +577,12 @@ void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, struct GPUTexture **tex); -void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, - const char *name, - const struct GPUUniformBuf *ubo); -void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, - const char *name, - struct GPUUniformBuf **ubo); +void DRW_shgroup_uniform_block_ex(DRWShadingGroup *shgroup, + const char *name, + const struct GPUUniformBuf *ubo DRW_DEBUG_FILE_LINE_ARGS); +void DRW_shgroup_uniform_block_ref_ex(DRWShadingGroup *shgroup, + const char *name, + struct GPUUniformBuf **ubo DRW_DEBUG_FILE_LINE_ARGS); void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, @@ -622,9 +642,32 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, const char *name, const float (*value)[4], int arraysize); -void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, - const char *name, - struct GPUVertBuf *vertex_buffer); +void DRW_shgroup_vertex_buffer_ex(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf *vertex_buffer DRW_DEBUG_FILE_LINE_ARGS); +void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf **vertex_buffer DRW_DEBUG_FILE_LINE_ARGS); + +#ifdef DRW_UNUSED_RESOURCE_TRACKING +# define DRW_shgroup_vertex_buffer(shgroup, name, vert) \ + DRW_shgroup_vertex_buffer_ex(shgroup, name, vert, __FILE__, __LINE__) +# define DRW_shgroup_vertex_buffer_ref(shgroup, name, vert) \ + DRW_shgroup_vertex_buffer_ref_ex(shgroup, name, vert, __FILE__, __LINE__) +# define DRW_shgroup_uniform_block(shgroup, name, ubo) \ + DRW_shgroup_uniform_block_ex(shgroup, name, ubo, __FILE__, __LINE__) +# define DRW_shgroup_uniform_block_ref(shgroup, name, ubo) \ + DRW_shgroup_uniform_block_ref_ex(shgroup, name, ubo, __FILE__, __LINE__) +#else +# define DRW_shgroup_vertex_buffer(shgroup, name, vert) \ + DRW_shgroup_vertex_buffer_ex(shgroup, name, vert) +# define DRW_shgroup_vertex_buffer_ref(shgroup, name, vert) \ + DRW_shgroup_vertex_buffer_ref_ex(shgroup, name, vert) +# define DRW_shgroup_uniform_block(shgroup, name, ubo) \ + DRW_shgroup_uniform_block_ex(shgroup, name, ubo) +# define DRW_shgroup_uniform_block_ref(shgroup, name, ubo) \ + DRW_shgroup_uniform_block_ref_ex(shgroup, name, ubo) +#endif bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); @@ -696,7 +739,7 @@ const DRWView *DRW_view_default_get(void); /** * MUST only be called once per render and only in render mode. Sets default view. */ -void DRW_view_default_set(DRWView *view); +void DRW_view_default_set(const DRWView *view); /** * \warning Only use in render AND only if you are going to set view_default again. */ @@ -704,7 +747,7 @@ void DRW_view_reset(void); /** * Set active view for rendering. */ -void DRW_view_set_active(DRWView *view); +void DRW_view_set_active(const DRWView *view); const DRWView *DRW_view_get_active(void); /** diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 1110658e3b2..430b28f1224 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -19,7 +19,7 @@ */ #include "DNA_curve_types.h" -#include "DNA_hair_types.h" +#include "DNA_curves_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" @@ -835,7 +835,7 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold) return NULL; case OB_MBALL: return DRW_cache_mball_edge_detection_get(ob, r_is_manifold); - case OB_HAIR: + case OB_CURVES: return NULL; case OB_POINTCLOUD: return NULL; @@ -859,7 +859,7 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob) return NULL; case OB_MBALL: return DRW_cache_mball_face_wireframe_get(ob); - case OB_HAIR: + case OB_CURVES: return NULL; case OB_POINTCLOUD: return DRW_pointcloud_batch_cache_get_dots(ob); @@ -886,7 +886,7 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob) return NULL; case OB_MBALL: return NULL; - case OB_HAIR: + case OB_CURVES: return NULL; case OB_POINTCLOUD: return NULL; @@ -910,7 +910,7 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob) return NULL; case OB_MBALL: return DRW_cache_mball_surface_get(ob); - case OB_HAIR: + case OB_CURVES: return NULL; case OB_POINTCLOUD: return DRW_cache_pointcloud_surface_get(ob); @@ -935,7 +935,7 @@ GPUVertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob) return DRW_curve_batch_cache_pos_vertbuf_get(ob->data); case OB_MBALL: return DRW_mball_batch_cache_pos_vertbuf_get(ob); - case OB_HAIR: + case OB_CURVES: return NULL; case OB_POINTCLOUD: return NULL; @@ -960,15 +960,15 @@ int DRW_cache_object_material_count_get(struct Object *ob) switch (type) { case OB_MESH: - return DRW_mesh_material_count_get((me != NULL) ? me : ob->data); + return DRW_mesh_material_count_get(ob, (me != NULL) ? me : ob->data); case OB_CURVE: case OB_SURF: case OB_FONT: return DRW_curve_material_count_get(ob->data); case OB_MBALL: return DRW_metaball_material_count_get(ob->data); - case OB_HAIR: - return DRW_hair_material_count_get(ob->data); + case OB_CURVES: + return DRW_curves_material_count_get(ob->data); case OB_POINTCLOUD: return DRW_pointcloud_material_count_get(ob->data); case OB_VOLUME: @@ -994,7 +994,7 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, return NULL; case OB_MBALL: return DRW_cache_mball_surface_shaded_get(ob, gpumat_array, gpumat_array_len); - case OB_HAIR: + case OB_CURVES: return NULL; case OB_POINTCLOUD: return DRW_cache_pointcloud_surface_shaded_get(ob, gpumat_array, gpumat_array_len); @@ -2875,7 +2875,7 @@ GPUBatch *DRW_cache_mesh_surface_get(Object *ob) GPUBatch *DRW_cache_mesh_surface_edges_get(Object *ob) { BLI_assert(ob->type == OB_MESH); - return DRW_mesh_batch_cache_get_surface_edges(ob->data); + return DRW_mesh_batch_cache_get_surface_edges(ob, ob->data); } GPUBatch **DRW_cache_mesh_surface_shaded_get(Object *ob, @@ -2883,31 +2883,31 @@ GPUBatch **DRW_cache_mesh_surface_shaded_get(Object *ob, uint gpumat_array_len) { BLI_assert(ob->type == OB_MESH); - return DRW_mesh_batch_cache_get_surface_shaded(ob->data, gpumat_array, gpumat_array_len); + return DRW_mesh_batch_cache_get_surface_shaded(ob, ob->data, gpumat_array, gpumat_array_len); } GPUBatch **DRW_cache_mesh_surface_texpaint_get(Object *ob) { BLI_assert(ob->type == OB_MESH); - return DRW_mesh_batch_cache_get_surface_texpaint(ob->data); + return DRW_mesh_batch_cache_get_surface_texpaint(ob, ob->data); } GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(Object *ob) { BLI_assert(ob->type == OB_MESH); - return DRW_mesh_batch_cache_get_surface_texpaint_single(ob->data); + return DRW_mesh_batch_cache_get_surface_texpaint_single(ob, ob->data); } GPUBatch *DRW_cache_mesh_surface_vertpaint_get(Object *ob) { BLI_assert(ob->type == OB_MESH); - return DRW_mesh_batch_cache_get_surface_vertpaint(ob->data); + return DRW_mesh_batch_cache_get_surface_vertpaint(ob, ob->data); } GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(Object *ob) { BLI_assert(ob->type == OB_MESH); - return DRW_mesh_batch_cache_get_surface_sculpt(ob->data); + return DRW_mesh_batch_cache_get_surface_sculpt(ob, ob->data); } GPUBatch *DRW_cache_mesh_surface_weights_get(Object *ob) @@ -3091,7 +3091,7 @@ GPUBatch **DRW_cache_surf_surface_shaded_get(Object *ob, struct Curve *cu = ob->data; struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(ob); if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); + return DRW_mesh_batch_cache_get_surface_shaded(ob, mesh_eval, gpumat_array, gpumat_array_len); } return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); @@ -3385,7 +3385,7 @@ void drw_batch_cache_validate(Object *ob) struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(ob); switch (ob->type) { case OB_MESH: - DRW_mesh_batch_cache_validate((Mesh *)ob->data); + DRW_mesh_batch_cache_validate(ob, (Mesh *)ob->data); break; case OB_CURVE: case OB_FONT: @@ -3393,7 +3393,7 @@ void drw_batch_cache_validate(Object *ob) break; case OB_SURF: if (mesh_eval != NULL) { - DRW_mesh_batch_cache_validate(mesh_eval); + DRW_mesh_batch_cache_validate(ob, mesh_eval); } DRW_curve_batch_cache_validate((Curve *)ob->data); break; @@ -3403,8 +3403,8 @@ void drw_batch_cache_validate(Object *ob) case OB_LATTICE: DRW_lattice_batch_cache_validate((Lattice *)ob->data); break; - case OB_HAIR: - DRW_hair_batch_cache_validate((Hair *)ob->data); + case OB_CURVES: + DRW_curves_batch_cache_validate((Curves *)ob->data); break; case OB_POINTCLOUD: DRW_pointcloud_batch_cache_validate((PointCloud *)ob->data); diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 30e5a10df91..b94dd466364 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -246,14 +246,14 @@ struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob, struct GPUBatch *DRW_cache_mball_face_wireframe_get(struct Object *ob); struct GPUBatch *DRW_cache_mball_edge_detection_get(struct Object *ob, bool *r_is_manifold); -/* Hair */ - -struct GPUBatch *DRW_cache_hair_surface_get(struct Object *ob); -struct GPUBatch **DRW_cache_hair_surface_shaded_get(struct Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len); -struct GPUBatch *DRW_cache_hair_face_wireframe_get(struct Object *ob); -struct GPUBatch *DRW_cache_hair_edge_detection_get(struct Object *ob, bool *r_is_manifold); +/* Curves */ + +struct GPUBatch *DRW_cache_curves_surface_get(struct Object *ob); +struct GPUBatch **DRW_cache_curves_surface_shaded_get(struct Object *ob, + struct GPUMaterial **gpumat_array, + uint gpumat_array_len); +struct GPUBatch *DRW_cache_curves_face_wireframe_get(struct Object *ob); +struct GPUBatch *DRW_cache_curves_edge_detection_get(struct Object *ob, bool *r_is_manifold); /* PointCloud */ diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index bd4edaf09fb..e7f66ebacd0 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -28,6 +28,7 @@ struct TaskGraph; #include "DNA_customdata_types.h" #include "BKE_attribute.h" +#include "BKE_object.h" #include "GPU_batch.h" #include "GPU_index_buffer.h" @@ -107,11 +108,13 @@ ENUM_OPERATORS(eMRDataType, MR_DATA_POLYS_SORTED) extern "C" { #endif -BLI_INLINE int mesh_render_mat_len_get(const Mesh *me) +BLI_INLINE int mesh_render_mat_len_get(const Object *object, const Mesh *me) { - /* In edit mode, the displayed mesh is stored in the edit-mesh. */ - if (me->edit_mesh && me->edit_mesh->mesh_eval_final) { - return MAX2(1, me->edit_mesh->mesh_eval_final->totcol); + if (me->edit_mesh != NULL) { + const Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object); + if (editmesh_eval_final != NULL) { + return MAX2(1, editmesh_eval_final->totcol); + } } return MAX2(1, me->totcol); } @@ -328,6 +331,7 @@ typedef struct MeshBatchCache { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache *mbc, + Object *object, Mesh *me, bool is_editmode, bool is_paint_mode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index fe55778527b..987ddf3a938 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -570,6 +570,7 @@ static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *t static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache *mbc, + Object *object, Mesh *me, const bool is_editmode, @@ -615,7 +616,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, */ const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || GPU_use_hq_normals_workaround(); - const bool override_single_mat = mesh_render_mat_len_get(me) <= 1; + const bool override_single_mat = mesh_render_mat_len_get(object, me) <= 1; /* Create an array containing all the extractors that needs to be executed. */ ExtractorRunDatas extractors; @@ -700,7 +701,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, #endif MeshRenderData *mr = mesh_render_data_create( - me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts); + object, me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts); mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; mr->use_final_mesh = do_final; @@ -902,6 +903,7 @@ extern "C" { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache *mbc, + Object *object, Mesh *me, const bool is_editmode, @@ -918,6 +920,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, blender::draw::mesh_buffer_cache_create_requested(task_graph, cache, mbc, + object, me, is_editmode, is_paint_mode, 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 29430c46d93..a47a124bd24 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 @@ -438,7 +438,8 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_ } } -MeshRenderData *mesh_render_data_create(Mesh *me, +MeshRenderData *mesh_render_data_create(Object *object, + Mesh *me, const bool is_editmode, const bool is_paint_mode, const bool is_mode_active, @@ -449,15 +450,18 @@ MeshRenderData *mesh_render_data_create(Mesh *me, { MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); mr->toolsettings = ts; - mr->mat_len = mesh_render_mat_len_get(me); + mr->mat_len = mesh_render_mat_len_get(object, me); copy_m4_m4(mr->obmat, obmat); if (is_editmode) { - BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object); + Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(object); + + BLI_assert(editmesh_eval_cage && editmesh_eval_final); mr->bm = me->edit_mesh->bm; mr->edit_bmesh = me->edit_mesh; - mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; + mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage; mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; if (mr->edit_data) { @@ -507,7 +511,7 @@ MeshRenderData *mesh_render_data_create(Mesh *me, /* Seems like the mesh_eval_final do not have the right origin indices. * Force not mapped in this case. */ - if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { + if (has_mdata && do_final && editmesh_eval_final != editmesh_eval_cage) { // mr->edit_bmesh = NULL; mr->extract_type = MR_EXTRACT_MESH; } diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 77b507bc2b6..9c6814d910e 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -33,7 +33,7 @@ struct ParticleSystem; struct TaskGraph; struct Curve; -struct Hair; +struct Curves; struct Lattice; struct Mesh; struct MetaBall; @@ -60,7 +60,7 @@ void DRW_curve_batch_cache_validate(struct Curve *cu); void DRW_curve_batch_cache_free(struct Curve *cu); void DRW_mesh_batch_cache_dirty_tag(struct Mesh *me, eMeshBatchDirtyMode mode); -void DRW_mesh_batch_cache_validate(struct Mesh *me); +void DRW_mesh_batch_cache_validate(struct Object *object, struct Mesh *me); void DRW_mesh_batch_cache_free(struct Mesh *me); void DRW_lattice_batch_cache_dirty_tag(struct Lattice *lt, int mode); @@ -73,9 +73,9 @@ void DRW_particle_batch_cache_free(struct ParticleSystem *psys); void DRW_gpencil_batch_cache_dirty_tag(struct bGPdata *gpd); void DRW_gpencil_batch_cache_free(struct bGPdata *gpd); -void DRW_hair_batch_cache_dirty_tag(struct Hair *hair, int mode); -void DRW_hair_batch_cache_validate(struct Hair *hair); -void DRW_hair_batch_cache_free(struct Hair *hair); +void DRW_curves_batch_cache_dirty_tag(struct Curves *curves, int mode); +void DRW_curves_batch_cache_validate(struct Curves *curves); +void DRW_curves_batch_cache_free(struct Curves *curves); void DRW_pointcloud_batch_cache_dirty_tag(struct PointCloud *pointcloud, int mode); void DRW_pointcloud_batch_cache_validate(struct PointCloud *pointcloud); @@ -188,7 +188,7 @@ struct GPUBatch *DRW_lattice_batch_cache_get_edit_verts(struct Lattice *lt); /** \name Hair * \{ */ -int DRW_hair_material_count_get(struct Hair *hair); +int DRW_curves_material_count_get(struct Curves *curves); /** \} */ @@ -236,14 +236,18 @@ struct GPUBatch *DRW_mesh_batch_cache_get_all_edges(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_loose_edges(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_edge_detection(struct Mesh *me, bool *r_is_manifold); struct GPUBatch *DRW_mesh_batch_cache_get_surface(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_surface_edges(struct Mesh *me); -struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Mesh *me, +struct GPUBatch *DRW_mesh_batch_cache_get_surface_edges(struct Object *object, struct Mesh *me); +struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Object *object, + struct Mesh *me, struct GPUMaterial **gpumat_array, uint gpumat_array_len); -struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(struct Mesh *me); +struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Object *object, + struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Object *object, + struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Object *object, + struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(struct Object *object, struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_sculpt_overlays(struct Mesh *me); @@ -293,14 +297,16 @@ struct GPUBatch *DRW_mesh_batch_cache_get_wireframes_face(struct Mesh *me); * The `cache->tot_area` and cache->tot_uv_area` update are calculation are * only valid after calling `DRW_mesh_batch_cache_create_requested`. */ -struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(struct Mesh *me, +struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(struct Object *object, + struct Mesh *me, float **tot_area, float **tot_uv_area); -struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(struct Object *object, + struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(struct Object *object, struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(struct Object *object, struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(struct Object *object, struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Object *object, struct Mesh *me); /** \} */ @@ -308,7 +314,7 @@ struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Mesh *me); /** \name For Image UV Editor * \{ */ -struct GPUBatch *DRW_mesh_batch_cache_get_uv_edges(struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_uv_edges(struct Object *object, struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(struct Mesh *me); /** \} */ @@ -321,7 +327,7 @@ struct GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(struct Mesh *me); struct GPUVertBuf *DRW_curve_batch_cache_pos_vertbuf_get(struct Curve *cu); struct GPUVertBuf *DRW_mball_batch_cache_pos_vertbuf_get(struct Object *ob); -int DRW_mesh_material_count_get(const struct Mesh *me); +int DRW_mesh_material_count_get(const struct Object *object, const struct Mesh *me); /* See 'common_globals_lib.glsl' for duplicate defines. */ diff --git a/source/blender/draw/intern/draw_cache_impl_hair.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index d236f0c2f59..8ec97495fcf 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -29,13 +29,16 @@ #include "BLI_listbase.h" #include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" #include "BLI_math_vector.h" +#include "BLI_math_vector.hh" +#include "BLI_span.hh" #include "BLI_utildefines.h" -#include "DNA_hair_types.h" +#include "DNA_curves_types.h" #include "DNA_object_types.h" -#include "BKE_hair.h" +#include "BKE_curves.h" #include "GPU_batch.h" #include "GPU_material.h" @@ -44,7 +47,11 @@ #include "draw_cache_impl.h" /* own include */ #include "draw_hair_private.h" /* own include */ -static void hair_batch_cache_clear(Hair *hair); +using blender::float3; +using blender::IndexRange; +using blender::Span; + +static void curves_batch_cache_clear(Curves *curves); /* ---------------------------------------------------------------------- */ /* Hair GPUBatch Cache */ @@ -58,19 +65,19 @@ struct HairBatchCache { /* GPUBatch cache management. */ -static bool hair_batch_cache_valid(Hair *hair) +static bool curves_batch_cache_valid(Curves *curves) { - HairBatchCache *cache = static_cast<HairBatchCache *>(hair->batch_cache); + HairBatchCache *cache = static_cast<HairBatchCache *>(curves->batch_cache); return (cache && cache->is_dirty == false); } -static void hair_batch_cache_init(Hair *hair) +static void curves_batch_cache_init(Curves *curves) { - HairBatchCache *cache = static_cast<HairBatchCache *>(hair->batch_cache); + HairBatchCache *cache = static_cast<HairBatchCache *>(curves->batch_cache); if (!cache) { cache = MEM_cnew<HairBatchCache>(__func__); - hair->batch_cache = cache; + curves->batch_cache = cache; } else { memset(cache, 0, sizeof(*cache)); @@ -79,28 +86,28 @@ static void hair_batch_cache_init(Hair *hair) cache->is_dirty = false; } -void DRW_hair_batch_cache_validate(Hair *hair) +void DRW_curves_batch_cache_validate(Curves *curves) { - if (!hair_batch_cache_valid(hair)) { - hair_batch_cache_clear(hair); - hair_batch_cache_init(hair); + if (!curves_batch_cache_valid(curves)) { + curves_batch_cache_clear(curves); + curves_batch_cache_init(curves); } } -static HairBatchCache *hair_batch_cache_get(Hair *hair) +static HairBatchCache *curves_batch_cache_get(Curves *curves) { - DRW_hair_batch_cache_validate(hair); - return static_cast<HairBatchCache *>(hair->batch_cache); + DRW_curves_batch_cache_validate(curves); + return static_cast<HairBatchCache *>(curves->batch_cache); } -void DRW_hair_batch_cache_dirty_tag(Hair *hair, int mode) +void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode) { - HairBatchCache *cache = static_cast<HairBatchCache *>(hair->batch_cache); + HairBatchCache *cache = static_cast<HairBatchCache *>(curves->batch_cache); if (cache == nullptr) { return; } switch (mode) { - case BKE_HAIR_BATCH_DIRTY_ALL: + case BKE_CURVES_BATCH_DIRTY_ALL: cache->is_dirty = true; break; default: @@ -108,9 +115,9 @@ void DRW_hair_batch_cache_dirty_tag(Hair *hair, int mode) } } -static void hair_batch_cache_clear(Hair *hair) +static void curves_batch_cache_clear(Curves *curves) { - HairBatchCache *cache = static_cast<HairBatchCache *>(hair->batch_cache); + HairBatchCache *cache = static_cast<HairBatchCache *>(curves->batch_cache); if (!cache) { return; } @@ -118,69 +125,67 @@ static void hair_batch_cache_clear(Hair *hair) particle_batch_cache_clear_hair(&cache->hair); } -void DRW_hair_batch_cache_free(Hair *hair) +void DRW_curves_batch_cache_free(Curves *curves) { - hair_batch_cache_clear(hair); - MEM_SAFE_FREE(hair->batch_cache); + curves_batch_cache_clear(curves); + MEM_SAFE_FREE(curves->batch_cache); } -static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache) +static void ensure_seg_pt_count(Curves *curves, ParticleHairCache *curves_cache) { - if ((hair_cache->pos != nullptr && hair_cache->indices != nullptr) || - (hair_cache->proc_point_buf != nullptr)) { + if ((curves_cache->pos != nullptr && curves_cache->indices != nullptr) || + (curves_cache->proc_point_buf != nullptr)) { return; } - hair_cache->strands_len = 0; - hair_cache->elems_len = 0; - hair_cache->point_len = 0; - - HairCurve *curve = hair->curves; - int num_curves = hair->totcurve; - for (int i = 0; i < num_curves; i++, curve++) { - hair_cache->strands_len++; - hair_cache->elems_len += curve->numpoints + 1; - hair_cache->point_len += curve->numpoints; - } + curves_cache->strands_len = curves->geometry.curve_size; + curves_cache->elems_len = curves->geometry.point_size + curves->geometry.curve_size; + curves_cache->point_len = curves->geometry.point_size; } -static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, - GPUVertBufRaw *attr_step, - GPUVertBufRaw *length_step) +static void curves_batch_cache_fill_segments_proc_pos(Curves *curves, + GPUVertBufRaw *attr_step, + GPUVertBufRaw *length_step) { /* TODO: use hair radius layer if available. */ - HairCurve *curve = hair->curves; - int num_curves = hair->totcurve; - for (int i = 0; i < num_curves; i++, curve++) { - float(*curve_co)[3] = hair->co + curve->firstpoint; + const int curve_size = curves->geometry.curve_size; + Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1}; + + Span<float3> positions{(float3 *)curves->geometry.position, curves->geometry.point_size}; + + for (const int i : IndexRange(curve_size)) { + const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); + + Span<float3> spline_positions = positions.slice(curve_range); float total_len = 0.0f; - float *co_prev = nullptr, *seg_data_first; - for (int j = 0; j < curve->numpoints; j++) { + float *seg_data_first; + for (const int i_spline : spline_positions.index_range()) { float *seg_data = (float *)GPU_vertbuf_raw_step(attr_step); - copy_v3_v3(seg_data, curve_co[j]); - if (co_prev) { - total_len += len_v3v3(co_prev, curve_co[j]); + copy_v3_v3(seg_data, spline_positions[i_spline]); + if (i_spline == 0) { + seg_data_first = seg_data; } else { - seg_data_first = seg_data; + total_len += blender::math::distance(spline_positions[i_spline - 1], + spline_positions[i_spline]); } seg_data[3] = total_len; - co_prev = curve_co[j]; } /* Assign length value. */ *(float *)GPU_vertbuf_raw_step(length_step) = total_len; if (total_len > 0.0f) { /* Divide by total length to have a [0-1] number. */ - for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) { + for ([[maybe_unused]] const int i_spline : spline_positions.index_range()) { seg_data_first[3] /= total_len; + seg_data_first += 4; } } } } -static void hair_batch_cache_ensure_procedural_pos(Hair *hair, - ParticleHairCache *cache, - GPUMaterial *gpu_material) +static void curves_batch_cache_ensure_procedural_pos(Curves *curves, + ParticleHairCache *cache, + GPUMaterial *gpu_material) { if (cache->proc_point_buf == nullptr) { /* initialize vertex format */ @@ -203,7 +208,7 @@ static void hair_batch_cache_ensure_procedural_pos(Hair *hair, GPUVertBufRaw length_step; GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step); - hair_batch_cache_fill_segments_proc_pos(hair, &point_step, &length_step); + curves_batch_cache_fill_segments_proc_pos(curves, &point_step, &length_step); /* Create vbo immediately to bind to texture buffer. */ GPU_vertbuf_use(cache->proc_point_buf); @@ -222,19 +227,23 @@ static void hair_batch_cache_ensure_procedural_pos(Hair *hair, } } -static void hair_batch_cache_fill_strands_data(Hair *hair, - GPUVertBufRaw *data_step, - GPUVertBufRaw *seg_step) +static void curves_batch_cache_fill_strands_data(Curves *curves, + GPUVertBufRaw *data_step, + GPUVertBufRaw *seg_step) { - HairCurve *curve = hair->curves; - int num_curves = hair->totcurve; - for (int i = 0; i < num_curves; i++, curve++) { - *(uint *)GPU_vertbuf_raw_step(data_step) = curve->firstpoint; - *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve->numpoints - 1; + const int curve_size = curves->geometry.curve_size; + Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1}; + + for (const int i : IndexRange(curve_size)) { + const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); + + *(uint *)GPU_vertbuf_raw_step(data_step) = curve_range.start(); + *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve_range.size() - 1; } } -static void hair_batch_cache_ensure_procedural_strand_data(Hair *hair, ParticleHairCache *cache) +static void curves_batch_cache_ensure_procedural_strand_data(Curves *curves, + ParticleHairCache *cache) { GPUVertBufRaw data_step, seg_step; @@ -253,18 +262,18 @@ static void hair_batch_cache_ensure_procedural_strand_data(Hair *hair, ParticleH GPU_vertbuf_data_alloc(cache->proc_strand_seg_buf, cache->strands_len); GPU_vertbuf_attr_get_raw_data(cache->proc_strand_seg_buf, seg_id, &seg_step); - hair_batch_cache_fill_strands_data(hair, &data_step, &seg_step); + curves_batch_cache_fill_strands_data(curves, &data_step, &seg_step); /* Create vbo immediately to bind to texture buffer. */ GPU_vertbuf_use(cache->proc_strand_buf); - cache->strand_tex = GPU_texture_create_from_vertbuf("hair_strand", cache->proc_strand_buf); + cache->strand_tex = GPU_texture_create_from_vertbuf("curves_strand", cache->proc_strand_buf); GPU_vertbuf_use(cache->proc_strand_seg_buf); - cache->strand_seg_tex = GPU_texture_create_from_vertbuf("hair_strand_seg", + cache->strand_seg_tex = GPU_texture_create_from_vertbuf("curves_strand_seg", cache->proc_strand_seg_buf); } -static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *cache, int subdiv) +static void curves_batch_cache_ensure_procedural_final_points(ParticleHairCache *cache, int subdiv) { /* Same format as point_tex. */ GPUVertFormat format = {0}; @@ -285,14 +294,15 @@ static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *c cache->final[subdiv].proc_buf); } -static void hair_batch_cache_fill_segments_indices(Hair *hair, - const int res, - GPUIndexBufBuilder *elb) +static void curves_batch_cache_fill_segments_indices(Curves *curves, + const int res, + GPUIndexBufBuilder *elb) { - HairCurve *curve = hair->curves; - int num_curves = hair->totcurve; + const int curve_size = curves->geometry.curve_size; + uint curr_point = 0; - for (int i = 0; i < num_curves; i++, curve++) { + + for ([[maybe_unused]] const int i : IndexRange(curve_size)) { for (int k = 0; k < res; k++) { GPU_indexbuf_add_generic_vert(elb, curr_point++); } @@ -300,10 +310,10 @@ static void hair_batch_cache_fill_segments_indices(Hair *hair, } } -static void hair_batch_cache_ensure_procedural_indices(Hair *hair, - ParticleHairCache *cache, - int thickness_res, - int subdiv) +static void curves_batch_cache_ensure_procedural_indices(Curves *curves, + ParticleHairCache *cache, + int thickness_res, + int subdiv) { BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */ @@ -328,7 +338,7 @@ static void hair_batch_cache_ensure_procedural_indices(Hair *hair, GPUIndexBufBuilder elb; GPU_indexbuf_init_ex(&elb, prim_type, element_count, element_count); - hair_batch_cache_fill_segments_indices(hair, verts_per_hair, &elb); + curves_batch_cache_fill_segments_indices(curves, verts_per_hair, &elb); cache->final[subdiv].proc_hairs[thickness_res - 1] = GPU_batch_create_ex( prim_type, vbo, GPU_indexbuf_build(&elb), GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX); @@ -341,9 +351,9 @@ bool hair_ensure_procedural_data(Object *object, int thickness_res) { bool need_ft_update = false; - Hair *hair = static_cast<Hair *>(object->data); + Curves *curves = static_cast<Curves *>(object->data); - HairBatchCache *cache = hair_batch_cache_get(hair); + HairBatchCache *cache = curves_batch_cache_get(curves); *r_hair_cache = &cache->hair; const int steps = 2; /* TODO: don't hard-code? */ @@ -351,29 +361,29 @@ bool hair_ensure_procedural_data(Object *object, /* Refreshed on combing and simulation. */ if ((*r_hair_cache)->proc_point_buf == nullptr) { - ensure_seg_pt_count(hair, &cache->hair); - hair_batch_cache_ensure_procedural_pos(hair, &cache->hair, gpu_material); + ensure_seg_pt_count(curves, &cache->hair); + curves_batch_cache_ensure_procedural_pos(curves, &cache->hair, gpu_material); need_ft_update = true; } /* Refreshed if active layer or custom data changes. */ if ((*r_hair_cache)->strand_tex == nullptr) { - hair_batch_cache_ensure_procedural_strand_data(hair, &cache->hair); + curves_batch_cache_ensure_procedural_strand_data(curves, &cache->hair); } /* Refreshed only on subdiv count change. */ if ((*r_hair_cache)->final[subdiv].proc_buf == nullptr) { - hair_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv); + curves_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv); need_ft_update = true; } if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == nullptr) { - hair_batch_cache_ensure_procedural_indices(hair, &cache->hair, thickness_res, subdiv); + curves_batch_cache_ensure_procedural_indices(curves, &cache->hair, thickness_res, subdiv); } return need_ft_update; } -int DRW_hair_material_count_get(Hair *hair) +int DRW_curves_material_count_get(Curves *curves) { - return max_ii(1, hair->totcol); + return max_ii(1, curves->totcol); } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 1e5ffc14911..e7e0e97499f 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -280,9 +280,16 @@ BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a) *((uint32_t *)a) = 0; } -BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me) +BLI_INLINE const Mesh *editmesh_final_or_this(const Object *object, const Mesh *me) { - return (me->edit_mesh && me->edit_mesh->mesh_eval_final) ? me->edit_mesh->mesh_eval_final : me; + if (me->edit_mesh != NULL) { + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object); + if (editmesh_eval_final != NULL) { + return editmesh_eval_final; + } + } + + return me; } static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *cd_used) @@ -443,9 +450,11 @@ BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me) return &me->vdata; } -static void mesh_cd_calc_active_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used) +static void mesh_cd_calc_active_uv_layer(const Object *object, + const Mesh *me, + DRW_MeshCDMask *cd_used) { - const Mesh *me_final = editmesh_final_or_this(me); + const Mesh *me_final = editmesh_final_or_this(object, me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV); if (layer != -1) { @@ -453,9 +462,11 @@ static void mesh_cd_calc_active_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used } } -static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used) +static void mesh_cd_calc_active_mask_uv_layer(const Object *object, + const Mesh *me, + DRW_MeshCDMask *cd_used) { - const Mesh *me_final = editmesh_final_or_this(me); + const Mesh *me_final = editmesh_final_or_this(object, me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); int layer = CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV); if (layer != -1) { @@ -463,9 +474,11 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd } } -static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *attrs_used) +static void mesh_cd_calc_active_vcol_layer(const Object *object, + const Mesh *me, + DRW_MeshAttributes *attrs_used) { - const Mesh *me_final = editmesh_final_or_this(me); + const Mesh *me_final = editmesh_final_or_this(object, me); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR); @@ -474,9 +487,11 @@ static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *a } } -static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used) +static void mesh_cd_calc_active_mloopcol_layer(const Object *object, + const Mesh *me, + DRW_MeshCDMask *cd_used) { - const Mesh *me_final = editmesh_final_or_this(me); + const Mesh *me_final = editmesh_final_or_this(object, me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL); @@ -490,8 +505,9 @@ static bool custom_data_match_attribute(const CustomData *custom_data, int *r_layer_index, int *r_type) { - const int possible_attribute_types[6] = { + const int possible_attribute_types[7] = { CD_PROP_BOOL, + CD_PROP_INT8, CD_PROP_INT32, CD_PROP_FLOAT, CD_PROP_FLOAT2, @@ -514,12 +530,13 @@ static bool custom_data_match_attribute(const CustomData *custom_data, return false; } -static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, +static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, + const Mesh *me, struct GPUMaterial **gpumat_array, int gpumat_array_len, DRW_MeshAttributes *attributes) { - const Mesh *me_final = editmesh_final_or_this(me); + const Mesh *me_final = editmesh_final_or_this(object, me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); const CustomData *cd_pdata = mesh_cd_pdata_get_from_mesh(me_final); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); @@ -639,6 +656,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, break; } case CD_PROP_BOOL: + case CD_PROP_INT8: case CD_PROP_INT32: case CD_PROP_FLOAT: case CD_PROP_FLOAT2: @@ -793,7 +811,7 @@ BLI_INLINE void mesh_batch_cache_add_request(MeshBatchCache *cache, DRWBatchFlag /* GPUBatch cache management. */ -static bool mesh_batch_cache_valid(Mesh *me) +static bool mesh_batch_cache_valid(Object *object, Mesh *me) { MeshBatchCache *cache = me->runtime.batch_cache; @@ -809,14 +827,14 @@ static bool mesh_batch_cache_valid(Mesh *me) return false; } - if (cache->mat_len != mesh_render_mat_len_get(me)) { + if (cache->mat_len != mesh_render_mat_len_get(object, me)) { return false; } return true; } -static void mesh_batch_cache_init(Mesh *me) +static void mesh_batch_cache_init(Object *object, Mesh *me) { MeshBatchCache *cache = me->runtime.batch_cache; @@ -836,7 +854,7 @@ static void mesh_batch_cache_init(Mesh *me) // cache->vert_len = mesh_render_verts_len_get(me); } - cache->mat_len = mesh_render_mat_len_get(me); + cache->mat_len = mesh_render_mat_len_get(object, me); cache->surface_per_mat = MEM_callocN(sizeof(*cache->surface_per_mat) * cache->mat_len, __func__); cache->tris_per_mat = MEM_callocN(sizeof(*cache->tris_per_mat) * cache->mat_len, __func__); @@ -847,11 +865,11 @@ static void mesh_batch_cache_init(Mesh *me) drw_mesh_weight_state_clear(&cache->weight_state); } -void DRW_mesh_batch_cache_validate(Mesh *me) +void DRW_mesh_batch_cache_validate(Object *object, Mesh *me) { - if (!mesh_batch_cache_valid(me)) { + if (!mesh_batch_cache_valid(object, me)) { mesh_batch_cache_clear(me); - mesh_batch_cache_init(me); + mesh_batch_cache_init(object, me); } } @@ -1095,24 +1113,24 @@ void DRW_mesh_batch_cache_free(Mesh *me) /** \name Public API * \{ */ -static void texpaint_request_active_uv(MeshBatchCache *cache, Mesh *me) +static void texpaint_request_active_uv(MeshBatchCache *cache, Object *object, Mesh *me) { DRW_MeshCDMask cd_needed; mesh_cd_layers_type_clear(&cd_needed); - mesh_cd_calc_active_uv_layer(me, &cd_needed); + mesh_cd_calc_active_uv_layer(object, me, &cd_needed); BLI_assert(cd_needed.uv != 0 && "No uv layer available in texpaint, but batches requested anyway!"); - mesh_cd_calc_active_mask_uv_layer(me, &cd_needed); + mesh_cd_calc_active_mask_uv_layer(object, me, &cd_needed); mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); } -static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me) +static void texpaint_request_active_vcol(MeshBatchCache *cache, Object *object, Mesh *me) { DRW_MeshCDMask cd_needed; mesh_cd_layers_type_clear(&cd_needed); - mesh_cd_calc_active_mloopcol_layer(me, &cd_needed); + mesh_cd_calc_active_mloopcol_layer(object, me, &cd_needed); BLI_assert(cd_needed.vcol != 0 && "No MLOOPCOL layer available in vertpaint, but batches requested anyway!"); @@ -1120,11 +1138,11 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me) mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); } -static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me) +static void sculpt_request_active_vcol(MeshBatchCache *cache, Object *object, Mesh *me) { DRW_MeshAttributes attrs_needed; drw_mesh_attributes_clear(&attrs_needed); - mesh_cd_calc_active_vcol_layer(me, &attrs_needed); + mesh_cd_calc_active_vcol_layer(object, me, &attrs_needed); BLI_assert(attrs_needed.num_requests != 0 && "No MPropCol layer available in Sculpt, but batches requested anyway!"); @@ -1197,7 +1215,8 @@ GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(Mesh *me) return DRW_batch_request(&cache->batch.edit_mesh_analysis); } -GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, +GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object, + Mesh *me, struct GPUMaterial **gpumat_array, uint gpumat_array_len) { @@ -1205,7 +1224,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, DRW_MeshAttributes attrs_needed; drw_mesh_attributes_clear(&attrs_needed); DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( - me, gpumat_array, gpumat_array_len, &attrs_needed); + object, me, gpumat_array, gpumat_array_len, &attrs_needed); BLI_assert(gpumat_array_len == cache->mat_len); @@ -1216,41 +1235,41 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, return cache->surface_per_mat; } -GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me) +GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - texpaint_request_active_uv(cache, me); + texpaint_request_active_uv(cache, object, me); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } -GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - texpaint_request_active_uv(cache, me); + texpaint_request_active_uv(cache, object, me); mesh_batch_cache_request_surface_batches(cache); return cache->batch.surface; } -GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - texpaint_request_active_vcol(cache, me); + texpaint_request_active_vcol(cache, object, me); mesh_batch_cache_request_surface_batches(cache); return cache->batch.surface; } -GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - sculpt_request_active_vcol(cache, me); + sculpt_request_active_vcol(cache, object, me); mesh_batch_cache_request_surface_batches(cache); return cache->batch.surface; } -int DRW_mesh_material_count_get(const Mesh *me) +int DRW_mesh_material_count_get(const Object *object, const Mesh *me) { - return mesh_render_mat_len_get(me); + return mesh_render_mat_len_get(object, me); } GPUBatch *DRW_mesh_batch_cache_get_sculpt_overlays(Mesh *me) @@ -1375,26 +1394,27 @@ GPUBatch *DRW_mesh_batch_cache_get_verts_with_select_id(Mesh *me) /** \name UV Image editor API * \{ */ -static void edituv_request_active_uv(MeshBatchCache *cache, Mesh *me) +static void edituv_request_active_uv(MeshBatchCache *cache, Object *object, Mesh *me) { DRW_MeshCDMask cd_needed; mesh_cd_layers_type_clear(&cd_needed); - mesh_cd_calc_active_uv_layer(me, &cd_needed); + mesh_cd_calc_active_uv_layer(object, me, &cd_needed); mesh_cd_calc_edit_uv_layer(me, &cd_needed); BLI_assert(cd_needed.edit_uv != 0 && "No uv layer available in edituv, but batches requested anyway!"); - mesh_cd_calc_active_mask_uv_layer(me, &cd_needed); + mesh_cd_calc_active_mask_uv_layer(object, me, &cd_needed); mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); } -GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Mesh *me, +GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Object *object, + Mesh *me, float **tot_area, float **tot_uv_area) { MeshBatchCache *cache = mesh_batch_cache_get(me); - edituv_request_active_uv(cache, me); + edituv_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES_STRETCH_AREA); if (tot_area != NULL) { @@ -1406,58 +1426,58 @@ GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Mesh *me, return DRW_batch_request(&cache->batch.edituv_faces_stretch_area); } -GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - edituv_request_active_uv(cache, me); + edituv_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES_STRETCH_ANGLE); return DRW_batch_request(&cache->batch.edituv_faces_stretch_angle); } -GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - edituv_request_active_uv(cache, me); + edituv_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES); return DRW_batch_request(&cache->batch.edituv_faces); } -GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - edituv_request_active_uv(cache, me); + edituv_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_EDGES); return DRW_batch_request(&cache->batch.edituv_edges); } -GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - edituv_request_active_uv(cache, me); + edituv_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_VERTS); return DRW_batch_request(&cache->batch.edituv_verts); } -GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - edituv_request_active_uv(cache, me); + edituv_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACEDOTS); return DRW_batch_request(&cache->batch.edituv_fdots); } -GPUBatch *DRW_mesh_batch_cache_get_uv_edges(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_uv_edges(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - edituv_request_active_uv(cache, me); + edituv_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_WIRE_LOOPS_UVS); return DRW_batch_request(&cache->batch.wire_loops_uvs); } -GPUBatch *DRW_mesh_batch_cache_get_surface_edges(Mesh *me) +GPUBatch *DRW_mesh_batch_cache_get_surface_edges(Object *object, Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); - texpaint_request_active_uv(cache, me); + texpaint_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_WIRE_LOOPS); return DRW_batch_request(&cache->batch.wire_loops); } @@ -1560,20 +1580,12 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, /* Sanity check. */ if ((me->edit_mesh != NULL) && (ob->mode & OB_MODE_EDIT)) { - BLI_assert(me->edit_mesh->mesh_eval_final != NULL); + BLI_assert(BKE_object_get_editmesh_eval_final(ob) != NULL); } - /* 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 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) && - /* In rare cases we have the edit-mode data but not the generated cache. - * This can happen when switching an objects data to a mesh which - * happens to be in edit-mode in another scene, see: T82952. */ - (me->edit_mesh->mesh_eval_final != - NULL) /* && DRW_object_is_in_edit_mode(ob) */; + (BKE_object_get_editmesh_eval_final(ob) != NULL) && + DRW_object_is_in_edit_mode(ob); /* This could be set for paint mode too, currently it's only used for edit-mode. */ const bool is_mode_active = is_editmode && DRW_object_is_in_edit_mode(ob); @@ -1599,7 +1611,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, /* Modifiers will only generate an orco layer if the mesh is deformed. */ if (cache->cd_needed.orco != 0) { /* Orco is always extracted from final mesh. */ - Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me; + Mesh *me_final = (me->edit_mesh) ? BKE_object_get_editmesh_eval_final(ob) : me; if (CustomData_get_layer(&me_final->vdata, CD_ORCO) == NULL) { /* Skip orco calculation */ cache->cd_needed.orco = 0; @@ -1705,10 +1717,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready |= batch_requested; - const bool do_cage = (is_editmode && - (me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage)); + bool do_cage = false, do_uvcage = false; + if (is_editmode) { + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob); + Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob); - const bool do_uvcage = is_editmode && !me->edit_mesh->mesh_eval_final->runtime.is_original; + do_cage = editmesh_eval_final != editmesh_eval_cage; + do_uvcage = !editmesh_eval_final->runtime.is_original; + } const int required_mode = BKE_subsurf_modifier_eval_required_mode(DRW_state_is_scene_render(), is_editmode); @@ -2029,6 +2045,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, &cache->uv_cage, + ob, me, is_editmode, is_paint_mode, @@ -2046,6 +2063,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, &cache->cage, + ob, me, is_editmode, is_paint_mode, @@ -2071,6 +2089,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, &cache->final, + ob, me, is_editmode, is_paint_mode, diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 37bc9435c76..4a8670a9ee2 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -573,6 +573,9 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache) GPU_VERTBUF_DISCARD_SAFE(cache->subdiv_vertex_face_adjacency); cache->resolution = 0; cache->num_subdiv_loops = 0; + cache->num_subdiv_edges = 0; + cache->num_subdiv_verts = 0; + cache->num_subdiv_triangles = 0; cache->num_coarse_poly = 0; cache->num_subdiv_quads = 0; draw_subdiv_free_edit_mode_cache(cache); @@ -749,9 +752,10 @@ static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_con cache->subdiv_loop_poly_index = static_cast<int *>( MEM_mallocN(cache->num_subdiv_loops * sizeof(int), "subdiv_loop_poly_index")); + const int num_coarse_vertices = ctx->coarse_mesh->totvert; cache->point_indices = static_cast<int *>( - MEM_mallocN(cache->num_subdiv_verts * sizeof(int), "point_indices")); - for (int i = 0; i < num_vertices; i++) { + MEM_mallocN(num_coarse_vertices * sizeof(int), "point_indices")); + for (int i = 0; i < num_coarse_vertices; i++) { cache->point_indices[i] = -1; } @@ -976,6 +980,7 @@ static bool draw_subdiv_build_cache(DRWSubdivCache *cache, } DRWCacheBuildingContext cache_building_context; + memset(&cache_building_context, 0, sizeof(DRWCacheBuildingContext)); cache_building_context.coarse_mesh = mesh_eval; cache_building_context.settings = &to_mesh_settings; cache_building_context.cache = cache; @@ -994,7 +999,7 @@ static bool draw_subdiv_build_cache(DRWSubdivCache *cache, cache->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv); - // Build patch coordinates for all the face dots + /* Build patch coordinates for all the face dots. */ cache->fdots_patch_coords = gpu_vertbuf_create_from_format(get_blender_patch_coords_format(), mesh_eval->totpoly); CompressedPatchCoord *blender_fdots_patch_coords = (CompressedPatchCoord *)GPU_vertbuf_get_data( @@ -1755,7 +1760,7 @@ static void draw_subdiv_cache_ensure_mat_offsets(DRWSubdivCache *cache, int *mat_start = static_cast<int *>(MEM_callocN(sizeof(int) * mat_len, "subdiv mat_start")); int *subdiv_polygon_offset = cache->subdiv_polygon_offset; - // TODO: parallel_reduce? + /* TODO: parallel_reduce? */ for (int i = 0; i < mesh_eval->totpoly; i++) { const MPoly *mpoly = &mesh_eval->mpoly[i]; const int next_offset = (i == mesh_eval->totpoly - 1) ? number_of_quads : @@ -1823,7 +1828,7 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, Mesh *mesh_eval = mesh; BMesh *bm = nullptr; if (mesh->edit_mesh) { - mesh_eval = mesh->edit_mesh->mesh_eval_final; + mesh_eval = BKE_object_get_editmesh_eval_final(ob); bm = mesh->edit_mesh->bm; } diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index 65afc5ed3d8..82b830f6799 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -431,7 +431,7 @@ bool DRW_object_is_flat(Object *ob, int *r_axis) OB_SURF, OB_FONT, OB_MBALL, - OB_HAIR, + OB_CURVES, OB_POINTCLOUD, OB_VOLUME)) { /* Non-meshes object cannot be considered as flat. */ diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index 0abb00a71a9..e9010d7a81a 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -54,7 +54,7 @@ BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void) { #ifdef USE_COMPUTE_SHADERS - if (GPU_compute_shader_support()) { + if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) { return PART_REFINE_SHADER_COMPUTE; } #endif @@ -130,7 +130,7 @@ static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, con GPUShader *shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); drw_hair_particle_cache_shgrp_attach_resources(shgrp, cache, subdiv); - DRW_shgroup_vertex_buffer(shgrp, "hairPointOutputBuffer", cache->final[subdiv].proc_buf); + DRW_shgroup_vertex_buffer(shgrp, "posTime", cache->final[subdiv].proc_buf); const int max_strands_per_call = GPU_max_work_group_count(0); int strands_start = 0; diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 7e1d1208698..ca01a99c0d3 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -80,7 +80,7 @@ typedef struct DRWTempInstancingHandle { GPUBatch *batch; /** Batch containing instancing attributes. */ GPUBatch *instancer; - /** Callbuffer to be used instead of instancer. */ + /** Call-buffer to be used instead of instancer. */ GPUVertBuf *buf; /** Original non-instanced batch pointer. */ GPUBatch *geom; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 276a8cc3a13..039fae43329 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -35,11 +35,11 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_curves.h" #include "BKE_duplilist.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_gpencil.h" -#include "BKE_hair.h" #include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_mball.h" @@ -212,21 +212,7 @@ bool DRW_object_is_in_edit_mode(const Object *ob) if (BKE_object_is_in_editmode(ob)) { if (ob->type == OB_MESH) { if ((ob->mode & OB_MODE_EDIT) == 0) { - Mesh *me = (Mesh *)ob->data; - BMEditMesh *embm = me->edit_mesh; - /* Sanity check when rendering in multiple windows. */ - if (embm && embm->mesh_eval_final == NULL) { - return false; - } - /* Do not draw ob with edit overlay when edit data is present and is modified. */ - if (embm && embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final)) { - return false; - } - /* Check if the object that we are drawing is modified. */ - if (!DEG_is_original_id(&me->id)) { - return false; - } - return true; + return false; } } return true; @@ -1496,7 +1482,7 @@ void DRW_draw_callbacks_post_scene(void) * Don't trust them! */ DRW_state_reset(); - /* needed so gizmo isn't obscured */ + /* Needed so gizmo isn't occluded. */ if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) { GPU_depth_test(GPU_DEPTH_NONE); DRW_draw_gizmo_3d(); @@ -2962,8 +2948,8 @@ void DRW_engines_register(void) BKE_gpencil_batch_cache_dirty_tag_cb = DRW_gpencil_batch_cache_dirty_tag; BKE_gpencil_batch_cache_free_cb = DRW_gpencil_batch_cache_free; - BKE_hair_batch_cache_dirty_tag_cb = DRW_hair_batch_cache_dirty_tag; - BKE_hair_batch_cache_free_cb = DRW_hair_batch_cache_free; + BKE_curves_batch_cache_dirty_tag_cb = DRW_curves_batch_cache_dirty_tag; + BKE_curves_batch_cache_free_cb = DRW_curves_batch_cache_free; BKE_pointcloud_batch_cache_dirty_tag_cb = DRW_pointcloud_batch_cache_dirty_tag; BKE_pointcloud_batch_cache_free_cb = DRW_pointcloud_batch_cache_free; diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index d27eb8be9e0..73fd157426c 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -205,8 +205,10 @@ typedef enum { /* Compute Commands. */ DRW_CMD_COMPUTE = 8, + DRW_CMD_COMPUTE_REF = 9, /* Other Commands */ + DRW_CMD_BARRIER = 11, DRW_CMD_CLEAR = 12, DRW_CMD_DRWSTATE = 13, DRW_CMD_STENCIL = 14, @@ -249,6 +251,14 @@ typedef struct DRWCommandCompute { int groups_z_len; } DRWCommandCompute; +typedef struct DRWCommandComputeRef { + int *groups_ref; +} DRWCommandComputeRef; + +typedef struct DRWCommandBarrier { + eGPUBarrier type; +} DRWCommandBarrier; + typedef struct DRWCommandDrawProcedural { GPUBatch *batch; DRWResourceHandle handle; @@ -286,6 +296,8 @@ typedef union DRWCommand { DRWCommandDrawInstanceRange instance_range; DRWCommandDrawProcedural procedural; DRWCommandCompute compute; + DRWCommandComputeRef compute_ref; + DRWCommandBarrier barrier; DRWCommandSetMutableState state; DRWCommandSetStencil stencil; DRWCommandSetSelectID select_id; @@ -300,7 +312,7 @@ struct DRWCallBuffer { }; /** Used by #DRWUniform.type */ -/* TODO(jbakker): rename to DRW_RESOURCE/DRWResourceType. */ +/* TODO(@jbakker): rename to DRW_RESOURCE/DRWResourceType. */ typedef enum { DRW_UNIFORM_INT = 0, DRW_UNIFORM_INT_COPY, @@ -314,6 +326,7 @@ typedef enum { DRW_UNIFORM_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, + DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, /** Per drawcall uniforms/UBO */ DRW_UNIFORM_BLOCK_OBMATS, DRW_UNIFORM_BLOCK_OBINFOS, @@ -345,6 +358,11 @@ struct DRWUniform { GPUUniformBuf *block; GPUUniformBuf **block_ref; }; + /* DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE */ + union { + GPUVertBuf *vertbuf; + GPUVertBuf **vertbuf_ref; + }; /* DRW_UNIFORM_FLOAT_COPY */ float fvalue[4]; /* DRW_UNIFORM_INT_COPY */ @@ -529,7 +547,7 @@ typedef struct DRWData { struct GHash *obattrs_ubo_pool; uint ubo_len; /** Texture pool to reuse temp texture across engines. */ - /* TODO(fclem) the pool could be shared even between viewports. */ + /* TODO(@fclem): The pool could be shared even between view-ports. */ struct DRWTexturePool *texture_pool; /** Per stereo view data. Contains engine data and default framebuffers. */ struct DRWViewData *view_data[2]; @@ -549,7 +567,7 @@ typedef struct DupliKey { typedef struct DRWManager { /* TODO: clean up this struct a bit. */ /* Cache generation */ - /* TODO(fclem) Rename to data. */ + /* TODO(@fclem): Rename to data. */ DRWData *vmempool; /** Active view data structure for one of the 2 stereo view. Not related to DRWView. */ struct DRWViewData *view_data_active; @@ -572,7 +590,7 @@ typedef struct DRWManager { struct ID *dupli_origin_data; /** Hash-map: #DupliKey -> void pointer for each enabled engine. */ struct GHash *dupli_ghash; - /** TODO(fclem): try to remove usage of this. */ + /** TODO(@fclem): try to remove usage of this. */ DRWInstanceData *object_instance_data[MAX_INSTANCE_DATA_SIZE]; /* Dupli data for the current dupli for each enabled engine. */ void **dupli_datas; @@ -615,7 +633,7 @@ typedef struct DRWManager { DRWView *view_active; DRWView *view_previous; uint primary_view_ct; - /** TODO(fclem): Remove this. Only here to support + /** TODO(@fclem): Remove this. Only here to support * shaders without common_view_lib.glsl */ DRWViewUboStorage view_storage_cpy; @@ -640,7 +658,7 @@ typedef struct DRWManager { GPUDrawList *draw_list; struct { - /* TODO(fclem): optimize: use chunks. */ + /* TODO(@fclem): optimize: use chunks. */ DRWDebugLine *lines; DRWDebugSphere *spheres; } debug; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index f1c13bc039a..3f299e878c4 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -283,19 +283,45 @@ void DRW_shgroup_uniform_image_ref(DRWShadingGroup *shgroup, const char *name, G drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_IMAGE_REF, tex, 0, 0, 1); } -void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, - const char *name, - const GPUUniformBuf *ubo) +void DRW_shgroup_uniform_block_ex(DRWShadingGroup *shgroup, + const char *name, + const GPUUniformBuf *ubo DRW_DEBUG_FILE_LINE_ARGS) { BLI_assert(ubo != NULL); int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name); + if (loc == -1) { +#ifdef DRW_UNUSED_RESOURCE_TRACKING + printf("%s:%d: Unable to locate binding of shader uniform buffer object: %s.\n", + file, + line, + name); +#else + /* TODO(@fclem): Would be good to have, but eevee has too much of this for the moment. */ + // BLI_assert_msg(0, "Unable to locate binding of shader uniform buffer objects."); +#endif + return; + } drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK, ubo, 0, 0, 1); } -void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, GPUUniformBuf **ubo) +void DRW_shgroup_uniform_block_ref_ex(DRWShadingGroup *shgroup, + const char *name, + GPUUniformBuf **ubo DRW_DEBUG_FILE_LINE_ARGS) { BLI_assert(ubo != NULL); int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name); + if (loc == -1) { +#ifdef DRW_UNUSED_RESOURCE_TRACKING + printf("%s:%d: Unable to locate binding of shader uniform buffer object: %s.\n", + file, + line, + name); +#else + /* TODO(@fclem): Would be good to have, but eevee has too much of this for the moment. */ + // BLI_assert_msg(0, "Unable to locate binding of shader uniform buffer objects."); +#endif + return; + } drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK_REF, ubo, 0, 0, 1); } @@ -447,19 +473,46 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, } } -void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, - const char *name, - GPUVertBuf *vertex_buffer) +void DRW_shgroup_vertex_buffer_ex(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf *vertex_buffer DRW_DEBUG_FILE_LINE_ARGS) { int location = GPU_shader_get_ssbo(shgroup->shader, name); if (location == -1) { +#ifdef DRW_UNUSED_RESOURCE_TRACKING + printf("%s:%d: Unable to locate binding of shader storage buffer object: %s.\n", + file, + line, + name); +#else BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects."); +#endif return; } drw_shgroup_uniform_create_ex( shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1); } +void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf **vertex_buffer DRW_DEBUG_FILE_LINE_ARGS) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { +#ifdef DRW_UNUSED_RESOURCE_TRACKING + printf("%s:%d: Unable to locate binding of shader storage buffer object: %s.\n", + file, + line, + name); +#else + BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects."); +#endif + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, vertex_buffer, 0, 0, 1); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -730,6 +783,18 @@ static void drw_command_compute(DRWShadingGroup *shgroup, cmd->groups_z_len = groups_z_len; } +static void drw_command_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]) +{ + DRWCommandComputeRef *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE_REF); + cmd->groups_ref = groups_ref; +} + +static void drw_command_barrier(DRWShadingGroup *shgroup, eGPUBarrier type) +{ + DRWCommandBarrier *cmd = drw_command_create(shgroup, DRW_CMD_BARRIER); + cmd->type = type; +} + static void drw_command_draw_procedural(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, @@ -816,7 +881,7 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, culling->user_data = user_data; } if (bypass_culling) { - /* NOTE this will disable culling for the whole object. */ + /* NOTE: this will disable culling for the whole object. */ culling->bsphere.radius = -1.0f; } } @@ -855,6 +920,20 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len); } +void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]) +{ + BLI_assert(GPU_compute_shader_support()); + + drw_command_compute_ref(shgroup, groups_ref); +} + +void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type) +{ + BLI_assert(GPU_compute_shader_support()); + + drw_command_barrier(shgroup, type); +} + static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup, GPUBatch *geom, Object *ob, @@ -1248,6 +1327,17 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) int chunkid_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_RESOURCE_CHUNK); int resourceid_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_RESOURCE_ID); + /* TODO(@fclem): Will take the place of the above after the GPUShaderCreateInfo port. */ + if (view_ubo_location == -1) { + view_ubo_location = GPU_shader_get_builtin_block(shader, GPU_UNIFORM_BLOCK_DRW_VIEW); + } + if (model_ubo_location == -1) { + model_ubo_location = GPU_shader_get_builtin_block(shader, GPU_UNIFORM_BLOCK_DRW_MODEL); + } + if (info_ubo_location == -1) { + info_ubo_location = GPU_shader_get_builtin_block(shader, GPU_UNIFORM_BLOCK_DRW_INFOS); + } + if (chunkid_location != -1) { drw_shgroup_uniform_create_ex( shgroup, chunkid_location, DRW_UNIFORM_RESOURCE_CHUNK, NULL, 0, 0, 1); @@ -1893,10 +1983,10 @@ void DRW_view_reset(void) DST.view_previous = NULL; } -void DRW_view_default_set(DRWView *view) +void DRW_view_default_set(const DRWView *view) { BLI_assert(DST.view_default == NULL); - DST.view_default = view; + DST.view_default = (DRWView *)view; } void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len) diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 8dd24c01337..a579403975f 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -354,9 +354,9 @@ static bool draw_call_is_culled(const DRWResourceHandle *handle, DRWView *view) return (culling->mask & view->culling_mask) != 0; } -void DRW_view_set_active(DRWView *view) +void DRW_view_set_active(const DRWView *view) { - DST.view_active = (view) ? view : DST.view_default; + DST.view_active = (view != NULL) ? ((DRWView *)view) : DST.view_default; } const DRWView *DRW_view_get_active(void) @@ -662,8 +662,11 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF: + GPU_vertbuf_bind_as_ssbo(*uni->vertbuf_ref, uni->location); + break; case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE: - GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location); + GPU_vertbuf_bind_as_ssbo(uni->vertbuf, uni->location); break; /* Legacy/Fallback support. */ case DRW_UNIFORM_BASE_INSTANCE: @@ -1049,6 +1052,15 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->compute.groups_y_len, cmd->compute.groups_z_len); break; + case DRW_CMD_COMPUTE_REF: + GPU_compute_dispatch(shgroup->shader, + cmd->compute_ref.groups_ref[0], + cmd->compute_ref.groups_ref[1], + cmd->compute_ref.groups_ref[2]); + break; + case DRW_CMD_BARRIER: + GPU_memory_barrier(cmd->barrier.type); + break; } } diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 84440a8effe..3cfbb3fe416 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -46,8 +46,10 @@ #include "draw_manager.h" -extern char datatoc_gpu_shader_2D_vert_glsl[]; -extern char datatoc_gpu_shader_3D_vert_glsl[]; +#include "CLG_log.h" + +static CLG_LogRef LOG = {"draw.manager.shader"}; + extern char datatoc_gpu_shader_depth_only_frag_glsl[]; extern char datatoc_common_fullscreen_vert_glsl[]; @@ -567,7 +569,7 @@ void DRW_shader_free(GPUShader *shader) #define MAX_LIB_DEPS 8 struct DRWShaderLibrary { - char *libs[MAX_LIB]; + const char *libs[MAX_LIB]; char libs_name[MAX_LIB][MAX_LIB_NAME]; uint32_t libs_deps[MAX_LIB]; }; @@ -616,11 +618,11 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c } dbg_name[i + 1] = '\0'; - printf( - "Error: Dependency not found: %s\n" - "This might be due to bad lib ordering.\n", - dbg_name); - BLI_assert(0); + CLOG_INFO(&LOG, + 0, + "Dependency '%s' not found\n" + "This might be due to bad lib ordering or overriding a builtin shader.\n", + dbg_name); } else { deps |= 1u << (uint32_t)dep; @@ -629,7 +631,7 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c return deps; } -void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name) +void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, const char *lib_name) { int index = -1; for (int i = 0; i < MAX_LIB; i++) { diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.c index 121a0acd059..28ac76ccd7f 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.c @@ -47,12 +47,7 @@ static struct { 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; + return GPU_shader_create_from_info_name("draw_hair_refine_compute"); } static GPUShader *hair_refine_shader_transform_feedback_create( diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index aa117f44e84..fba8f91b2f6 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -1,6 +1,6 @@ #ifndef GPU_SHADER -# include "gpu_shader_shared_utils.h" +# include "GPU_shader_shared_utils.h" #endif #define DRW_SHADER_SHARED_H @@ -16,21 +16,21 @@ struct ViewInfos { float4x4 winmat; float4x4 wininv; - float4 clipplanes[6]; + float4 clip_planes[6]; float4 viewvecs[2]; /* Should not be here. Not view dependent (only main view). */ float4 viewcamtexcofac; }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) -/* TODO(fclem) Mass rename. */ +/* TODO(@fclem): Mass rename. */ #define ViewProjectionMatrix drw_view.persmat #define ViewProjectionMatrixInverse drw_view.persinv #define ViewMatrix drw_view.viewmat #define ViewMatrixInverse drw_view.viewinv #define ProjectionMatrix drw_view.winmat #define ProjectionMatrixInverse drw_view.wininv -#define clipPlanes drw_view.clipplanes +#define clipPlanes drw_view.clip_planes #define ViewVecs drw_view.viewvecs #define CameraTexCoFactors drw_view.viewcamtexcofac @@ -39,3 +39,14 @@ struct ObjectMatrices { float4x4 drw_modelMatrixInverse; }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) + +struct ObjectInfos { + float4 drw_OrcoTexCoFactors[2]; + float4 drw_ObjectColor; + float4 drw_Infos; +}; +BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) + +#define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors) +#define ObjectInfo (drw_infos[resource_id].drw_Infos) +#define ObjectColor (drw_infos[resource_id].drw_ObjectColor) diff --git a/source/blender/draw/intern/draw_view_data.h b/source/blender/draw/intern/draw_view_data.h index b37e48c11e8..98ada5a59cb 100644 --- a/source/blender/draw/intern/draw_view_data.h +++ b/source/blender/draw/intern/draw_view_data.h @@ -35,7 +35,7 @@ struct DRWRegisteredDrawEngine; struct DrawEngineType; struct GPUViewport; -/* NOTE these structs are only here for reading the actual lists from the engine. +/* NOTE: these structs are only here for reading the actual lists from the engine. * The actual length of them is stored in a ViewportEngineData_Info. * The length of 1 is just here to avoid compiler warning. */ typedef struct FramebufferList { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index bb55d0c5b2c..37eb4f80442 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -281,7 +281,8 @@ typedef struct MeshExtract { * \param is_mode_active: When true, use the modifiers from the edit-data, * otherwise don't use modifiers as they are not from this object. */ -MeshRenderData *mesh_render_data_create(Mesh *me, +MeshRenderData *mesh_render_data_create(Object *object, + Mesh *me, bool is_editmode, bool is_paint_mode, bool is_mode_active, 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 3ea3e67a8da..c3f89ab96ee 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 @@ -157,7 +157,7 @@ static void extract_points_finish(const MeshRenderData *UNUSED(mr), } static void extract_points_init_subdiv(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *UNUSED(mr), + const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *UNUSED(buffer), void *data) @@ -165,9 +165,9 @@ static void extract_points_init_subdiv(const DRWSubdivCache *subdiv_cache, GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); /* Copy the points as the data upload will free them. */ elb->data = (uint *)MEM_dupallocN(subdiv_cache->point_indices); - elb->index_len = subdiv_cache->num_subdiv_verts; + elb->index_len = mr->vert_len; elb->index_min = 0; - elb->index_max = subdiv_cache->num_subdiv_loops - 1; + elb->index_max = subdiv_cache->num_subdiv_loops + mr->loop_loose_len; elb->prim_type = GPU_PRIM_POINTS; } @@ -184,9 +184,6 @@ static void extract_points_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); - elb->data = static_cast<uint32_t *>( - MEM_reallocN(elb->data, sizeof(uint) * (subdiv_cache->num_subdiv_loops + loop_loose_len))); - const Mesh *coarse_mesh = subdiv_cache->mesh; const MEdge *coarse_edges = coarse_mesh->medge; @@ -195,22 +192,18 @@ static void extract_points_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, for (int i = 0; i < loose_geom->edge_len; i++) { const MEdge *loose_edge = &coarse_edges[loose_geom->edges[i]]; if (elb->data[loose_edge->v1] == -1u) { - elb->data[loose_edge->v1] = offset; + GPU_indexbuf_set_point_vert(elb, loose_edge->v1, offset); } if (elb->data[loose_edge->v2] == -1u) { - elb->data[loose_edge->v2] = offset + 1; + GPU_indexbuf_set_point_vert(elb, loose_edge->v2, offset + 1); } - elb->index_max += 2; - elb->index_len += 2; offset += 2; } for (int i = 0; i < loose_geom->vert_len; i++) { if (elb->data[loose_geom->verts[i]] == -1u) { - elb->data[loose_geom->verts[i]] = offset; + GPU_indexbuf_set_point_vert(elb, loose_geom->verts[i], offset); } - elb->index_max += 1; - elb->index_len += 1; offset += 1; } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index b846da3f016..b27633405b9 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -99,9 +99,10 @@ static uint gpu_component_size_for_attribute_type(CustomDataType type) { switch (type) { case CD_PROP_BOOL: + case CD_PROP_INT8: case CD_PROP_INT32: case CD_PROP_FLOAT: { - /* TODO(kevindietrich) : should be 1 when scalar attributes conversion is handled by us. See + /* TODO(@kevindietrich): should be 1 when scalar attributes conversion is handled by us. See * comment #extract_attr_init. */ return 3; } @@ -317,7 +318,7 @@ static void extract_attr_init(const MeshRenderData *mr, init_vbo_for_attribute(mr, vbo, request, false, static_cast<uint32_t>(mr->loop_len)); - /* TODO(kevindietrich) : float3 is used for scalar attributes as the implicit conversion done by + /* TODO(@kevindietrich): float3 is used for scalar attributes as the implicit conversion done by * OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar * texture as for volume attribute, so we can control the conversion ourselves. */ @@ -326,6 +327,10 @@ static void extract_attr_init(const MeshRenderData *mr, extract_attr_generic<bool, float3>(mr, vbo, request); break; } + case CD_PROP_INT8: { + extract_attr_generic<int8_t, float3>(mr, vbo, request); + break; + } case CD_PROP_INT32: { extract_attr_generic<int32_t, float3>(mr, vbo, request); break; @@ -378,6 +383,10 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, extract_attr_generic<bool, float3>(mr, src_data, request); break; } + case CD_PROP_INT8: { + extract_attr_generic<int8_t, float3>(mr, src_data, request); + break; + } case CD_PROP_INT32: { extract_attr_generic<int32_t, float3>(mr, src_data, request); break; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc index 8470a71059f..4185f2f84a2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc @@ -78,7 +78,7 @@ static void extract_edge_fac_init(const MeshRenderData *mr, data->edge_loop_count = static_cast<uchar *>( MEM_callocN(sizeof(uint32_t) * mr->edge_len, __func__)); - /* HACK(fclem) Detecting the need for edge render. + /* HACK(@fclem): Detecting the need for edge render. * We could have a flag in the mesh instead or check the modifier stack. */ const MEdge *med = mr->medge; for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index 5d2ea923658..11f1515275c 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -270,7 +270,7 @@ static void extract_pos_nor_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache const MVert *coarse_verts = coarse_mesh->mvert; uint offset = subdiv_cache->num_subdiv_loops; - /* TODO(kevindietrich) : replace this when compressed normals are supported. */ + /* TODO(@kevindietrich): replace this when compressed normals are supported. */ struct SubdivPosNorLoop { float pos[3]; float nor[3]; diff --git a/source/blender/draw/intern/shaders/common_fxaa_lib.glsl b/source/blender/draw/intern/shaders/common_fxaa_lib.glsl index bf59972fbaa..599d875c500 100644 --- a/source/blender/draw/intern/shaders/common_fxaa_lib.glsl +++ b/source/blender/draw/intern/shaders/common_fxaa_lib.glsl @@ -51,7 +51,7 @@ /*============================================================================ FXAA QUALITY - TUNING KNOBS ------------------------------------------------------------------------------ -NOTE the other tuning knobs are now in the shader function inputs! +NOTE: the other tuning knobs are now in the shader function inputs! ============================================================================*/ #ifndef FXAA_QUALITY__PRESET /* diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index 6cc7f09a852..ed8b8aeb849 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -5,6 +5,9 @@ * of data the CPU has to precompute and transfer for each update. */ +/* TODO(fclem): Keep documentation but remove the uniform declaration. */ +#ifndef USE_GPU_SHADER_CREATE_INFO + /** * hairStrandsRes: Number of points per hair strand. * 2 - no subdivision @@ -33,8 +36,6 @@ uniform int hairStrandOffset = 0; /* -- Per control points -- */ uniform samplerBuffer hairPointBuffer; /* RGBA32F */ -#define point_position xyz -#define point_time w /* Position along the hair length */ /* -- Per strands data -- */ uniform usamplerBuffer hairStrandBuffer; /* R32UI */ @@ -43,6 +44,10 @@ uniform usamplerBuffer hairStrandSegBuffer; /* R16UI */ /* Not used, use one buffer per uv layer */ // uniform samplerBuffer hairUVBuffer; /* RG32F */ // uniform samplerBuffer hairColBuffer; /* RGBA16 linear color */ +#endif + +#define point_position xyz +#define point_time w /* Position along the hair length */ /* -- Subdivision stage -- */ /** diff --git a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl index 4dcde4b0245..6a3a7815cdc 100644 --- a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl +++ b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#ifndef USE_GPU_SHADER_CREATE_INFO /* * To be compiled with common_hair_lib.glsl. */ @@ -9,6 +11,7 @@ layout(std430, binding = 0) writeonly buffer hairPointOutputBuffer vec4 posTime[]; } out_vertbuf; +#endif void main(void) { @@ -20,5 +23,5 @@ void main(void) vec4 result = hair_interp_data(data0, data1, data2, data3, weights); uint index = uint(hair_get_id() * hairStrandsRes) + gl_GlobalInvocationID.y; - out_vertbuf.posTime[index] = result; + posTime[index] = result; } diff --git a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl index 2da16d3f6a9..dd725ad327f 100644 --- a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl +++ b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl @@ -22,13 +22,22 @@ mat3 pointcloud_get_facing_matrix(vec3 p) return facing_mat; } +/* Returns world center position and radius. */ +void pointcloud_get_pos_and_radius(out vec3 outpos, out float outradius) +{ + outpos = point_object_to_world(pos.xyz); + outradius = dot(abs(mat3(ModelMatrix) * pos.www), vec3(1.0 / 3.0)); +} + /* Return world position and normal. */ void pointcloud_get_pos_and_nor(out vec3 outpos, out vec3 outnor) { - vec3 p = point_object_to_world(pos.xyz); + vec3 p; + float radius; + pointcloud_get_pos_and_radius(p, radius); + mat3 facing_mat = pointcloud_get_facing_matrix(p); - float radius = dot(abs(mat3(ModelMatrix) * pos.www), vec3(1.0 / 3.0)); /* TODO(fclem): remove multiplication here. Here only for keeping the size correct for now. */ radius *= 0.01; outpos = p + (facing_mat * pos_inst) * radius; diff --git a/source/blender/draw/intern/shaders/common_smaa_lib.glsl b/source/blender/draw/intern/shaders/common_smaa_lib.glsl index 36ffb4d8b32..73f65fb0799 100644 --- a/source/blender/draw/intern/shaders/common_smaa_lib.glsl +++ b/source/blender/draw/intern/shaders/common_smaa_lib.glsl @@ -736,9 +736,11 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord, float2 edges = step(threshold, delta.xy); # ifndef SMAA_NO_DISCARD +# ifdef GPU_FRAGMENT_SHADER // Then discard if there is no edge: if (dot(edges, float2(1.0, 1.0)) == 0.0) discard; +# endif # endif // Calculate right and bottom deltas: @@ -804,9 +806,11 @@ float2 SMAAColorEdgeDetectionPS(float2 texcoord, float2 edges = step(threshold, delta.xy); # ifndef SMAA_NO_DISCARD +# ifdef GPU_FRAGMENT_SHADER // Then discard if there is no edge: if (dot(edges, float2(1.0, 1.0)) == 0.0) discard; +# endif # endif // Calculate right and bottom deltas: @@ -851,8 +855,10 @@ float2 SMAADepthEdgeDetectionPS(float2 texcoord, float4 offset[3], SMAATexture2D float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); +# ifdef GPU_FRAGMENT_SHADER if (dot(edges, float2(1.0, 1.0)) == 0.0) discard; +# endif return edges; } diff --git a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl index 9dd86c35ee4..e6538d80111 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl @@ -101,7 +101,7 @@ uint get_index(uint i) * the format. */ struct PosNorLoop { float x, y, z; - /* TODO(kevindietrich) : figure how to compress properly as GLSL does not have char/short types, + /* TODO(@kevindietrich): figure how to compress properly as GLSL does not have char/short types, * bit operations get tricky. */ float nx, ny, nz; float flag; diff --git a/source/blender/draw/intern/shaders/common_view_clipping_lib.glsl b/source/blender/draw/intern/shaders/common_view_clipping_lib.glsl new file mode 100644 index 00000000000..d55808c42d2 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_view_clipping_lib.glsl @@ -0,0 +1,33 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +#if defined(GPU_VERTEX_SHADER) || defined(GPU_GEOMETRY_SHADER) + +void view_clipping_distances(vec3 wpos) +{ +# ifdef USE_WORLD_CLIP_PLANES + vec4 pos = vec4(wpos, 1.0); + gl_ClipDistance[0] = dot(drw_view.clip_planes[0], pos); + gl_ClipDistance[1] = dot(drw_view.clip_planes[1], pos); + gl_ClipDistance[2] = dot(drw_view.clip_planes[2], pos); + gl_ClipDistance[3] = dot(drw_view.clip_planes[3], pos); + gl_ClipDistance[4] = dot(drw_view.clip_planes[4], pos); + gl_ClipDistance[5] = dot(drw_view.clip_planes[5], pos); +# endif +} + +/* Kept as define for compiler compatibility. */ +# ifdef USE_WORLD_CLIP_PLANES +# define view_clipping_distances_set(c) \ + gl_ClipDistance[0] = (c).gl_ClipDistance[0]; \ + gl_ClipDistance[1] = (c).gl_ClipDistance[1]; \ + gl_ClipDistance[2] = (c).gl_ClipDistance[2]; \ + gl_ClipDistance[3] = (c).gl_ClipDistance[3]; \ + gl_ClipDistance[4] = (c).gl_ClipDistance[4]; \ + gl_ClipDistance[5] = (c).gl_ClipDistance[5]; + +# else +# define view_clipping_distances_set(c) +# endif + +#endif diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index b0d405165f2..2ac157ad208 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -1,5 +1,5 @@ /* Temporary until we fully make the switch. */ -#ifndef DRW_SHADER_SHARED_H +#ifndef USE_GPU_SHADER_CREATE_INFO # define DRW_RESOURCE_CHUNK_LEN 512 @@ -24,7 +24,13 @@ layout(std140) uniform viewBlock vec4 CameraTexCoFactors; }; -#endif /* DRW_SHADER_SHARED_H */ +#endif /* USE_GPU_SHADER_CREATE_INFO */ + +#ifdef USE_GPU_SHADER_CREATE_INFO +# ifndef DRW_RESOURCE_CHUNK_LEN +# error "Missing draw_view additional create info on shader create info" +# endif +#endif #define ViewNear (ViewVecs[0].w) #define ViewFar (ViewVecs[1].w) @@ -84,66 +90,91 @@ vec4 pack_line_data(vec2 frag_co, vec2 edge_start, vec2 edge_pos) } } -uniform int resourceChunk; +/* Temporary until we fully make the switch. */ +#ifndef DRW_SHADER_SHARED_H +uniform int drw_resourceChunk; +#endif /* DRW_SHADER_SHARED_H */ #ifdef GPU_VERTEX_SHADER -# ifdef GPU_ARB_shader_draw_parameters -# define baseInstance gl_BaseInstanceARB -# else /* no ARB_shader_draw_parameters */ -uniform int baseInstance; -# endif -# if defined(IN_PLACE_INSTANCES) || defined(INSTANCED_ATTR) -/* When drawing instances of an object at the same position. */ -# define instanceId 0 -# elif defined(GPU_DEPRECATED_AMD_DRIVER) -/* A driver bug make it so that when using an attribute with GL_INT_2_10_10_10_REV as format, - * the gl_InstanceID is incremented by the 2 bit component of the attribute. - * Ignore gl_InstanceID then. */ -# define instanceId 0 -# else -# define instanceId gl_InstanceID -# endif +/* Temporary until we fully make the switch. */ +# ifndef DRW_SHADER_SHARED_H -# ifdef UNIFORM_RESOURCE_ID -/* This is in the case we want to do a special instance drawcall but still want to have the - * right resourceId and all the correct ubo datas. */ -uniform int resourceId; -# define resource_id resourceId -# else -# define resource_id (baseInstance + instanceId) -# endif +/* clang-format off */ +# if defined(IN_PLACE_INSTANCES) || defined(INSTANCED_ATTR) || defined(DRW_LEGACY_MODEL_MATRIX) || defined(GPU_DEPRECATED_AMD_DRIVER) +/* clang-format on */ +/* When drawing instances of an object at the same position. */ +# define instanceId 0 +# else +# define instanceId gl_InstanceID +# endif + +# if defined(UNIFORM_RESOURCE_ID) +/* This is in the case we want to do a special instance drawcall for one object but still want to + * have the right resourceId and all the correct ubo datas. */ +uniform int drw_ResourceID; +# define resource_id drw_ResourceID +# else +# define resource_id (gpu_BaseInstance + instanceId) +# endif /* Use this to declare and pass the value if * the fragment shader uses the resource_id. */ -# ifdef USE_GEOMETRY_SHADER -# define RESOURCE_ID_VARYING flat out int resourceIDGeom; -# define PASS_RESOURCE_ID resourceIDGeom = resource_id; -# else -# define RESOURCE_ID_VARYING flat out int resourceIDFrag; -# define PASS_RESOURCE_ID resourceIDFrag = resource_id; +# ifdef USE_GEOMETRY_SHADER +# define RESOURCE_ID_VARYING flat out int resourceIDGeom; +# define PASS_RESOURCE_ID resourceIDGeom = resource_id; +# else +# define RESOURCE_ID_VARYING flat out int resourceIDFrag; +# define PASS_RESOURCE_ID resourceIDFrag = resource_id; +# endif + +# endif /* DRW_SHADER_SHARED_H */ + +#endif /* GPU_VERTEX_SHADER */ + +/* Temporary until we fully make the switch. */ +#ifdef DRW_SHADER_SHARED_H +/* TODO(fclem): Rename PASS_RESOURCE_ID to DRW_RESOURCE_ID_VARYING_SET */ +# if defined(UNIFORM_RESOURCE_ID) +# define resource_id drw_ResourceID +# define PASS_RESOURCE_ID + +# elif defined(GPU_VERTEX_SHADER) +# define resource_id gpu_InstanceIndex +# define PASS_RESOURCE_ID drw_ResourceID_iface.resource_index = resource_id; + +# elif defined(GPU_GEOMETRY_SHADER) +# define resource_id drw_ResourceID_iface_in[0].index +# define PASS_RESOURCE_ID drw_ResourceID_iface_out.resource_index = resource_id; + +# elif defined(GPU_FRAGMENT_SHADER) +# define resource_id drw_ResourceID_iface.resource_index # endif -#endif +/* TODO(fclem): Remove. */ +# define RESOURCE_ID_VARYING + +#else /* If used in a fragment / geometry shader, we pass * resource_id as varying. */ -#ifdef GPU_GEOMETRY_SHADER -# define RESOURCE_ID_VARYING \ - flat out int resourceIDFrag; \ - flat in int resourceIDGeom[]; +# ifdef GPU_GEOMETRY_SHADER +# define RESOURCE_ID_VARYING \ + flat out int resourceIDFrag; \ + flat in int resourceIDGeom[]; -# define resource_id resourceIDGeom -# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0]; -#endif +# define resource_id resourceIDGeom +# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0]; +# endif -#ifdef GPU_FRAGMENT_SHADER +# ifdef GPU_FRAGMENT_SHADER flat in int resourceIDFrag; -# define resource_id resourceIDFrag +# define resource_id resourceIDFrag +# endif #endif /* Breaking this across multiple lines causes issues for some older GLSL compilers. */ /* clang-format off */ -#if !defined(GPU_INTEL) && !defined(GPU_DEPRECATED_AMD_DRIVER) && !defined(OS_MAC) && !defined(INSTANCED_ATTR) +#if !defined(GPU_INTEL) && !defined(GPU_DEPRECATED_AMD_DRIVER) && !defined(OS_MAC) && !defined(INSTANCED_ATTR) && !defined(DRW_LEGACY_MODEL_MATRIX) /* clang-format on */ /* Temporary until we fully make the switch. */ @@ -158,10 +189,10 @@ layout(std140) uniform modelBlock { ObjectMatrices drw_matrices[DRW_RESOURCE_CHUNK_LEN]; }; -# endif /* DRW_SHADER_SHARED_H */ -# define ModelMatrix (drw_matrices[resource_id].drw_modelMatrix) -# define ModelMatrixInverse (drw_matrices[resource_id].drw_modelMatrixInverse) +# define ModelMatrix (drw_matrices[resource_id].drw_modelMatrix) +# define ModelMatrixInverse (drw_matrices[resource_id].drw_modelMatrixInverse) +# endif /* DRW_SHADER_SHARED_H */ #else /* GPU_INTEL */ @@ -177,7 +208,10 @@ uniform mat4 ModelMatrixInverse; #endif -#define resource_handle (resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id) +/* Temporary until we fully make the switch. */ +#ifndef DRW_SHADER_SHARED_H +# define resource_handle (drw_resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id) +#endif /** Transform shortcuts. */ /* Rule of thumb: Try to reuse world positions and normals because converting through viewspace diff --git a/source/blender/draw/intern/shaders/draw_hair_refine_info.hh b/source/blender/draw/intern/shaders/draw_hair_refine_info.hh new file mode 100644 index 00000000000..bdfc26b7dcd --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_hair_refine_info.hh @@ -0,0 +1,42 @@ +/* + * 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) 2022 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(draw_hair_refine_compute) + .local_group_size(1, 1) + .storage_buf(0, Qualifier::WRITE, "vec4", "posTime[]") + .sampler(0, ImageType::FLOAT_BUFFER, "hairPointBuffer") + .sampler(1, ImageType::UINT_BUFFER, "hairStrandBuffer") + .sampler(2, ImageType::UINT_BUFFER, "hairStrandSegBuffer") + .push_constant(Type::VEC4, "hairDupliMatrix", 4) + .push_constant(Type::BOOL, "hairCloseTip") + .push_constant(Type::FLOAT, "hairRadShape") + .push_constant(Type::FLOAT, "hairRadTip") + .push_constant(Type::FLOAT, "hairRadRoot") + .push_constant(Type::INT, "hairThicknessRes") + .push_constant(Type::INT, "hairStrandsRes") + .push_constant(Type::INT, "hairStrandOffset") + .compute_source("common_hair_refine_comp.glsl") + .define("HAIR_PHASE_SUBDIV") + .do_static_compilation(true); diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh index 10b3754eebb..17a32c7d5ed 100644 --- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh +++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh @@ -2,4 +2,5 @@ #include "gpu_shader_create_info.hh" GPU_SHADER_CREATE_INFO(draw_object_infos) + .typedef_source("draw_shader_shared.h") .uniform_buf(1, "ObjectInfos", "drw_infos[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH); diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh index a92284efa5b..f9dcf291f95 100644 --- a/source/blender/draw/intern/shaders/draw_view_info.hh +++ b/source/blender/draw/intern/shaders/draw_view_info.hh @@ -2,6 +2,43 @@ #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ +/** \name Resource ID + * + * This is used to fetch per object data in drw_matrices and other object indexed + * buffers. There is multiple possibilities depending on how we are drawing the object. + * + * \{ */ + +/* Standard way. Use gpu_InstanceIndex to index the object data. */ +GPU_SHADER_CREATE_INFO(draw_resource_id).define("DYNAMIC_RESOURCE_ID"); + +/** + * Used if the resource index needs to be passed to the fragment shader. + * IMPORTANT: Vertex and Geometry shaders need to use PASS_RESOURCE_ID in main(). + */ +GPU_SHADER_INTERFACE_INFO(draw_resource_id_iface, "drw_ResourceID_iface") + .flat(Type::INT, "resource_index"); + +GPU_SHADER_CREATE_INFO(draw_resource_id_varying) + .vertex_out(draw_resource_id_iface) + .geometry_out(draw_resource_id_iface); /* Used if needed. */ + +/* Variation used when drawing multiple instances for one object. */ +GPU_SHADER_CREATE_INFO(draw_resource_id_uniform) + .define("UNIFORM_RESOURCE_ID") + .push_constant(Type::INT, "drw_ResourceID"); + +/** + * Declare a resource handle that identify a unique object. + * Requires draw_resource_id[_uniform]. + */ +GPU_SHADER_CREATE_INFO(draw_resource_handle) + .define("resource_handle (drw_resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id)") + .push_constant(Type::INT, "drw_resourceChunk"); + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Draw View * \{ */ @@ -9,32 +46,60 @@ GPU_SHADER_CREATE_INFO(draw_view) .uniform_buf(0, "ViewInfos", "drw_view", Frequency::PASS) .typedef_source("draw_shader_shared.h"); -GPU_SHADER_CREATE_INFO(draw_view_instanced_attr) - .push_constant(0, Type::MAT4, "ModelMatrix") - .push_constant(16, Type::MAT4, "ModelMatrixInverse") +GPU_SHADER_CREATE_INFO(draw_modelmat) + .uniform_buf(8, "ObjectMatrices", "drw_matrices[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH) + .define("ModelMatrix", "(drw_matrices[resource_id].drw_modelMatrix)") + .define("ModelMatrixInverse", "(drw_matrices[resource_id].drw_modelMatrixInverse)") .additional_info("draw_view"); +GPU_SHADER_CREATE_INFO(draw_modelmat_legacy) + .define("DRW_LEGACY_MODEL_MATRIX") + .push_constant(Type::MAT4, "ModelMatrix") + .push_constant(Type::MAT4, "ModelMatrixInverse") + .additional_info("draw_view"); + +GPU_SHADER_CREATE_INFO(draw_modelmat_instanced_attr) + .push_constant(Type::MAT4, "ModelMatrix") + .push_constant(Type::MAT4, "ModelMatrixInverse") + .additional_info("draw_view"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw View + * \{ */ + +GPU_SHADER_CREATE_INFO(drw_clipped).define("USE_WORLD_CLIP_PLANES"); + /** \} */ /* -------------------------------------------------------------------- */ /** \name Geometry Type * \{ */ -GPU_SHADER_CREATE_INFO(draw_mesh) - .uniform_buf(8, "ObjectMatrices", "drw_matrices[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH) - .additional_info("draw_view"); +GPU_SHADER_CREATE_INFO(draw_mesh).additional_info("draw_modelmat", "draw_resource_id"); GPU_SHADER_CREATE_INFO(draw_hair) - /* TODO(fclem) Finish */ - .uniform_buf(8, "ObjectMatrices", "drw_matrices[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH) - .additional_info("draw_view"); + .sampler(15, ImageType::FLOAT_BUFFER, "hairPointBuffer") + .sampler(14, ImageType::UINT_BUFFER, "hairStrandBuffer") + .sampler(13, ImageType::UINT_BUFFER, "hairStrandSegBuffer") + /* TODO(@fclem): Pack these into one UBO. */ + .push_constant(Type::INT, "hairStrandsRes") + .push_constant(Type::INT, "hairThicknessRes") + .push_constant(Type::FLOAT, "hairRadRoot") + .push_constant(Type::FLOAT, "hairRadTip") + .push_constant(Type::FLOAT, "hairRadShape") + .push_constant(Type::BOOL, "hairCloseTip") + .push_constant(Type::INT, "hairStrandOffset") + .push_constant(Type::VEC4, "hairDupliMatrix", 4) + .additional_info("draw_modelmat", "draw_resource_id"); GPU_SHADER_CREATE_INFO(draw_pointcloud) .vertex_in(0, Type::VEC4, "pos") .vertex_in(1, Type::VEC3, "pos_inst") .vertex_in(2, Type::VEC3, "nor") - .define("UNIFORM_RESOURCE_ID") - .define("INSTANCED_ATTR") - .additional_info("draw_view_instanced_attr"); + .additional_info("draw_modelmat_instanced_attr", "draw_resource_id_uniform"); + +GPU_SHADER_CREATE_INFO(draw_volume).additional_info("draw_modelmat", "draw_resource_id_uniform"); /** \} */ diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index ef702821a59..80f87eea7ca 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -27,8 +27,6 @@ using namespace blender::draw::image_engine; static void test_workbench_glsl_shaders() { - workbench_shader_library_ensure(); - const int MAX_WPD = 6; WORKBENCH_PrivateData wpds[MAX_WPD]; @@ -185,10 +183,8 @@ DRAW_TEST(gpencil_glsl_shaders) static void test_image_glsl_shaders() { - IMAGE_shader_library_ensure(); - - EXPECT_NE(IMAGE_shader_image_get(false), nullptr); - EXPECT_NE(IMAGE_shader_image_get(true), nullptr); + EXPECT_NE(IMAGE_shader_image_get(), nullptr); + EXPECT_NE(IMAGE_shader_depth_get(), nullptr); IMAGE_shader_free(); } diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt index 6fa4d94df3a..0baac40660d 100644 --- a/source/blender/editors/animation/CMakeLists.txt +++ b/source/blender/editors/animation/CMakeLists.txt @@ -60,10 +60,6 @@ set(LIB bf_blenlib ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 2eaa42ee578..a697fd2fc96 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -35,8 +35,8 @@ #include "DNA_armature_types.h" #include "DNA_cachefile_types.h" #include "DNA_camera_types.h" +#include "DNA_curves_types.h" #include "DNA_gpencil_types.h" -#include "DNA_hair_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" #include "DNA_light_types.h" @@ -700,8 +700,8 @@ static int acf_object_icon(bAnimListElem *ale) return ICON_OUTLINER_OB_FONT; case OB_SURF: return ICON_OUTLINER_OB_SURFACE; - case OB_HAIR: - return ICON_OUTLINER_OB_HAIR; + case OB_CURVES: + return ICON_OUTLINER_OB_CURVES; case OB_POINTCLOUD: return ICON_OUTLINER_OB_POINTCLOUD; case OB_VOLUME: @@ -2813,15 +2813,15 @@ static bAnimChannelType ACF_DSSPK = { /* Hair Expander ------------------------------------------- */ /* TODO: just get this from RNA? */ -static int acf_dshair_icon(bAnimListElem *UNUSED(ale)) +static int acf_dscurves_icon(bAnimListElem *UNUSED(ale)) { - return ICON_HAIR_DATA; + return ICON_CURVES_DATA; } /* Get the appropriate flag(s) for the setting when it is valid. */ -static int acf_dshair_setting_flag(bAnimContext *UNUSED(ac), - eAnimChannel_Settings setting, - bool *neg) +static int acf_dscurves_setting_flag(bAnimContext *UNUSED(ac), + eAnimChannel_Settings setting, + bool *neg) { /* clear extra return data first */ *neg = false; @@ -2846,22 +2846,24 @@ static int acf_dshair_setting_flag(bAnimContext *UNUSED(ac), } /* get pointer to the setting */ -static void *acf_dshair_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings setting, short *type) +static void *acf_dscurves_setting_ptr(bAnimListElem *ale, + eAnimChannel_Settings setting, + short *type) { - Hair *hair = (Hair *)ale->data; + Curves *curves = (Curves *)ale->data; /* clear extra return data first */ *type = 0; switch (setting) { case ACHANNEL_SETTING_EXPAND: /* expanded */ - return GET_ACF_FLAG_PTR(hair->flag, type); + return GET_ACF_FLAG_PTR(curves->flag, type); case ACHANNEL_SETTING_SELECT: /* selected */ case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */ case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */ - if (hair->adt) { - return GET_ACF_FLAG_PTR(hair->adt->flag, type); + if (curves->adt) { + return GET_ACF_FLAG_PTR(curves->adt->flag, type); } return NULL; @@ -2870,9 +2872,9 @@ static void *acf_dshair_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se } } -/* hair expander type define */ +/* Curves expander type define */ static bAnimChannelType ACF_DSHAIR = { - "Hair Expander", /* type name */ + "Curves Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ acf_generic_dataexpand_color, /* backdrop color */ @@ -2882,11 +2884,11 @@ static bAnimChannelType ACF_DSHAIR = { acf_generic_idblock_name, /* name */ acf_generic_idblock_name_prop, /* name prop */ - acf_dshair_icon, /* icon */ + acf_dscurves_icon, /* icon */ acf_generic_dataexpand_setting_valid, /* has setting */ - acf_dshair_setting_flag, /* flag for setting */ - acf_dshair_setting_ptr /* pointer for setting */ + acf_dscurves_setting_flag, /* flag for setting */ + acf_dscurves_setting_ptr /* pointer for setting */ }; /* PointCloud Expander ------------------------------------------- */ @@ -3418,7 +3420,7 @@ static void acf_gpd_color(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale), /* TODO: just get this from RNA? */ static int acf_gpd_icon(bAnimListElem *UNUSED(ale)) { - return ICON_GREASEPENCIL; + return ICON_OUTLINER_OB_GREASEPENCIL; } /* check if some setting exists for this channel */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index c1a09b9d21f..3307385b84a 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -47,8 +47,8 @@ #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" #include "DNA_camera_types.h" +#include "DNA_curves_types.h" #include "DNA_gpencil_types.h" -#include "DNA_hair_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" #include "DNA_layer_types.h" @@ -791,10 +791,10 @@ static bAnimListElem *make_new_animlistelem(void *data, break; } case ANIMTYPE_DSHAIR: { - Hair *hair = (Hair *)data; - AnimData *adt = hair->adt; + Curves *curves = (Curves *)data; + AnimData *adt = curves->adt; - ale->flag = FILTER_HAIR_OBJD(hair); + ale->flag = FILTER_CURVES_OBJD(curves); ale->key_data = (adt) ? adt->action : NULL; ale->datatype = ALE_ACT; @@ -2616,16 +2616,16 @@ static size_t animdata_filter_ds_obdata( expanded = FILTER_SPK_OBJD(spk); break; } - case OB_HAIR: /* ---------- Hair ----------- */ + case OB_CURVES: /* ---------- Curves ----------- */ { - Hair *hair = (Hair *)ob->data; + Curves *curves = (Curves *)ob->data; if (ads->filterflag2 & ADS_FILTER_NOHAIR) { return 0; } type = ANIMTYPE_DSHAIR; - expanded = FILTER_HAIR_OBJD(hair); + expanded = FILTER_CURVES_OBJD(curves); break; } case OB_POINTCLOUD: /* ---------- PointCloud ----------- */ diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 1e60a129535..8c52480b89a 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -169,7 +169,7 @@ static void change_frame_apply(bContext *C, wmOperator *op) FRAMENUMBER_MIN_CLAMP(CFRA); /* do updates */ - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index 145d67b7810..dfe6566df67 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -1283,6 +1283,61 @@ static short set_bezt_sine(KeyframeEditData *UNUSED(ked), BezTriple *bezt) return 0; } +static void handle_flatten(float vec[3][3], const int idx, const float direction[2]) +{ + BLI_assert_msg(idx == 0 || idx == 2, "handle_flatten() expects a handle index"); + + add_v2_v2v2(vec[idx], vec[1], direction); +} + +static void handle_set_length(float vec[3][3], const int idx, const float handle_length) +{ + BLI_assert_msg(idx == 0 || idx == 2, "handle_set_length() expects a handle index"); + + float handle_direction[2]; + sub_v2_v2v2(handle_direction, vec[idx], vec[1]); + normalize_v2_length(handle_direction, handle_length); + add_v2_v2v2(vec[idx], vec[1], handle_direction); +} + +void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu, + const eEditKeyframes_Equalize mode, + const float handle_length, + const bool flatten) +{ + uint i; + BezTriple *bezt; + const float flat_direction_left[2] = {-handle_length, 0.f}; + const float flat_direction_right[2] = {handle_length, 0.f}; + + /* Loop through an F-Curves keyframes. */ + for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) { + if ((bezt->f2 & SELECT) == 0) { + continue; + } + + /* Perform handle equalization if mode is 'Both' or 'Left'. */ + if (mode & EQUALIZE_HANDLES_LEFT) { + if (flatten) { + handle_flatten(bezt->vec, 0, flat_direction_left); + } + else { + handle_set_length(bezt->vec, 0, handle_length); + } + } + + /* Perform handle equalization if mode is 'Both' or 'Right'. */ + if (mode & EQUALIZE_HANDLES_RIGHT) { + if (flatten) { + handle_flatten(bezt->vec, 2, flat_direction_right); + } + else { + handle_set_length(bezt->vec, 2, handle_length); + } + } + } +} + KeyframeEditFunc ANIM_editkeyframes_ipo(short mode) { switch (mode) { diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index aff5803f037..c8ca27c64a3 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -63,9 +63,5 @@ set(LIB bf_blenlib ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_armature "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 02ecfdb4ea6..4a327904ddd 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -581,8 +581,6 @@ static void updateDuplicateLocRotConstraintSettings(Object *ob, { /* This code assumes that bRotLimitConstraint and bLocLimitConstraint have the same fields in * the same memory locations. */ - BLI_assert(sizeof(bLocLimitConstraint) == sizeof(bRotLimitConstraint)); - bRotLimitConstraint *limit = (bRotLimitConstraint *)curcon->data; float local_mat[4][4], imat[4][4]; @@ -798,6 +796,13 @@ static void updateDuplicateTransformConstraintSettings(Object *ob, trans->to_min_rot[i] = temp_vec[i]; } } + + if (trans->from == TRANS_ROTATION && trans->map[1] == Y) { + /* Y Rot to Y Rot: Flip and invert */ + trans->to_max_rot[1] = -trans->to_min_rot[1]; + trans->to_min_rot[1] = -temp_vec[1]; + } + break; } /* convert back to the settings space */ diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 252cf806e34..aaeac29b7d0 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -27,6 +27,7 @@ struct wmOperatorType; struct Base; +struct GPUSelectResult; struct Object; struct Scene; struct bContext; @@ -323,21 +324,21 @@ struct Bone *ED_armature_pick_bone(struct bContext *C, struct EditBone *ED_armature_pick_ebone_from_selectbuffer(struct Base **bases, uint bases_len, - const uint *buffer, + const struct GPUSelectResult *buffer, short hits, bool findunsel, bool do_nearest, struct Base **r_base); struct bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(struct Base **bases, uint bases_len, - const uint *buffer, + const struct GPUSelectResult *buffer, short hits, bool findunsel, bool do_nearest, struct Base **r_base); struct Bone *ED_armature_pick_bone_from_selectbuffer(struct Base **bases, uint bases_len, - const uint *buffer, + const struct GPUSelectResult *buffer, short hits, bool findunsel, bool do_nearest, diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 5e4cb813064..f9b52eb53ed 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -55,6 +55,8 @@ #include "DEG_depsgraph.h" +#include "GPU_select.h" + #include "armature_intern.h" /* utility macros for storing a temp int in the bone (selection flag) */ @@ -67,10 +69,10 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases, uint bases_len, - int hit, + const uint select_id, EditBone **r_ebone) { - const uint hit_object = hit & 0xFFFF; + const uint hit_object = select_id & 0xFFFF; Base *base = NULL; EditBone *ebone = NULL; /* TODO(campbell): optimize, eg: sort & binary search. */ @@ -81,7 +83,7 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases, } } if (base != NULL) { - const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16; bArmature *arm = base->object->data; ebone = BLI_findlink(arm->edbo, hit_bone); } @@ -91,10 +93,10 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases, Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects, uint objects_len, - int hit, + const uint select_id, EditBone **r_ebone) { - const uint hit_object = hit & 0xFFFF; + const uint hit_object = select_id & 0xFFFF; Object *ob = NULL; EditBone *ebone = NULL; /* TODO(campbell): optimize, eg: sort & binary search. */ @@ -105,7 +107,7 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects, } } if (ob != NULL) { - const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16; bArmature *arm = ob->data; ebone = BLI_findlink(arm->edbo, hit_bone); } @@ -115,10 +117,10 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects, Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases, uint bases_len, - int hit, + const uint select_id, bPoseChannel **r_pchan) { - const uint hit_object = hit & 0xFFFF; + const uint hit_object = select_id & 0xFFFF; Base *base = NULL; bPoseChannel *pchan = NULL; /* TODO(campbell): optimize, eg: sort & binary search. */ @@ -130,7 +132,7 @@ Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases, } if (base != NULL) { if (base->object->pose != NULL) { - const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16; /* pchan may be NULL. */ pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone); } @@ -141,11 +143,11 @@ Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases, Base *ED_armature_base_and_bone_from_select_buffer(Base **bases, uint bases_len, - int hit, + const uint select_id, Bone **r_bone) { bPoseChannel *pchan = NULL; - Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, bases_len, hit, &pchan); + Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, bases_len, select_id, &pchan); *r_bone = pchan ? pchan->bone : NULL; return base; } @@ -166,8 +168,8 @@ Base *ED_armature_base_and_bone_from_select_buffer(Base **bases, static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode, Base **bases, uint bases_len, - const uint *buffer, - short hits, + const GPUSelectResult *buffer, + const short hits, bool findunsel, bool do_nearest, Base **r_base) @@ -181,7 +183,7 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode int minsel = 0xffffffff, minunsel = 0xffffffff; for (short i = 0; i < hits; i++) { - hitresult = buffer[3 + (i * 4)]; + hitresult = buffer[i].id; if (hitresult & BONESEL_ANY) { /* to avoid including objects in selection */ Base *base = NULL; @@ -221,10 +223,10 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode if (data) { if (sel) { if (do_nearest) { - if (minsel > buffer[4 * i + 1]) { + if (minsel > buffer[i].depth) { firstSel = data; firstSel_base = base; - minsel = buffer[4 * i + 1]; + minsel = buffer[i].depth; } } else { @@ -237,10 +239,10 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode } else { if (do_nearest) { - if (minunsel > buffer[4 * i + 1]) { + if (minunsel > buffer[i].depth) { firstunSel = data; firstunSel_base = base; - minunsel = buffer[4 * i + 1]; + minunsel = buffer[i].depth; } } else { @@ -268,8 +270,8 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode EditBone *ED_armature_pick_ebone_from_selectbuffer(Base **bases, uint bases_len, - const uint *buffer, - short hits, + const GPUSelectResult *buffer, + const short hits, bool findunsel, bool do_nearest, Base **r_base) @@ -281,8 +283,8 @@ EditBone *ED_armature_pick_ebone_from_selectbuffer(Base **bases, bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(Base **bases, uint bases_len, - const uint *buffer, - short hits, + const GPUSelectResult *buffer, + const short hits, bool findunsel, bool do_nearest, Base **r_base) @@ -294,8 +296,8 @@ bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(Base **bases, Bone *ED_armature_pick_bone_from_selectbuffer(Base **bases, uint bases_len, - const uint *buffer, - short hits, + const GPUSelectResult *buffer, + const short hits, bool findunsel, bool do_nearest, Base **r_base) @@ -327,7 +329,7 @@ static void *ed_armature_pick_bone_impl( Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; rcti rect; - uint buffer[MAXPICKBUF]; + GPUSelectResult buffer[MAXPICKELEMS]; short hits; ED_view3d_viewcontext_init(C, &vc, depsgraph); @@ -340,7 +342,7 @@ static void *ed_armature_pick_bone_impl( hits = view3d_opengl_select_with_id_filter(&vc, buffer, - MAXPICKBUF, + ARRAY_SIZE(buffer), &rect, VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP, @@ -636,15 +638,15 @@ void ARMATURE_OT_select_linked_pick(wmOperatorType *ot) * \{ */ /* utility function for get_nearest_editbonepoint */ -static int selectbuffer_ret_hits_12(uint *UNUSED(buffer), const int hits12) +static int selectbuffer_ret_hits_12(GPUSelectResult *UNUSED(buffer), const int hits12) { return hits12; } -static int selectbuffer_ret_hits_5(uint *buffer, const int hits12, const int hits5) +static int selectbuffer_ret_hits_5(GPUSelectResult *buffer, const int hits12, const int hits5) { - const int ofs = 4 * hits12; - memcpy(buffer, buffer + ofs, 4 * hits5 * sizeof(uint)); + const int ofs = hits12; + memcpy(buffer, buffer + ofs, hits5 * sizeof(*buffer)); return hits5; } @@ -653,7 +655,7 @@ static int selectbuffer_ret_hits_5(uint *buffer, const int hits12, const int hit static EditBone *get_nearest_editbonepoint( ViewContext *vc, bool findunsel, bool use_cycle, Base **r_base, int *r_selmask) { - uint buffer[MAXPICKBUF]; + GPUSelectResult buffer[MAXPICKELEMS]; struct { uint hitresult; Base *base; @@ -692,7 +694,7 @@ static EditBone *get_nearest_editbonepoint( rcti rect; BLI_rcti_init_pt_radius(&rect, vc->mval, 12); const int hits12 = view3d_opengl_select_with_id_filter( - vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter, select_id_ignore); + vc, buffer, ARRAY_SIZE(buffer), &rect, select_mode, select_filter, select_id_ignore); if (hits12 == 1) { hits = selectbuffer_ret_hits_12(buffer, hits12); @@ -701,10 +703,15 @@ static EditBone *get_nearest_editbonepoint( else if (hits12 > 0) { int ofs; - ofs = 4 * hits12; + ofs = hits12; BLI_rcti_init_pt_radius(&rect, vc->mval, 5); - const int hits5 = view3d_opengl_select_with_id_filter( - vc, buffer + ofs, MAXPICKBUF - ofs, &rect, select_mode, select_filter, select_id_ignore); + const int hits5 = view3d_opengl_select_with_id_filter(vc, + buffer + ofs, + ARRAY_SIZE(buffer) - ofs, + &rect, + select_mode, + select_filter, + select_id_ignore); if (hits5 == 1) { hits = selectbuffer_ret_hits_5(buffer, hits12, hits5); @@ -732,7 +739,7 @@ cache_end: /* See if there are any selected bones in this group */ if (hits > 0) { if (hits == 1) { - result_bias.hitresult = buffer[3]; + result_bias.hitresult = buffer->id; result_bias.base = ED_armature_base_and_ebone_from_select_buffer( bases, bases_len, result_bias.hitresult, &result_bias.ebone); } @@ -771,7 +778,7 @@ cache_end: } for (int i = 0; i < hits; i++) { - const uint hitresult = buffer[3 + (i * 4)]; + const uint hitresult = buffer[i].id; Base *base = NULL; EditBone *ebone; diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index 787d7cbaab0..14f47a84286 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -1742,14 +1742,15 @@ static void harmonic_coordinates_bind(MeshDeformModifierData *mmd, MeshDeformBin free_bvhtree_from_mesh(&mdb->bvhdata); } -void ED_mesh_deform_bind_callback(MeshDeformModifierData *mmd, +void ED_mesh_deform_bind_callback(Object *object, + MeshDeformModifierData *mmd, Mesh *cagemesh, float *vertexcos, int totvert, float cagemat[4][4]) { MeshDeformModifierData *mmd_orig = (MeshDeformModifierData *)BKE_modifier_get_original( - &mmd->modifier); + object, &mmd->modifier); MeshDeformBind mdb; MVert *mvert; int a; diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 8bd6c9f54fd..cc99027c470 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -149,36 +149,6 @@ bool ED_object_posemode_exit(bContext *C, Object *ob) return ok; } -/* if a selected or active bone is protected, throw error (only if warn == 1) and return 1 */ -/* only_selected == 1: the active bone is allowed to be protected */ -#if 0 /* UNUSED 2.5 */ -static bool pose_has_protected_selected(Object *ob, short warn) -{ - /* check protection */ - if (ob->proxy) { - bPoseChannel *pchan; - bArmature *arm = ob->data; - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone && BKE_pose_is_layer_visible(arm, pchan)) { - if (pchan->bone->layer & arm->layer_protected) { - if (pchan->bone->flag & BONE_SELECTED) { - break; - } - } - } - } - if (pchan) { - if (warn) { - error("Cannot change Proxy protected bones"); - } - return 1; - } - } - return 0; -} -#endif - /* ********************************************** */ /* Motion Paths */ @@ -1056,10 +1026,6 @@ static int pose_hide_exec(bContext *C, wmOperator *op) Object *ob_iter = objects[ob_index]; bArmature *arm = ob_iter->data; - if (ob_iter->proxy != NULL) { - BKE_report(op->reports, RPT_INFO, "Undo of hiding can only be done with Reveal Selected"); - } - bool changed = bone_looper(ob_iter, arm->bonebase.first, hide_select_p, hide_pose_bone_fn) != 0; if (changed) { diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c index 38d15d8b880..466c423c27c 100644 --- a/source/blender/editors/armature/pose_group.c +++ b/source/blender/editors/armature/pose_group.c @@ -62,8 +62,8 @@ static bool pose_group_poll(bContext *C) } Object *obpose = ED_pose_object_from_context(C); - if ((obpose->proxy != NULL) || (obpose->proxy_group != NULL) || ID_IS_OVERRIDE_LIBRARY(obpose)) { - CTX_wm_operator_poll_msg_set(C, "Cannot edit bone groups for proxies or library overrides"); + if (ID_IS_OVERRIDE_LIBRARY(obpose)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit bone groups for library overrides"); return false; } diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 0b889149f9d..f41c3657431 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -240,8 +240,8 @@ void ED_armature_pose_select_pick_bone(ViewLayer *view_layer, bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer, View3D *v3d, Base *base, - const uint *buffer, - short hits, + const struct GPUSelectResult *buffer, + const short hits, bool extend, bool deselect, bool toggle, diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 086fab4ab47..d91102528e5 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC ../../blenkernel ../../blenlib ../../blenloader + ../../blentranslation ../../makesdna ../../makesrna ../../windowmanager diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h index 36cbb4591e9..e1c327808aa 100644 --- a/source/blender/editors/asset/ED_asset_type.h +++ b/source/blender/editors/asset/ED_asset_type.h @@ -30,7 +30,7 @@ struct ID; bool ED_asset_type_id_is_non_experimental(const struct ID *id); #define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \ - (FILTER_ID_MA | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO) + (FILTER_ID_MA | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO | FILTER_ID_NT) /** * Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset, @@ -52,7 +52,8 @@ int64_t ED_asset_types_supported_as_filter_flags(void); * strings with this (not all UI code supports dynamic strings nicely). * Should start with a consonant, so usages can prefix it with "a" (not "an"). */ -#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Object, Pose Action, or World" +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING \ + "Material, Object, Pose Action, Node Group or World" #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index c075ae390d9..fa0946ffe3d 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -41,6 +41,7 @@ #include "WM_api.h" /* XXX uses private header of file-space. */ +#include "../space_file/file_indexer.h" #include "../space_file/filelist.h" #include "ED_asset_handle.h" @@ -170,7 +171,8 @@ void AssetList::setup() "", ""); - filelist_setindexer(files, &file_indexer_asset); + const bool use_asset_indexer = !USER_EXPERIMENTAL_TEST(&U, no_asset_indexing); + filelist_setindexer(files, use_asset_indexer ? &file_indexer_asset : &file_indexer_noop); char path[FILE_MAXDIR] = ""; if (user_library) { diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index f7755aa9fea..0469f14487d 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -39,6 +39,8 @@ /* XXX needs access to the file list, should all be done via the asset system in future. */ #include "ED_fileselect.h" +#include "BLT_translation.h" + #include "RNA_access.h" #include "RNA_define.h" @@ -342,8 +344,8 @@ static bool asset_clear_poll(bContext *C) IDVecStats ctx_stats = asset_operation_get_id_vec_stats_from_context(C); if (!ctx_stats.has_asset) { - const char *msg_single = "Data-block is not marked as asset"; - const char *msg_multiple = "No data-block selected that is marked as asset"; + const char *msg_single = TIP_("Data-block is not marked as asset"); + const char *msg_multiple = TIP_("No data-block selected that is marked as asset"); CTX_wm_operator_poll_msg_set(C, ctx_stats.is_single ? msg_single : msg_multiple); return false; } @@ -365,8 +367,8 @@ static char *asset_clear_get_description(struct bContext *UNUSED(C), } return BLI_strdup( - "Delete all asset metadata, turning the selected asset data-blocks back into normal " - "data-blocks, and set Fake User to ensure the data-blocks will still be saved"); + TIP_("Delete all asset metadata, turning the selected asset data-blocks back into normal " + "data-blocks, and set Fake User to ensure the data-blocks will still be saved")); } static void ASSET_OT_clear(wmOperatorType *ot) diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc index 028c0cb9ffc..3d6ce3e3409 100644 --- a/source/blender/editors/asset/intern/asset_type.cc +++ b/source/blender/editors/asset/intern/asset_type.cc @@ -30,7 +30,7 @@ bool ED_asset_type_id_is_non_experimental(const ID *id) { /* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and * #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */ - return ELEM(GS(id->name), ID_MA, ID_OB, ID_AC, ID_WO); + return ELEM(GS(id->name), ID_MA, ID_OB, ID_AC, ID_WO, ID_NT); } bool ED_asset_type_is_supported(const ID *id) diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt index 877c2d99102..0ac572c0422 100644 --- a/source/blender/editors/curve/CMakeLists.txt +++ b/source/blender/editors/curve/CMakeLists.txt @@ -51,9 +51,5 @@ set(LIB extern_curve_fit_nd ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 56ecd108bba..17229c4898e 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -325,6 +325,8 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) static void geometry_attribute_convert_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); uiItemR(layout, op->ptr, "mode", 0, nullptr, ICON_NONE); diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c index aed58e31798..3871c1de77a 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -91,7 +91,7 @@ static void gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[3], f static void gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[3], float margin[3]) { - const float handle_size = 0.15f; + const float handle_size = 9.0f; /* XXX, the scale isn't taking offset into account, we need to calculate scale per handle! */ // handle_size *= gz->scale_final; @@ -151,7 +151,9 @@ static void cage3d_draw_box_corners(const float r[3], immUnbindProgram(); } -static void cage3d_draw_box_interaction(const float color[4], +static void cage3d_draw_box_interaction(const RegionView3D *rv3d, + const float matrix_final[4][4], + const float color[4], const int highlighted, const float size[3], const float margin[3]) @@ -173,13 +175,17 @@ static void cage3d_draw_box_interaction(const float color[4], co[i] = size[i] * sign[range[i]]; } const float rad[3] = {margin[0] / 3, margin[1] / 3, margin[2] / 3}; + float co_test[3]; + mul_v3_m4v3(co_test, matrix_final, co); + float rad_scale[3]; + mul_v3_v3fl(rad_scale, rad, ED_view3d_pixel_size(rv3d, co_test)); { uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3fv(color); - imm_draw_cube_fill_3d(pos, co, rad); + imm_draw_cube_fill_3d(pos, co, rad_scale); immUnbindProgram(); } } @@ -249,7 +255,7 @@ static void cage3d_draw_circle_handles(const RegionView3D *rv3d, const float margin[3], const float color[3], bool solid, - float scale) + const float handle_scale) { uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); const float rad[3] = {margin[0] / 3, margin[1] / 3, margin[2] / 3}; @@ -268,7 +274,7 @@ static void cage3d_draw_circle_handles(const RegionView3D *rv3d, float co_test[3]; mul_v3_m4v3(co_test, matrix_final, co); float rad_scale[3]; - mul_v3_v3fl(rad_scale, rad, ED_view3d_pixel_size(rv3d, co_test) * scale); + mul_v3_v3fl(rad_scale, rad, ED_view3d_pixel_size(rv3d, co_test) * handle_scale); imm_draw_point_aspect_3d(pos, co, rad_scale, solid); } } @@ -334,13 +340,13 @@ static void gizmo_cage3d_draw_intern( continue; } GPU_select_load_id(select_id | i); - cage3d_draw_box_interaction(gz->color, i, size, margin); + cage3d_draw_box_interaction(rv3d, matrix_final, gz->color, i, size, margin); } } if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) { const int transform_part = ED_GIZMO_CAGE3D_PART_TRANSLATE; GPU_select_load_id(select_id | transform_part); - cage3d_draw_box_interaction(gz->color, transform_part, size, margin); + cage3d_draw_box_interaction(rv3d, matrix_final, gz->color, transform_part, size, margin); } } else { @@ -375,7 +381,8 @@ static void gizmo_cage3d_draw_intern( } if (show) { - cage3d_draw_box_interaction(gz->color, gz->highlight_part, size_real, margin); + cage3d_draw_box_interaction( + rv3d, matrix_final, gz->color, gz->highlight_part, size_real, margin); } } else if (draw_style == ED_GIZMO_CAGE2D_STYLE_CIRCLE) { @@ -389,10 +396,10 @@ static void gizmo_cage3d_draw_intern( cage3d_draw_circle_wire( size_real, margin, color, transform_flag, draw_options, gz->line_width); - /* corner gizmos */ + /* Corner gizmos (draw the outer & inner so there is a visible outline). */ GPU_polygon_smooth(true); - cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, black, true, 60); - cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, color, true, 40); + cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, black, true, 1.0f); + cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, color, true, 1.0f / 1.5f); GPU_polygon_smooth(false); GPU_blend(GPU_BLEND_NONE); diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index bff7310e9f7..93d17598181 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -86,9 +86,5 @@ if(WITH_POTRACE) add_definitions(-DWITH_POTRACE) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_gpencil "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index e71a56894d0..c910162415d 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -4626,6 +4626,31 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Not implemented!"); } else { + /* Check if all points are selected. */ + bool all_points_selected = true; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + all_points_selected = false; + break; + } + } + + /* Separate the entire stroke. */ + if (all_points_selected) { + /* deselect old stroke */ + gps->flag &= ~GP_STROKE_SELECT; + BKE_gpencil_stroke_select_index_reset(gps); + /* unlink from source frame */ + BLI_remlink(&gpf->strokes, gps); + gps->prev = gps->next = NULL; + /* relink to destination frame */ + BLI_addtail(&gpf_dst->strokes, gps); + /* Reassign material. */ + gps->mat_nr = idx; + + continue; + } + /* make copy of source stroke */ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true); diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 3294316f880..04a892ab411 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -378,7 +378,7 @@ typedef enum eAnimFilter_Flags { #define FILTER_MESH_OBJD(me) (CHECK_TYPE_INLINE(me, Mesh *), ((me->flag & ME_DS_EXPAND))) #define FILTER_LATTICE_OBJD(lt) (CHECK_TYPE_INLINE(lt, Lattice *), ((lt->flag & LT_DS_EXPAND))) #define FILTER_SPK_OBJD(spk) (CHECK_TYPE_INLINE(spk, Speaker *), ((spk->flag & SPK_DS_EXPAND))) -#define FILTER_HAIR_OBJD(ha) (CHECK_TYPE_INLINE(ha, Hair *), ((ha->flag & HA_DS_EXPAND))) +#define FILTER_CURVES_OBJD(ha) (CHECK_TYPE_INLINE(ha, Curves *), ((ha->flag & HA_DS_EXPAND))) #define FILTER_POINTS_OBJD(pt) (CHECK_TYPE_INLINE(pt, PointCloud *), ((pt->flag & PT_DS_EXPAND))) #define FILTER_VOLUME_OBJD(vo) (CHECK_TYPE_INLINE(vo, Volume *), ((vo->flag & VO_DS_EXPAND))) #define FILTER_SIMULATION_OBJD(sim) \ diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 7631bd35e79..0521b39ca1a 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -35,6 +35,7 @@ struct Base; struct Bone; struct Depsgraph; struct EditBone; +struct GPUSelectResult; struct ListBase; struct Main; struct Mesh; @@ -157,22 +158,22 @@ int ED_armature_join_objects_exec(struct bContext *C, struct wmOperator *op); struct Base *ED_armature_base_and_ebone_from_select_buffer(struct Base **bases, uint bases_len, - int hit, + unsigned int select_id, struct EditBone **r_ebone); struct Object *ED_armature_object_and_ebone_from_select_buffer(struct Object **objects, uint objects_len, - int hit, + unsigned int select_id, struct EditBone **r_ebone); struct Base *ED_armature_base_and_pchan_from_select_buffer(struct Base **bases, uint bases_len, - int hit, + unsigned int select_id, struct bPoseChannel **r_pchan); /** * For callers that don't need the pose channel. */ struct Base *ED_armature_base_and_bone_from_select_buffer(struct Base **bases, uint bases_len, - int hit, + unsigned int select_id, struct Bone **r_bone); bool ED_armature_edit_deselect_all(struct Object *obedit); bool ED_armature_edit_deselect_all_visible(struct Object *obedit); @@ -334,7 +335,7 @@ void ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer, bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer, struct View3D *v3d, struct Base *base, - const unsigned int *buffer, + const struct GPUSelectResult *buffer, short hits, bool extend, bool deselect, @@ -368,7 +369,8 @@ void ED_pose_bone_select_tag_update(struct Object *ob); void ED_pose_bone_select(struct Object *ob, struct bPoseChannel *pchan, bool select); /* meshlaplacian.c */ -void ED_mesh_deform_bind_callback(struct MeshDeformModifierData *mmd, +void ED_mesh_deform_bind_callback(struct Object *object, + struct MeshDeformModifierData *mmd, struct Mesh *cagemesh, float *vertexcos, int totvert, diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index c7e89030ee2..f006378658b 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -93,6 +93,13 @@ typedef enum eEditKeyframes_Snap { SNAP_KEYS_TIME, } eEditKeyframes_Snap; +/* equalizing tools */ +typedef enum eEditKeyframes_Equalize { + EQUALIZE_HANDLES_LEFT = (1 << 0), + EQUALIZE_HANDLES_RIGHT = (1 << 1), + EQUALIZE_HANDLES_BOTH = (EQUALIZE_HANDLES_LEFT | EQUALIZE_HANDLES_RIGHT), +} eEditKeyframes_Equalize; + /* mirroring tools */ typedef enum eEditKeyframes_Mirror { MIRROR_KEYS_CURFRAME = 1, @@ -259,6 +266,18 @@ short ANIM_fcurve_keyframes_loop(KeyframeEditData *ked, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb); /** + * Sets selected keyframes' bezier handles to an equal length and optionally makes + * the keyframes' handles horizontal. + * \param handle_length: Desired handle length, must be positive. + * \param flatten: Makes the keyframes' handles the same value as the keyframe, + * flattening the curve at that point. + */ +void ANIM_fcurve_equalize_keyframes_loop(struct FCurve *fcu, + eEditKeyframes_Equalize mode, + float handle_length, + bool flatten); + +/** * Function for working with any type (i.e. one of the known types) of animation channel. */ short ANIM_animchannel_keyframes_loop(KeyframeEditData *ked, diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 181b6848ac7..08f4648d02b 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -57,7 +57,7 @@ typedef enum { #define NODE_EDGE_PAN_DELAY 0.5f #define NODE_EDGE_PAN_ZOOM_INFLUENCE 0.5f -/* space_node.c */ +/* space_node.cc */ void ED_node_cursor_location_get(const struct SpaceNode *snode, float value[2]); void ED_node_cursor_location_set(struct SpaceNode *snode, const float value[2]); @@ -76,7 +76,7 @@ struct bNodeTree *ED_node_tree_get(struct SpaceNode *snode, int level); void ED_node_set_active_viewer_key(struct SpaceNode *snode); -/* drawnode.c */ +/* drawnode.cc */ void ED_node_init_butfuncs(void); void ED_init_custom_node_type(struct bNodeType *ntype); @@ -103,7 +103,7 @@ void ED_node_tag_update_id(struct ID *id); float ED_node_grid_size(void); -/* node_relationships.c */ +/* node_relationships.cc */ /** * Test == 0, clear all intersect flags. @@ -114,7 +114,7 @@ void ED_node_link_intersect_test(struct ScrArea *area, int test); */ void ED_node_link_insert(struct Main *bmain, struct ScrArea *area); -/* node_edit.c */ +/* node_edit.cc */ void ED_node_set_tree_type(struct SpaceNode *snode, struct bNodeTreeType *typeinfo); bool ED_node_is_compositor(struct SpaceNode *snode); @@ -175,11 +175,12 @@ void ED_node_composite_job(const struct bContext *C, struct bNodeTree *nodetree, struct Scene *scene_owner); -/* node_ops.c */ +/* node_ops.cc */ void ED_operatormacros_node(void); -/* node_view.c */ +/* node_view.cc */ + /** * Returns mouse position in image space. */ diff --git a/source/blender/editors/include/ED_transverts.h b/source/blender/editors/include/ED_transverts.h index cbcf28d53d5..dbf156d480f 100644 --- a/source/blender/editors/include/ED_transverts.h +++ b/source/blender/editors/include/ED_transverts.h @@ -28,6 +28,7 @@ extern "C" { #endif struct Object; +struct bContext; typedef struct TransVert { float *loc; @@ -42,10 +43,14 @@ typedef struct TransVertStore { int mode; } TransVertStore; -void ED_transverts_create_from_obedit(TransVertStore *tvs, struct Object *obedit, int mode); +/** + * \param obedit: When `mode` has the #TM_CALC_MAPLOC flag set, `obedit` must be evaluated, + * to access evaluated vertices. + */ +void ED_transverts_create_from_obedit(TransVertStore *tvs, const struct Object *obedit, int mode); void ED_transverts_update_obedit(TransVertStore *tvs, struct Object *obedit); void ED_transverts_free(TransVertStore *tvs); -bool ED_transverts_check_obedit(Object *obedit); +bool ED_transverts_check_obedit(const struct Object *obedit); bool ED_transverts_poll(struct bContext *C); /* currently only used for bmesh index values */ @@ -66,12 +71,16 @@ enum { TM_SKIP_HANDLES = (1 << 1), /** fill in normals when available */ TM_CALC_NORMALS = (1 << 2), + /** Calculates #TransVert.maploc where possible. */ + TM_CALC_MAPLOC = (1 << 2), }; enum { /* SELECT == (1 << 0) */ + /** Calculated when #TM_CALC_MAPLOC is set. */ TX_VERT_USE_MAPLOC = (1 << 1), - TX_VERT_USE_NORMAL = (1 << 2), /* avoid nonzero check */ + /** Calculated when #TM_CALC_NORMALS is set, avoid nonzero check. */ + TX_VERT_USE_NORMAL = (1 << 2), }; #ifdef __cplusplus diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 6bcddfa631a..6d9691d96f4 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -33,6 +33,7 @@ extern "C" { struct GPUBatch; struct Main; struct bContext; +struct IDRemapper; /* ed_util.c */ @@ -60,10 +61,13 @@ bool ED_editors_flush_edits(struct Main *bmain); * * \param new_id: may be NULL to unlink \a old_id. */ +void ED_spacedata_id_remap_single(struct ScrArea *area, + struct SpaceLink *sl, + struct ID *old_id, + struct ID *new_id); void ED_spacedata_id_remap(struct ScrArea *area, struct SpaceLink *sl, - struct ID *old_id, - struct ID *new_id); + const struct IDRemapper *mappings); void ED_operatortypes_edutils(void); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 0398c209c68..3bbffc3b7c9 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -41,6 +41,7 @@ struct Camera; struct CustomData_MeshMasks; struct Depsgraph; struct EditBone; +struct GPUSelectResult; struct ID; struct MVert; struct Main; @@ -871,9 +872,14 @@ bool ED_view3d_autodist_simple(struct ARegion *region, bool ED_view3d_depth_read_cached_seg( const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth); -/* select */ +/** + * The default value for the maximum number of elements that can be selected at once + * using view-port selection. + * + * \note in many cases this defines the size of fixed-size stack buffers, + * so take care increasing this value. + */ #define MAXPICKELEMS 2500 -#define MAXPICKBUF (4 * MAXPICKELEMS) typedef enum { /* all elements in the region, ignore depth */ @@ -912,21 +918,21 @@ void view3d_opengl_select_cache_end(void); * \note (vc->obedit == NULL) can be set to explicitly skip edit-object selection. */ int view3d_opengl_select_ex(struct ViewContext *vc, - unsigned int *buffer, - unsigned int bufsize, + struct GPUSelectResult *buffer, + unsigned int buffer_len, const struct rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter, bool do_material_slot_selection); int view3d_opengl_select(struct ViewContext *vc, - unsigned int *buffer, - unsigned int bufsize, + struct GPUSelectResult *buffer, + unsigned int buffer_len, const struct rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter); int view3d_opengl_select_with_id_filter(struct ViewContext *vc, - unsigned int *buffer, - unsigned int bufsize, + struct GPUSelectResult *buffer, + unsigned int buffer_len, const struct rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 4cf606bf98d..05353de2f92 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -752,9 +752,9 @@ DEF_ICON_BLANK(257) DEF_ICON_BLANK(257b) /* ADDITIONAL OBJECT TYPES */ -DEF_ICON_OBJECT(OUTLINER_OB_HAIR) -DEF_ICON_OBJECT_DATA(OUTLINER_DATA_HAIR) -DEF_ICON_OBJECT_DATA(HAIR_DATA) +DEF_ICON_OBJECT(OUTLINER_OB_CURVES) +DEF_ICON_OBJECT_DATA(OUTLINER_DATA_CURVES) +DEF_ICON_OBJECT_DATA(CURVES_DATA) DEF_ICON_OBJECT(OUTLINER_OB_POINTCLOUD) DEF_ICON_OBJECT_DATA(OUTLINER_DATA_POINTCLOUD) DEF_ICON_OBJECT_DATA(POINTCLOUD_DATA) @@ -840,7 +840,7 @@ DEF_ICON(MATPLANE) DEF_ICON(MATSPHERE) DEF_ICON(MATCUBE) DEF_ICON(MONKEY) -DEF_ICON(HAIR) +DEF_ICON(CURVES) DEF_ICON(ALIASED) DEF_ICON(ANTIALIASED) DEF_ICON(MAT_SPHERE_SKY) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 3796fa51499..ae4c2ff16fd 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1370,6 +1370,8 @@ uiBut *uiDefIconTextButO_ptr(uiBlock *block, /* for passing inputs to ButO buttons */ struct PointerRNA *UI_but_operator_ptr_get(uiBut *but); +struct bContextStore *UI_but_context_get(const uiBut *but); + void UI_but_unit_type_set(uiBut *but, int unit_type); int UI_but_unit_type_get(const uiBut *but); diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 6ffeb4134ae..ec80338b4c0 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -82,6 +82,8 @@ int UI_icon_get_height(int icon_id); bool UI_icon_get_theme_color(int icon_id, unsigned char color[4]); /** + * Render a #PreviewImage for the data block. + * * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored. */ void UI_icon_render_id(const struct bContext *C, @@ -89,6 +91,17 @@ void UI_icon_render_id(const struct bContext *C, struct ID *id, enum eIconSizes size, bool use_job); + +/** + * Render the data block into the provided #PreviewImage. + */ +void UI_icon_render_id_ex(const struct bContext *C, + struct Scene *scene, + struct ID *id_to_render, + const enum eIconSizes size, + const bool use_job, + struct PreviewImage *r_preview_image); + /** * Render size for preview images and icons */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 636281ba373..2292bf759b7 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -5928,6 +5928,11 @@ PointerRNA *UI_but_operator_ptr_get(uiBut *but) return but->opptr; } +bContextStore *UI_but_context_get(const uiBut *but) +{ + return but->context; +} + void UI_but_unit_type_set(uiBut *but, const int unit_type) { but->unit_type = (uchar)(RNA_SUBTYPE_UNIT_VALUE(unit_type)); diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 190b2d12ed9..dd5ce118d5f 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -809,12 +809,18 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev else { if (is_array_component) { ot = WM_operatortype_find("UI_OT_override_type_set_button", false); - uiItemFullO_ptr( - layout, ot, "Define Overrides", ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + uiItemFullO_ptr(layout, + ot, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Overrides"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); RNA_boolean_set(&op_ptr, "all", true); uiItemFullO_ptr(layout, ot, - "Define Single Override", + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Single Override"), ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, @@ -825,7 +831,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev else { uiItemFullO(layout, "UI_OT_override_type_set_button", - "Define Override", + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Override"), ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index fd03cc5e12c..6fa94730365 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -25,6 +25,7 @@ #include "DNA_space_types.h" #include "BLI_math_color.h" +#include "BLI_math_vector.h" #include "BKE_context.h" #include "BKE_screen.h" @@ -107,7 +108,7 @@ wmKeyMap *eyedropper_colorband_modal_keymap(wmKeyConfig *keyconf) /** \name Generic Shared Functions * \{ */ -static void eyedropper_draw_cursor_text_ex(const int x, const int y, const char *name) +static void eyedropper_draw_cursor_text_ex(const int xy[2], const char *name) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; @@ -119,7 +120,7 @@ static void eyedropper_draw_cursor_text_ex(const int x, const int y, const char rgba_uchar_to_float(col_fg, wcol->text); rgba_uchar_to_float(col_bg, wcol->inner); - UI_fontstyle_draw_simple_backdrop(fstyle, x, y + U.widget_unit, name, col_fg, col_bg); + UI_fontstyle_draw_simple_backdrop(fstyle, xy[0], xy[1] + U.widget_unit, name, col_fg, col_bg); } void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name) @@ -128,19 +129,16 @@ void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const cha return; } - const int x = window->eventstate->xy[0]; - const int y = window->eventstate->xy[1]; - - eyedropper_draw_cursor_text_ex(x, y, name); + eyedropper_draw_cursor_text_ex(window->eventstate->xy, name); } -void eyedropper_draw_cursor_text_region(const int x, const int y, const char *name) +void eyedropper_draw_cursor_text_region(const int xy[2], const char *name) { if (name[0] == '\0') { return; } - eyedropper_draw_cursor_text_ex(x, y, name); + eyedropper_draw_cursor_text_ex(xy, name); } uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event) @@ -173,8 +171,7 @@ void datadropper_win_area_find( } } else if (mval != r_mval) { - r_mval[0] = mval[0]; - r_mval[1] = mval[1]; + copy_v2_v2_int(r_mval, mval); } } diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 52730096b2f..b5eed2534a3 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -42,6 +42,8 @@ #include "BKE_node.h" #include "BKE_screen.h" +#include "NOD_composite.h" + #include "RNA_access.h" #include "UI_interface.h" @@ -252,8 +254,10 @@ static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node, return success; } -static bool eyedropper_cryptomatte_sample_fl( - bContext *C, Eyedropper *eye, int mx, int my, float r_col[3]) +static bool eyedropper_cryptomatte_sample_fl(bContext *C, + Eyedropper *eye, + const int m_xy[2], + float r_col[3]) { bNode *node = eye->crypto_node; NodeCryptomatte *crypto = node ? ((NodeCryptomatte *)node->storage) : NULL; @@ -263,17 +267,17 @@ static bool eyedropper_cryptomatte_sample_fl( } bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, (const int[2]){mx, my}); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy); if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP)) { return false; } - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my}); + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); if (!region) { return false; } - int mval[2] = {mx - region->winrct.xmin, my - region->winrct.ymin}; + int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; float fpos[2] = {-1.0f, -1.0}; switch (area->spacetype) { case SPACE_IMAGE: { @@ -322,7 +326,7 @@ static bool eyedropper_cryptomatte_sample_fl( return false; } -void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3]) +void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]) { /* we could use some clever */ Main *bmain = CTX_data_main(C); @@ -330,10 +334,10 @@ void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3]) const char *display_device = CTX_data_scene(C)->display_settings.display_device; struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + int mval[2]; wmWindow *win; ScrArea *area; - int mval[2] = {mx, my}; - datadropper_win_area_find(C, mval, mval, &win, &area); + datadropper_win_area_find(C, m_xy, mval, &win, &area); if (area) { if (area->spacetype == SPACE_IMAGE) { @@ -404,17 +408,17 @@ static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3 RNA_property_update(C, &eye->ptr, eye->prop); } -static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my) +static void eyedropper_color_sample(bContext *C, Eyedropper *eye, const int m_xy[2]) { /* Accumulate color. */ float col[3]; if (eye->crypto_node) { - if (!eyedropper_cryptomatte_sample_fl(C, eye, mx, my, col)) { + if (!eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) { return; } } else { - eyedropper_color_sample_fl(C, mx, my, col); + eyedropper_color_sample_fl(C, m_xy, col); } if (!eye->crypto_node) { @@ -437,13 +441,13 @@ static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my eyedropper_color_set(C, eye, accum_col); } -static void eyedropper_color_sample_text_update(bContext *C, Eyedropper *eye, int mx, int my) +static void eyedropper_color_sample_text_update(bContext *C, Eyedropper *eye, const int m_xy[2]) { float col[3]; eye->sample_text[0] = '\0'; if (eye->cryptomatte_session) { - if (eyedropper_cryptomatte_sample_fl(C, eye, mx, my, col)) { + if (eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) { BKE_cryptomatte_find_name( eye->cryptomatte_session, col[0], eye->sample_text, sizeof(eye->sample_text)); eye->sample_text[sizeof(eye->sample_text) - 1] = '\0'; @@ -474,7 +478,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = eye->is_undo; if (eye->accum_tot == 0) { - eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); + eyedropper_color_sample(C, eye, event->xy); } eyedropper_exit(C, op); /* Could support finished & undo-skip. */ @@ -483,23 +487,23 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ eye->accum_start = true; - eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); + eyedropper_color_sample(C, eye, event->xy); break; case EYE_MODAL_SAMPLE_RESET: eye->accum_tot = 0; zero_v3(eye->accum_col); - eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); + eyedropper_color_sample(C, eye, event->xy); break; } } else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { if (eye->accum_start) { /* button is pressed so keep sampling */ - eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); + eyedropper_color_sample(C, eye, event->xy); } if (eye->draw_handle_sample_text) { - eyedropper_color_sample_text_update(C, eye, event->xy[0], event->xy[1]); + eyedropper_color_sample_text_update(C, eye, event->xy); ED_region_tag_redraw(CTX_wm_region(C)); } } diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c index 22320282766..05ed4ecf601 100644 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ b/source/blender/editors/interface/interface_eyedropper_colorband.c @@ -58,7 +58,7 @@ typedef struct Colorband_RNAUpdateCb { } Colorband_RNAUpdateCb; typedef struct EyedropperColorband { - int last_x, last_y; + int event_xy_last[2]; /* Alpha is currently fixed at 1.0, may support in future. */ float (*color_buffer)[4]; int color_buffer_alloc; @@ -142,13 +142,12 @@ static bool eyedropper_colorband_init(bContext *C, wmOperator *op) static void eyedropper_colorband_sample_point(bContext *C, EyedropperColorband *eye, - int mx, - int my) + const int m_xy[2]) { - if (eye->last_x != mx || eye->last_y != my) { + if (eye->event_xy_last[0] != m_xy[0] || eye->event_xy_last[1] != m_xy[1]) { float col[4]; col[3] = 1.0f; /* TODO: sample alpha */ - eyedropper_color_sample_fl(C, mx, my, col); + eyedropper_color_sample_fl(C, m_xy, col); if (eye->color_buffer_len + 1 == eye->color_buffer_alloc) { eye->color_buffer_alloc *= 2; eye->color_buffer = MEM_reallocN(eye->color_buffer, @@ -156,8 +155,7 @@ static void eyedropper_colorband_sample_point(bContext *C, } copy_v4_v4(eye->color_buffer[eye->color_buffer_len], col); eye->color_buffer_len += 1; - eye->last_x = mx; - eye->last_y = my; + copy_v2_v2_int(eye->event_xy_last, m_xy); eye->is_set = true; } } @@ -167,21 +165,20 @@ static bool eyedropper_colorband_sample_callback(int mx, int my, void *userdata) struct EyedropperColorband_Context *data = userdata; bContext *C = data->context; EyedropperColorband *eye = data->eye; - eyedropper_colorband_sample_point(C, eye, mx, my); + const int cursor[2] = {mx, my}; + eyedropper_colorband_sample_point(C, eye, cursor); return true; } static void eyedropper_colorband_sample_segment(bContext *C, EyedropperColorband *eye, - int mx, - int my) + const int m_xy[2]) { /* Since the mouse tends to move rather rapidly we use #BLI_bitmap_draw_2d_line_v2v2i * to interpolate between the reported coordinates */ struct EyedropperColorband_Context userdata = {C, eye}; - const int p1[2] = {eye->last_x, eye->last_y}; - const int p2[2] = {mx, my}; - BLI_bitmap_draw_2d_line_v2v2i(p1, p2, eyedropper_colorband_sample_callback, &userdata); + BLI_bitmap_draw_2d_line_v2v2i( + eye->event_xy_last, m_xy, eyedropper_colorband_sample_callback, &userdata); } static void eyedropper_colorband_exit(bContext *C, wmOperator *op) @@ -233,7 +230,7 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = eye->is_undo; - eyedropper_colorband_sample_segment(C, eye, event->xy[0], event->xy[1]); + eyedropper_colorband_sample_segment(C, eye, event->xy); eyedropper_colorband_apply(C, op); eyedropper_colorband_exit(C, op); /* Could support finished & undo-skip. */ @@ -242,10 +239,9 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ eye->sample_start = true; - eyedropper_colorband_sample_point(C, eye, event->xy[0], event->xy[1]); + eyedropper_colorband_sample_point(C, eye, event->xy); eyedropper_colorband_apply(C, op); - eye->last_x = event->xy[0]; - eye->last_y = event->xy[1]; + copy_v2_v2_int(eye->event_xy_last, event->xy); break; case EYE_MODAL_SAMPLE_RESET: break; @@ -253,7 +249,7 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent } else if (event->type == MOUSEMOVE) { if (eye->sample_start) { - eyedropper_colorband_sample_segment(C, eye, event->xy[0], event->xy[1]); + eyedropper_colorband_sample_segment(C, eye, event->xy); eyedropper_colorband_apply(C, op); } } @@ -280,7 +276,7 @@ static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const w } break; case EYE_MODAL_POINT_SAMPLE: - eyedropper_colorband_sample_point(C, eye, event->xy[0], event->xy[1]); + eyedropper_colorband_sample_point(C, eye, event->xy); eyedropper_colorband_apply(C, op); if (eye->color_buffer_len == MAXCOLORBAND) { eyedropper_colorband_exit(C, op); diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index cf53ef0ec75..812011101e8 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -32,6 +32,7 @@ #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "BLI_math_vector.h" #include "BLI_string.h" #include "BLT_translation.h" @@ -80,7 +81,7 @@ static void datadropper_draw_cb(const struct bContext *UNUSED(C), void *arg) { DataDropper *ddr = arg; - eyedropper_draw_cursor_text_region(UNPACK2(ddr->name_pos), ddr->name); + eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name); } static int datadropper_init(bContext *C, wmOperator *op) @@ -152,7 +153,7 @@ static void datadropper_exit(bContext *C, wmOperator *op) * \brief get the ID from the 3D view or outliner. */ static void datadropper_id_sample_pt( - bContext *C, wmWindow *win, ScrArea *area, DataDropper *ddr, int mx, int my, ID **r_id) + bContext *C, wmWindow *win, ScrArea *area, DataDropper *ddr, const int m_xy[2], ID **r_id) { wmWindow *win_prev = CTX_wm_window(C); ScrArea *area_prev = CTX_wm_area(C); @@ -162,9 +163,9 @@ static void datadropper_id_sample_pt( if (area) { if (ELEM(area->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) { - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my}); + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); if (region) { - const int mval[2] = {mx - region->winrct.xmin, my - region->winrct.ymin}; + const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; Base *base; CTX_wm_window_set(C, win); @@ -205,8 +206,7 @@ static void datadropper_id_sample_pt( *r_id = id; } - ddr->name_pos[0] = mval[0]; - ddr->name_pos[1] = mval[1]; + copy_v2_v2_int(ddr->name_pos, mval); } } } @@ -234,17 +234,16 @@ static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id) } /* single point sample & set */ -static bool datadropper_id_sample(bContext *C, DataDropper *ddr, int mx, int my) +static bool datadropper_id_sample(bContext *C, DataDropper *ddr, const int m_xy[2]) { ID *id = NULL; + int mval[2]; wmWindow *win; ScrArea *area; + datadropper_win_area_find(C, m_xy, mval, &win, &area); - int mval[] = {mx, my}; - datadropper_win_area_find(C, mval, mval, &win, &area); - - datadropper_id_sample_pt(C, win, area, ddr, mval[0], mval[1], &id); + datadropper_id_sample_pt(C, win, area, ddr, mval, &id); return datadropper_id_set(C, ddr, id); } @@ -292,7 +291,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = ddr->is_undo; - const bool success = datadropper_id_sample(C, ddr, event->xy[0], event->xy[1]); + const bool success = datadropper_id_sample(C, ddr, event->xy); datadropper_exit(C, op); if (success) { /* Could support finished & undo-skip. */ @@ -306,16 +305,15 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) else if (event->type == MOUSEMOVE) { ID *id = NULL; + int mval[2]; wmWindow *win; ScrArea *area; - - int mval[] = {event->xy[0], event->xy[1]}; - datadropper_win_area_find(C, mval, mval, &win, &area); + datadropper_win_area_find(C, event->xy, mval, &win, &area); /* Set the region for eyedropper cursor text drawing */ datadropper_set_draw_callback_region(area, ddr); - datadropper_id_sample_pt(C, win, area, ddr, mval[0], mval[1], &id); + datadropper_id_sample_pt(C, win, area, ddr, mval, &id); } return OPERATOR_RUNNING_MODAL; diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c index 8c6b0ac9cfe..b11001c4bf2 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/interface_eyedropper_depth.c @@ -81,7 +81,7 @@ static void depthdropper_draw_cb(const struct bContext *UNUSED(C), void *arg) { DepthDropper *ddr = arg; - eyedropper_draw_cursor_text_region(UNPACK2(ddr->name_pos), ddr->name); + eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name); } static int depthdropper_init(bContext *C, wmOperator *op) @@ -152,12 +152,14 @@ static void depthdropper_exit(bContext *C, wmOperator *op) /** * \brief get the ID from the screen. */ -static void depthdropper_depth_sample_pt( - bContext *C, DepthDropper *ddr, int mx, int my, float *r_depth) +static void depthdropper_depth_sample_pt(bContext *C, + DepthDropper *ddr, + const int m_xy[2], + float *r_depth) { /* we could use some clever */ bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, (const int[2]){mx, my}); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy); Scene *scene = CTX_data_scene(C); ScrArea *area_prev = CTX_wm_area(C); @@ -167,14 +169,14 @@ static void depthdropper_depth_sample_pt( if (area) { if (area->spacetype == SPACE_VIEW3D) { - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my}); + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); if (region) { struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); View3D *v3d = area->spacedata.first; RegionView3D *rv3d = region->regiondata; /* weak, we could pass in some reference point */ const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3]; - const int mval[2] = {mx - region->winrct.xmin, my - region->winrct.ymin}; + const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; copy_v2_v2_int(ddr->name_pos, mval); float co[3]; @@ -234,19 +236,19 @@ static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr) } /* single point sample & set */ -static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, int mx, int my) +static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, const int m_xy[2]) { float depth = -1.0f; if (depth != -1.0f) { - depthdropper_depth_sample_pt(C, ddr, mx, my, &depth); + depthdropper_depth_sample_pt(C, ddr, m_xy, &depth); depthdropper_depth_set(C, ddr, depth); } } -static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, int mx, int my) +static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, const int m_xy[2]) { float depth = -1.0f; - depthdropper_depth_sample_pt(C, ddr, mx, my, &depth); + depthdropper_depth_sample_pt(C, ddr, m_xy, &depth); if (depth != -1.0f) { ddr->accum_depth += depth; ddr->accum_tot++; @@ -276,7 +278,7 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = ddr->is_undo; if (ddr->accum_tot == 0) { - depthdropper_depth_sample(C, ddr, event->xy[0], event->xy[1]); + depthdropper_depth_sample(C, ddr, event->xy); } else { depthdropper_depth_set_accum(C, ddr); @@ -288,12 +290,12 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ ddr->accum_start = true; - depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); + depthdropper_depth_sample_accum(C, ddr, event->xy); break; case EYE_MODAL_SAMPLE_RESET: ddr->accum_tot = 0; ddr->accum_depth = 0.0f; - depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); + depthdropper_depth_sample_accum(C, ddr, event->xy); depthdropper_depth_set_accum(C, ddr); break; } @@ -301,7 +303,7 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) else if (event->type == MOUSEMOVE) { if (ddr->accum_start) { /* button is pressed so keep sampling */ - depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); + depthdropper_depth_sample_accum(C, ddr, event->xy); depthdropper_depth_set_accum(C, ddr); } } diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c index d76ff84bcad..c1d49406818 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -265,9 +265,9 @@ static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, Eyed } /* Sample the color below cursor. */ -static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, int mx, int my) +static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, const int m_xy[2]) { - eyedropper_color_sample_fl(C, mx, my, eye->color); + eyedropper_color_sample_fl(C, m_xy, eye->color); } /* Cancel operator. */ @@ -292,7 +292,7 @@ static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_CANCELLED; } case EYE_MODAL_SAMPLE_CONFIRM: { - eyedropper_gpencil_color_sample(C, eye, event->xy[0], event->xy[1]); + eyedropper_gpencil_color_sample(C, eye, event->xy); /* Create material. */ eyedropper_gpencil_color_set(C, event, eye); @@ -309,7 +309,7 @@ static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent * } case MOUSEMOVE: case INBETWEEN_MOUSEMOVE: { - eyedropper_gpencil_color_sample(C, eye, event->xy[0], event->xy[1]); + eyedropper_gpencil_color_sample(C, eye, event->xy); break; } default: { diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/interface_eyedropper_intern.h index ec448ef9b9f..335ee520791 100644 --- a/source/blender/editors/interface/interface_eyedropper_intern.h +++ b/source/blender/editors/interface/interface_eyedropper_intern.h @@ -25,7 +25,7 @@ /* interface_eyedropper.c */ void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name); -void eyedropper_draw_cursor_text_region(int x, int y, const char *name); +void eyedropper_draw_cursor_text_region(const int xy[2], const char *name); /** * Utility to retrieve a button representing a RNA property that is currently under the cursor. * @@ -51,7 +51,7 @@ void datadropper_win_area_find(const struct bContext *C, * * \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking. */ -void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3]); +void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]); /* Used for most eye-dropper operators. */ enum { diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 905fd452b6c..a171160d020 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3957,7 +3957,14 @@ static void ui_do_but_textedit( ui_textedit_delete_selection(but, data); } if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) { - ui_textedit_insert_buf(but, data, ime_data->str_result, ime_data->result_len); + if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) && + strcmp(ime_data->str_result, "\xE3\x80\x82") == 0) { + /* Convert Ideographic Full Stop (U+3002) to decimal point when entering numbers. */ + ui_textedit_insert_ascii(but, data, '.'); + } + else { + ui_textedit_insert_buf(but, data, ime_data->str_result, ime_data->result_len); + } } } else if (event->type == WM_IME_COMPOSITE_END) { diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 0f5b4a1a0f1..d7d3288a68d 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1949,6 +1949,16 @@ static void ui_id_preview_image_render_size( } } +void UI_icon_render_id_ex(const bContext *C, + Scene *scene, + ID *id_to_render, + const enum eIconSizes size, + const bool use_job, + PreviewImage *r_preview_image) +{ + ui_id_preview_image_render_size(C, scene, id_to_render, r_preview_image, size, use_job); +} + void UI_icon_render_id( const bContext *C, Scene *scene, ID *id, const enum eIconSizes size, const bool use_job) { @@ -1957,19 +1967,21 @@ void UI_icon_render_id( return; } + ID *id_to_render = id; + /* For objects, first try if a preview can created via the object data. */ if (GS(id->name) == ID_OB) { Object *ob = (Object *)id; if (ED_preview_id_is_supported(ob->data)) { - id = ob->data; + id_to_render = ob->data; } } - if (!ED_preview_id_is_supported(id)) { + if (!ED_preview_id_is_supported(id_to_render)) { return; } - ui_id_preview_image_render_size(C, scene, id, pi, size, use_job); + UI_icon_render_id_ex(C, scene, id_to_render, size, use_job, pi); } static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs) @@ -2323,8 +2335,8 @@ int UI_icon_from_idcode(const int idcode) return ICON_TEXT; case ID_VF: return ICON_FONT_DATA; - case ID_HA: - return ICON_HAIR_DATA; + case ID_CV: + return ICON_CURVES_DATA; case ID_PT: return ICON_POINTCLOUD_DATA; case ID_VO: diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index f7424066ad8..260e3dabc25 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -597,6 +597,9 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op) opop->operation = operation; } + /* Outliner e.g. has to be aware of this change. */ + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); + return operator_button_property_finish(C, &ptr, prop); } @@ -714,6 +717,9 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) } } + /* Outliner e.g. has to be aware of this change. */ + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); + return operator_button_property_finish(C, &ptr, prop); } @@ -2067,8 +2073,9 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot) /** \name UI Tree-View Item Rename Operator * * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to - * context menus for example, tree-view API users don't have to implement own renaming operators - * with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() override. + * context menus for example, tree-view API users don't have to implement their own renaming + * operators with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() + * override. * * \{ */ diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 44942d508ca..bf3fa6e62d4 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -39,9 +39,8 @@ #include "BKE_global.h" #include "BLF_api.h" -#ifdef WITH_INTERNATIONAL -# include "BLT_translation.h" -#endif + +#include "BLT_translation.h" #include "UI_interface.h" @@ -454,15 +453,6 @@ void uiStyleInit(void) printf("%s: error, no fonts available\n", __func__); } } - else { - /* ? just for speed to initialize? - * Yes, this build the glyph cache and create - * the texture. - */ - BLF_size(font->blf_id, 11.0f * U.pixelsize, U.dpi); - BLF_size(font->blf_id, 12.0f * U.pixelsize, U.dpi); - BLF_size(font->blf_id, 14.0f * U.pixelsize, U.dpi); - } } if (style == NULL) { @@ -486,8 +476,6 @@ void uiStyleInit(void) blf_mono_font = BLF_load_mono_default(unique); } - BLF_size(blf_mono_font, 12.0f * U.pixelsize, 72); - /* Set default flags based on UI preferences (not render fonts) */ { const int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT | @@ -530,8 +518,6 @@ void uiStyleInit(void) const bool unique = true; blf_mono_font_render = BLF_load_mono_default(unique); } - - BLF_size(blf_mono_font_render, 12.0f * U.pixelsize, 72); } void UI_fontstyle_set(const uiFontStyle *fs) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 8330f8c0db7..1f81dd21b83 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -792,8 +792,8 @@ static const char *template_id_browse_tip(const StructRNA *type) return N_("Browse Workspace to be linked"); case ID_LP: return N_("Browse LightProbe to be linked"); - case ID_HA: - return N_("Browse Hair Data to be linked"); + case ID_CV: + return N_("Browse Hair Curves Data to be linked"); case ID_PT: return N_("Browse Point Cloud Data to be linked"); case ID_VO: @@ -874,7 +874,7 @@ static uiBut *template_id_def_new_but(uiBlock *block, BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, BLT_I18NCONTEXT_ID_WORKSPACE, BLT_I18NCONTEXT_ID_LIGHTPROBE, - BLT_I18NCONTEXT_ID_HAIR, + BLT_I18NCONTEXT_ID_CURVES, BLT_I18NCONTEXT_ID_POINTCLOUD, BLT_I18NCONTEXT_ID_VOLUME, BLT_I18NCONTEXT_ID_SIMULATION, ); @@ -2672,18 +2672,6 @@ static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con) { - bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); - short proxy_protected, xco = 0, yco = 0; - // int rb_col; // UNUSED - - /* determine whether constraint is proxy protected or not */ - if (BKE_constraints_proxylocked_owner(ob, pchan)) { - proxy_protected = (con->flag & CONSTRAINT_PROXY_LOCAL) == 0; - } - else { - proxy_protected = 0; - } - /* unless button has own callback, it adds this callback to button */ uiBlock *block = uiLayoutGetBlock(layout); UI_block_func_set(block, constraint_active_func, ob, con); @@ -2708,72 +2696,23 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co uiLayout *row = uiLayoutRow(layout, true); - if (proxy_protected == 0) { - uiItemR(row, &ptr, "name", 0, "", ICON_NONE); - } - else { - uiItemL(row, IFACE_(con->name), ICON_NONE); - } - - /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */ - if (proxy_protected) { - UI_block_emboss_set(block, UI_EMBOSS_NONE); - - /* draw a ghost icon (for proxy) and also a lock beside it, - * to show that constraint is "proxy locked" */ - uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_GHOST_ENABLED, - xco + 12.2f * UI_UNIT_X, - yco, - 0.95f * UI_UNIT_X, - 0.95f * UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Proxy Protected")); - uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_LOCKED, - xco + 13.1f * UI_UNIT_X, - yco, - 0.95f * UI_UNIT_X, - 0.95f * UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Proxy Protected")); + uiItemR(row, &ptr, "name", 0, "", ICON_NONE); - UI_block_emboss_set(block, UI_EMBOSS); - } - else { - /* Enabled eye icon. */ - uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE); + /* Enabled eye icon. */ + uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE); - /* Extra operators menu. */ - uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con); + /* Extra operators menu. */ + uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con); - /* Close 'button' - emboss calls here disable drawing of 'button' behind X */ - sub = uiLayoutRow(row, false); - uiLayoutSetEmboss(sub, UI_EMBOSS_NONE); - uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT); - uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete"); - } + /* Close 'button' - emboss calls here disable drawing of 'button' behind X */ + sub = uiLayoutRow(row, false); + uiLayoutSetEmboss(sub, UI_EMBOSS_NONE); + uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT); + uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete"); /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */ uiItemS(layout); - /* Set but-locks for protected settings (magic numbers are used here!) */ - if (proxy_protected) { - UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint")); - } - /* clear any locks set up for proxies/lib-linking */ UI_block_lock_clear(block); } diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index cb1c3cedf8e..5db16354124 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -84,10 +84,6 @@ if(WITH_USD) add_definitions(-DWITH_USD) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_PUGIXML) add_definitions(-DWITH_PUGIXML) endif() diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 4e2ccea36ab..49d60ede277 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -131,6 +131,11 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) const bool use_instancing = RNA_boolean_get(op->ptr, "use_instancing"); const bool evaluation_mode = RNA_enum_get(op->ptr, "evaluation_mode"); + const bool generate_preview_surface = RNA_boolean_get(op->ptr, "generate_preview_surface"); + const bool export_textures = RNA_boolean_get(op->ptr, "export_textures"); + const bool overwrite_textures = RNA_boolean_get(op->ptr, "overwrite_textures"); + const bool relative_texture_paths = RNA_boolean_get(op->ptr, "relative_texture_paths"); + struct USDExportParams params = { export_animation, export_hair, @@ -141,6 +146,10 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) visible_objects_only, use_instancing, evaluation_mode, + generate_preview_surface, + export_textures, + overwrite_textures, + relative_texture_paths, }; bool ok = USD_export(C, filename, ¶ms, as_background_job); @@ -173,6 +182,26 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(col, ptr, "evaluation_mode", 0, NULL, ICON_NONE); box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("Materials")); + uiItemR(col, ptr, "generate_preview_surface", 0, NULL, ICON_NONE); + const bool export_mtl = RNA_boolean_get(ptr, "export_materials"); + uiLayoutSetActive(col, export_mtl); + + uiLayout *row = uiLayoutRow(col, true); + uiItemR(row, ptr, "export_textures", 0, NULL, ICON_NONE); + const bool preview = RNA_boolean_get(ptr, "generate_preview_surface"); + uiLayoutSetActive(row, export_mtl && preview); + + row = uiLayoutRow(col, true); + uiItemR(row, ptr, "overwrite_textures", 0, NULL, ICON_NONE); + const bool export_tex = RNA_boolean_get(ptr, "export_textures"); + uiLayoutSetActive(row, export_mtl && preview && export_tex); + + row = uiLayoutRow(col, true); + uiItemR(row, ptr, "relative_texture_paths", 0, NULL, ICON_NONE); + uiLayoutSetActive(row, export_mtl && preview); + + box = uiLayoutBox(layout); uiItemL(box, IFACE_("Experimental"), ICON_NONE); uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE); } @@ -249,6 +278,32 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "Use Settings for", "Determines visibility of objects, modifier settings, and other areas where there " "are different settings for viewport and rendering"); + + RNA_def_boolean(ot->srna, + "generate_preview_surface", + true, + "To USD Preview Surface", + "Generate an approximate USD Preview Surface shader " + "representation of a Principled BSDF node network"); + + RNA_def_boolean(ot->srna, + "export_textures", + true, + "Export Textures", + "If exporting materials, export textures referenced by material nodes " + "to a 'textures' directory in the same directory as the USD file"); + + RNA_def_boolean(ot->srna, + "overwrite_textures", + false, + "Overwrite Textures", + "Allow overwriting existing texture files when exporting textures"); + + RNA_def_boolean(ot->srna, + "relative_texture_paths", + true, + "Relative Texture Paths", + "Make texture asset paths relative to the USD file"); } /* ====== USD Import ====== */ diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 4ad2e57d266..0fb64c8a46b 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -78,10 +78,6 @@ set(LIB bf_windowmanager ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 7e05209f79e..8383492e459 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -32,6 +32,7 @@ #include "BLI_math.h" #include "BLI_math_bits.h" #include "BLI_rand.h" +#include "BLI_string.h" #include "BLI_utildefines_stack.h" #include "BKE_context.h" @@ -55,6 +56,8 @@ #include "ED_transform.h" #include "ED_view3d.h" +#include "BLT_translation.h" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -1389,6 +1392,37 @@ static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *e return edbm_select_mode_exec(C, op); } +static char *edbm_select_mode_get_description(struct bContext *UNUSED(C), + struct wmOperatorType *UNUSED(op), + struct PointerRNA *values) +{ + const int type = RNA_enum_get(values, "type"); + + /* Because the special behavior for shift and ctrl click depend on user input, they may be + * incorrect if the operator is used from a script or from a special button. So only return the + * specialized descriptions if only the "type" is set, which conveys that the operator is meant + * to be used with the logic in the `invoke` method. */ + if (RNA_struct_property_is_set(values, "type") && + !RNA_struct_property_is_set(values, "use_extend") && + !RNA_struct_property_is_set(values, "use_expand") && + !RNA_struct_property_is_set(values, "action")) { + switch (type) { + case SCE_SELECT_VERTEX: + return BLI_strdup(TIP_( + "Vertex select - Shift-Click for multiple modes, Ctrl-Click contracts selection")); + case SCE_SELECT_EDGE: + return BLI_strdup( + TIP_("Edge select - Shift-Click for multiple modes, " + "Ctrl-Click expands/contracts selection depending on the current mode")); + case SCE_SELECT_FACE: + return BLI_strdup( + TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection")); + } + } + + return NULL; +} + void MESH_OT_select_mode(wmOperatorType *ot) { PropertyRNA *prop; @@ -1409,6 +1443,7 @@ void MESH_OT_select_mode(wmOperatorType *ot) ot->invoke = edbm_select_mode_invoke; ot->exec = edbm_select_mode_exec; ot->poll = ED_operator_editmesh; + ot->get_description = edbm_select_mode_get_description; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 013d5e5a661..f3db8f1f0d2 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -334,9 +334,6 @@ void EDBM_mesh_clear(BMEditMesh *em) /* clear bmesh */ BM_mesh_clear(em->bm); - /* Free evaluated meshes & cache. */ - BKE_editmesh_free_derived_caches(em); - /* free tessellation data */ em->tottri = 0; MEM_SAFE_FREE(em->looptris); @@ -1404,8 +1401,6 @@ void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params) BM_lnorspace_invalidate(em->bm, false); em->bm->spacearr_dirty &= ~BM_SPACEARR_BMO_SET; } - /* Don't keep stale evaluated mesh data around, see: T38872. */ - BKE_editmesh_free_derived_caches(em); #ifdef DEBUG { diff --git a/source/blender/editors/metaball/CMakeLists.txt b/source/blender/editors/metaball/CMakeLists.txt index 4e600dc0277..a247920c305 100644 --- a/source/blender/editors/metaball/CMakeLists.txt +++ b/source/blender/editors/metaball/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC ../../blenkernel ../../blenlib ../../depsgraph + ../../gpu ../../makesdna ../../makesrna ../../render diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index bedb9d4f4f4..51cfc920d1d 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -47,6 +47,8 @@ #include "DEG_depsgraph.h" +#include "GPU_select.h" + #include "ED_mball.h" #include "ED_object.h" #include "ED_screen.h" @@ -756,15 +758,19 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese static MetaElem *startelem = NULL; ViewContext vc; int a, hits; - uint buffer[MAXPICKBUF]; + GPUSelectResult buffer[MAXPICKELEMS]; rcti rect; ED_view3d_viewcontext_init(C, &vc, depsgraph); BLI_rcti_init_pt_radius(&rect, mval, 12); - hits = view3d_opengl_select( - &vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP); + hits = view3d_opengl_select(&vc, + buffer, + ARRAY_SIZE(buffer), + &rect, + VIEW3D_SELECT_PICK_NEAREST, + VIEW3D_SELECT_FILTER_NOP); FOREACH_BASE_IN_EDIT_MODE_BEGIN (vc.view_layer, vc.v3d, base) { ED_view3d_viewcontext_init_object(&vc, base->object); @@ -789,7 +795,7 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese ml = startelem; while (ml) { for (a = 0; a < hits; a++) { - int hitresult = buffer[(4 * a) + 3]; + const int hitresult = buffer[a].id; if (hitresult == -1) { continue; } diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 040b5cd5066..df76e605ebb 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -85,14 +85,10 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_HAIR_NODES) + add_definitions(-DWITH_NEW_CURVES_TYPE) endif() blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 06e21f91d04..d1deb6824ea 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -62,6 +62,7 @@ #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_curves.h" #include "BKE_displist.h" #include "BKE_duplilist.h" #include "BKE_effect.h" @@ -69,7 +70,6 @@ #include "BKE_gpencil_curve.h" #include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" -#include "BKE_hair.h" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_layer.h" @@ -1894,18 +1894,18 @@ void OBJECT_OT_speaker_add(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Add Hair Operator +/** \name Add Hair Curves Operator * \{ */ -static bool object_hair_add_poll(bContext *C) +static bool object_hair_curves_add_poll(bContext *C) { - if (!U.experimental.use_new_hair_type) { + if (!U.experimental.use_new_curves_type) { return false; } return ED_operator_objectmode(C); } -static int object_hair_add_exec(bContext *C, wmOperator *op) +static int object_hair_curves_add_exec(bContext *C, wmOperator *op) { ushort local_view_bits; float loc[3], rot[3]; @@ -1913,22 +1913,22 @@ static int object_hair_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - Object *object = ED_object_add_type(C, OB_HAIR, NULL, loc, rot, false, local_view_bits); + Object *object = ED_object_add_type(C, OB_CURVES, NULL, loc, rot, false, local_view_bits); object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */ return OPERATOR_FINISHED; } -void OBJECT_OT_hair_add(wmOperatorType *ot) +void OBJECT_OT_hair_curves_add(wmOperatorType *ot) { /* identifiers */ - ot->name = "Add Hair"; - ot->description = "Add a hair object to the scene"; - ot->idname = "OBJECT_OT_hair_add"; + ot->name = "Add Hair Curves"; + ot->description = "Add a hair curves object to the scene"; + ot->idname = "OBJECT_OT_hair_curves_add"; /* api callbacks */ - ot->exec = object_hair_add_exec; - ot->poll = object_hair_add_poll; + ot->exec = object_hair_curves_add_exec; + ot->poll = object_hair_curves_add_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1998,6 +1998,10 @@ void ED_object_base_free_and_unlink(Main *bmain, Scene *scene, Object *ob) ob->id.name + 2); return; } + if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) { + /* Do not delete objects used by overrides of collections. */ + return; + } DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_BASE_FLAGS); @@ -2038,10 +2042,9 @@ static int object_delete_exec(bContext *C, wmOperator *op) } if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) { - /* Can this case ever happen? */ BKE_reportf(op->reports, RPT_WARNING, - "Cannot delete object '%s' as it used by override collections", + "Cannot delete object '%s' as it is used by override collections", ob->id.name + 2); continue; } @@ -2344,11 +2347,6 @@ static void make_object_duplilist_real(bContext *C, BKE_animdata_free(&ob_dst->id, true); ob_dst->adt = NULL; - /* Proxies are not to be copied. */ - ob_dst->proxy_from = NULL; - ob_dst->proxy_group = NULL; - ob_dst->proxy = NULL; - ob_dst->parent = NULL; BKE_constraints_free(&ob_dst->constraints); ob_dst->runtime.curve_cache = NULL; @@ -2463,13 +2461,6 @@ static void make_object_duplilist_real(bContext *C, } if (base->object->transflag & OB_DUPLICOLLECTION && base->object->instance_collection) { - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->proxy_group == base->object) { - ob->proxy = NULL; - ob->proxy_from = NULL; - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - } - } base->object->instance_collection = NULL; } @@ -3731,6 +3722,7 @@ static bool object_join_poll(bContext *C) static int object_join_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Object *ob = CTX_data_active_object(C); if (ob->mode & OB_MODE_EDIT) { @@ -3741,6 +3733,14 @@ static int object_join_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Cannot edit external library data"); return OPERATOR_CANCELLED; } + if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) { + BKE_reportf(op->reports, + RPT_WARNING, + "Cannot edit object '%s' as it is used by override collections", + ob->id.name + 2); + return OPERATOR_CANCELLED; + } + if (ob->type == OB_GPENCIL) { bGPdata *gpd = (bGPdata *)ob->data; if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { @@ -3829,6 +3829,7 @@ static bool join_shapes_poll(bContext *C) static int join_shapes_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Object *ob = CTX_data_active_object(C); if (ob->mode & OB_MODE_EDIT) { @@ -3839,6 +3840,13 @@ static int join_shapes_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Cannot edit external library data"); return OPERATOR_CANCELLED; } + if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) { + BKE_reportf(op->reports, + RPT_WARNING, + "Cannot edit object '%s' as it is used by override collections", + ob->id.name + 2); + return OPERATOR_CANCELLED; + } if (ob->type == OB_MESH) { return ED_mesh_shapes_join_objects_exec(C, op); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index d56d0edd5a2..f52d2103fff 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -188,7 +188,7 @@ static bool write_internal_bake_pixels(Image *image, const char margin_type, const bool is_clear, const bool is_noncolor, - Mesh const *mesh, + Mesh const *mesh_eval, char const *uv_layer) { ImBuf *ibuf; @@ -285,7 +285,7 @@ static bool write_internal_bake_pixels(Image *image, /* margins */ if (margin > 0) { - RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh, uv_layer); + RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer); } ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; @@ -334,7 +334,7 @@ static bool write_external_bake_pixels(const char *filepath, const int margin_type, ImageFormatData *im_format, const bool is_noncolor, - Mesh const *mesh, + Mesh const *mesh_eval, char const *uv_layer) { ImBuf *ibuf = NULL; @@ -392,7 +392,7 @@ static bool write_external_bake_pixels(const char *filepath, mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask"); RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer); - RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh, uv_layer); + RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer); if (mask_buffer) { MEM_freeN(mask_buffer); @@ -774,10 +774,10 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr, BakeTargets *targets, Object *ob, BakePixel *pixel_array, - ReportList *reports) + ReportList *reports, + Mesh *mesh_eval) { bool all_ok = true; - const Mesh *me = (Mesh *)ob->data; for (int i = 0; i < targets->num_images; i++) { BakeImage *bk_image = &targets->images[i]; @@ -791,7 +791,7 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr, bkr->margin_type, bkr->is_clear, targets->is_noncolor, - me, + mesh_eval, bkr->uv_layer); /* might be read by UI to set active image for display */ @@ -852,7 +852,7 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr, BakeTargets *targets, Object *ob, Object *ob_eval, - Mesh *me, + Mesh *mesh_eval, BakePixel *pixel_array, ReportList *reports) { @@ -886,8 +886,8 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr, if (ob_eval->mat[i]) { BLI_path_suffix(name, FILE_MAX, ob_eval->mat[i]->id.name + 2, "_"); } - else if (me->mat[i]) { - BLI_path_suffix(name, FILE_MAX, me->mat[i]->id.name + 2, "_"); + else if (mesh_eval->mat[i]) { + BLI_path_suffix(name, FILE_MAX, mesh_eval->mat[i]->id.name + 2, "_"); } else { /* if everything else fails, use the material index */ @@ -909,7 +909,7 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr, bkr->margin_type, &bake->im_format, targets->is_noncolor, - me, + mesh_eval, bkr->uv_layer); if (!ok) { @@ -1211,7 +1211,7 @@ static bool bake_targets_output(const BakeAPIRender *bkr, { if (bkr->target == R_BAKE_TARGET_IMAGE_TEXTURES) { if (bkr->save_mode == R_BAKE_SAVE_INTERNAL) { - return bake_targets_output_internal(bkr, targets, ob, pixel_array, reports); + return bake_targets_output_internal(bkr, targets, ob, pixel_array, reports, me_eval); } if (bkr->save_mode == R_BAKE_SAVE_EXTERNAL) { return bake_targets_output_external( diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 91a512ae8e9..7ef17689912 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1356,15 +1356,6 @@ void ED_object_constraint_update(Main *bmain, Object *ob) static void object_pose_tag_update(Main *bmain, Object *ob) { BKE_pose_tag_recalc(bmain, ob->pose); /* Checks & sort pose channels. */ - if (ob->proxy && ob->adt) { - /* We need to make use of ugly #POSE_ANIMATION_WORKAROUND here too, - * else anim data are not reloaded after calling `BKE_pose_rebuild()`, - * which causes T43872. - * Note that this is a bit wide here, since we cannot be sure whether there are some locked - * proxy bones or not. - * XXX Temp hack until new depsgraph hopefully solves this. */ - DEG_id_tag_update(&ob->id, ID_RECALC_ANIMATION); - } } void ED_object_constraint_dependency_update(Main *bmain, Object *ob) @@ -2453,12 +2444,6 @@ static int constraint_add_exec( if ((ob->type == OB_ARMATURE) && (pchan)) { BKE_pose_tag_recalc(bmain, ob->pose); /* sort pose channels */ - if (BKE_constraints_proxylocked_owner(ob, pchan) && ob->adt) { - /* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too, - * else anim data are not reloaded after calling `BKE_pose_rebuild()`, which causes T43872. - * XXX Temp hack until new depsgraph hopefully solves this. */ - DEG_id_tag_update(&ob->id, ID_RECALC_ANIMATION); - } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM); } else { diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index 49149a5152f..3744cbee3a4 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -43,6 +43,8 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "BLT_translation.h" + #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -603,6 +605,20 @@ static bool data_transfer_poll_property(const bContext *UNUSED(C), return true; } +static char *data_transfer_get_description(bContext *UNUSED(C), + wmOperatorType *UNUSED(ot), + PointerRNA *ptr) +{ + const bool reverse_transfer = RNA_boolean_get(ptr, "use_reverse_transfer"); + + if (reverse_transfer) { + return BLI_strdup(TIP_( + "Transfer data layer(s) (weights, edge sharp, etc.) from selected meshes to active one")); + } + + return NULL; +} + void OBJECT_OT_data_transfer(wmOperatorType *ot) { PropertyRNA *prop; @@ -619,6 +635,7 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot) ot->invoke = WM_menu_invoke; ot->exec = data_transfer_exec; ot->check = data_transfer_check; + ot->get_description = data_transfer_get_description; /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 38d0a044cb4..a38a5165c8c 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1976,8 +1976,14 @@ static void move_to_collection_menu_create(bContext *C, uiLayout *layout, void * RNA_int_set(&menu->ptr, "collection_index", menu->index); RNA_boolean_set(&menu->ptr, "is_new", true); - uiItemFullO_ptr( - layout, menu->ot, "New Collection", ICON_ADD, menu->ptr.data, WM_OP_INVOKE_DEFAULT, 0, NULL); + uiItemFullO_ptr(layout, + menu->ot, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "New Collection"), + ICON_ADD, + menu->ptr.data, + WM_OP_INVOKE_DEFAULT, + 0, + NULL); uiItemS(layout); diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index d517d68f1fc..ddd44fb9ded 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -65,7 +65,6 @@ void OBJECT_OT_track_set(struct wmOperatorType *ot); void OBJECT_OT_track_clear(struct wmOperatorType *ot); void OBJECT_OT_make_local(struct wmOperatorType *ot); void OBJECT_OT_make_override_library(struct wmOperatorType *ot); -void OBJECT_OT_convert_proxy_to_override(struct wmOperatorType *ot); void OBJECT_OT_make_single_user(struct wmOperatorType *ot); void OBJECT_OT_make_links_scene(struct wmOperatorType *ot); void OBJECT_OT_make_links_data(struct wmOperatorType *ot); @@ -130,7 +129,7 @@ void OBJECT_OT_light_add(struct wmOperatorType *ot); void OBJECT_OT_effector_add(struct wmOperatorType *ot); void OBJECT_OT_camera_add(struct wmOperatorType *ot); void OBJECT_OT_speaker_add(struct wmOperatorType *ot); -void OBJECT_OT_hair_add(struct wmOperatorType *ot); +void OBJECT_OT_hair_curves_add(struct wmOperatorType *ot); void OBJECT_OT_pointcloud_add(struct wmOperatorType *ot); /** * Only used as menu. diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 71ad54383a6..af428512cfd 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -53,12 +53,12 @@ #include "BKE_armature.h" #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_curves.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_effect.h" #include "BKE_global.h" #include "BKE_gpencil_modifier.h" -#include "BKE_hair.h" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_lib_id.h" @@ -84,6 +84,8 @@ #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_query.h" +#include "BLT_translation.h" + #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -130,8 +132,8 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object * else if (ob->type == OB_GPENCIL) { BKE_gpencil_modifiers_calc(depsgraph, scene_eval, ob_eval); } - else if (ob->type == OB_HAIR) { - BKE_hair_data_update(depsgraph, scene_eval, ob); + else if (ob->type == OB_CURVES) { + BKE_curves_data_update(depsgraph, scene_eval, ob); } else if (ob->type == OB_POINTCLOUD) { BKE_pointcloud_data_update(depsgraph, scene_eval, ob); @@ -763,6 +765,12 @@ static bool modifier_apply_obdata( BKE_object_material_from_eval_data(bmain, ob, &mesh_applied->id); BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true); + /* Anonymous attributes shouldn't by available on the applied geometry. */ + CustomData_free_layers_anonymous(&me->vdata, me->totvert); + CustomData_free_layers_anonymous(&me->edata, me->totedge); + CustomData_free_layers_anonymous(&me->pdata, me->totpoly); + CustomData_free_layers_anonymous(&me->ldata, me->totloop); + if (md_eval->type == eModifierType_Multires) { multires_customdata_delete(me); } @@ -1515,7 +1523,7 @@ static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED( bool keep = RNA_boolean_get(values, "keep_modifier"); if (keep) { - return BLI_strdup("Apply modifier as a new shapekey and keep it in the stack"); + return BLI_strdup(TIP_("Apply modifier as a new shapekey and keep it in the stack")); } return NULL; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index b171da42522..a9a429e7e6f 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -75,7 +75,6 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_track_clear); WM_operatortype_append(OBJECT_OT_make_local); WM_operatortype_append(OBJECT_OT_make_override_library); - WM_operatortype_append(OBJECT_OT_convert_proxy_to_override); WM_operatortype_append(OBJECT_OT_make_single_user); WM_operatortype_append(OBJECT_OT_make_links_scene); WM_operatortype_append(OBJECT_OT_make_links_data); @@ -106,7 +105,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_light_add); WM_operatortype_append(OBJECT_OT_camera_add); WM_operatortype_append(OBJECT_OT_speaker_add); - WM_operatortype_append(OBJECT_OT_hair_add); + WM_operatortype_append(OBJECT_OT_hair_curves_add); WM_operatortype_append(OBJECT_OT_pointcloud_add); WM_operatortype_append(OBJECT_OT_volume_add); WM_operatortype_append(OBJECT_OT_volume_import); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index a6eb35d49b9..8678bf9bd92 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -63,11 +63,11 @@ #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_curves.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_fcurve.h" #include "BKE_gpencil.h" -#include "BKE_hair.h" #include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_lattice.h" @@ -1872,7 +1872,7 @@ static void single_obdata_users( ob->data, BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; - case OB_HAIR: + case OB_CURVES: ob->data = ID_NEW_SET( ob->data, BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); @@ -2334,7 +2334,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); const bool success = BKE_lib_override_library_create( - bmain, scene, view_layer, id_root, &obact->id, NULL); + bmain, scene, view_layer, NULL, id_root, &obact->id, NULL); /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ @@ -2419,63 +2419,6 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot) ot->prop = prop; } -static bool convert_proxy_to_override_poll(bContext *C) -{ - Object *obact = CTX_data_active_object(C); - - return obact != NULL && obact->proxy != NULL; -} - -static int convert_proxy_to_override_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - - Object *ob_proxy = CTX_data_active_object(C); - Object *ob_proxy_group = ob_proxy->proxy_group; - const bool is_override_instancing_object = ob_proxy_group != NULL; - - const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, view_layer, ob_proxy); - - if (!success) { - BKE_reportf( - op->reports, - RPT_ERROR_INVALID_INPUT, - "Could not create a library override from proxy '%s' (might use already local data?)", - ob_proxy->id.name + 2); - return OPERATOR_CANCELLED; - } - - /* Remove the instance empty from this scene, the items now have an overridden collection - * instead. */ - if (is_override_instancing_object) { - ED_object_base_free_and_unlink(bmain, scene, ob_proxy_group); - } - - DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_WINDOW, NULL); - - return OPERATOR_FINISHED; -} - -void OBJECT_OT_convert_proxy_to_override(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Convert Proxy to Override"; - ot->description = "Convert a proxy to a local library override"; - ot->idname = "OBJECT_OT_convert_proxy_to_override"; - - /* api callbacks */ - ot->exec = convert_proxy_to_override_exec; - ot->poll = convert_proxy_to_override_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /** \} */ /* ------------------------------------------------------------------- */ diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index fd649854d8f..a871ddea48c 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -43,13 +43,16 @@ #include "DNA_object_types.h" #include "BKE_context.h" +#include "BKE_crazyspace.h" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_object.h" +#include "BKE_report.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "BLI_sys_types.h" /* for intptr_t support */ diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 3e74aaeeb10..ed0f7b2fad0 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -39,6 +39,7 @@ #include "BLI_alloca.h" #include "BLI_array.h" +#include "BLI_bitmap.h" #include "BLI_blenlib.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -63,6 +64,8 @@ #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_query.h" +#include "BLT_translation.h" + #include "DNA_armature_types.h" #include "RNA_access.h" #include "RNA_define.h" @@ -2464,17 +2467,14 @@ void ED_vgroup_mirror(Object *ob, sel = sel_mirr = true; } - /* tag verts we have used */ - for (vidx = 0, mv = me->mvert; vidx < me->totvert; vidx++, mv++) { - mv->flag &= ~ME_VERT_TMP_TAG; - } + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(me->totvert, __func__); for (vidx = 0, mv = me->mvert; vidx < me->totvert; vidx++, mv++) { - if ((mv->flag & ME_VERT_TMP_TAG) == 0) { + if (!BLI_BITMAP_TEST(vert_tag, vidx)) { if ((vidx_mirr = mesh_get_x_mirror_vert(ob, NULL, vidx, use_topology)) != -1) { if (vidx != vidx_mirr) { mv_mirr = &me->mvert[vidx_mirr]; - if ((mv_mirr->flag & ME_VERT_TMP_TAG) == 0) { + if (!BLI_BITMAP_TEST(vert_tag, vidx_mirr)) { if (use_vert_sel) { sel = mv->flag & SELECT; @@ -2489,8 +2489,8 @@ void ED_vgroup_mirror(Object *ob, totmirr++; } - mv->flag |= ME_VERT_TMP_TAG; - mv_mirr->flag |= ME_VERT_TMP_TAG; + BLI_BITMAP_ENABLE(vert_tag, vidx); + BLI_BITMAP_ENABLE(vert_tag, vidx_mirr); } } } @@ -2499,6 +2499,8 @@ void ED_vgroup_mirror(Object *ob, } } } + + MEM_freeN(vert_tag); } } else if (ob->type == OB_LATTICE) { @@ -3427,16 +3429,16 @@ static char *vertex_group_lock_description(struct bContext *UNUSED(C), switch (action) { case VGROUP_LOCK: - action_str = "Lock"; + action_str = TIP_("Lock"); break; case VGROUP_UNLOCK: - action_str = "Unlock"; + action_str = TIP_("Unlock"); break; case VGROUP_TOGGLE: - action_str = "Toggle locks of"; + action_str = TIP_("Toggle locks of"); break; case VGROUP_INVERT: - action_str = "Invert locks of"; + action_str = TIP_("Invert locks of"); break; default: return NULL; @@ -3444,34 +3446,34 @@ static char *vertex_group_lock_description(struct bContext *UNUSED(C), switch (mask) { case VGROUP_MASK_ALL: - target_str = "all"; + target_str = TIP_("all"); break; case VGROUP_MASK_SELECTED: - target_str = "selected"; + target_str = TIP_("selected"); break; case VGROUP_MASK_UNSELECTED: - target_str = "unselected"; + target_str = TIP_("unselected"); break; case VGROUP_MASK_INVERT_UNSELECTED: switch (action) { case VGROUP_INVERT: - target_str = "selected"; + target_str = TIP_("selected"); break; case VGROUP_LOCK: - target_str = "selected and unlock unselected"; + target_str = TIP_("selected and unlock unselected"); break; case VGROUP_UNLOCK: - target_str = "selected and lock unselected"; + target_str = TIP_("selected and lock unselected"); break; default: - target_str = "all and invert unselected"; + target_str = TIP_("all and invert unselected"); } break; default: return NULL; } - return BLI_sprintfN("%s %s vertex groups of the active object", action_str, target_str); + return BLI_sprintfN(TIP_("%s %s vertex groups of the active object"), action_str, target_str); } void OBJECT_OT_vertex_group_lock(wmOperatorType *ot) diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt index a607663763e..7c32a2dcf1d 100644 --- a/source/blender/editors/physics/CMakeLists.txt +++ b/source/blender/editors/physics/CMakeLists.txt @@ -60,10 +60,6 @@ if(WITH_MOD_FLUID) add_definitions(-DWITH_FLUID) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_BULLET) list(APPEND INC ../../../../intern/rigidbody diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt index 31dca83a3ab..934badd3b6f 100644 --- a/source/blender/editors/render/CMakeLists.txt +++ b/source/blender/editors/render/CMakeLists.txt @@ -28,6 +28,7 @@ set(INC ../../imbuf ../../makesdna ../../makesrna + ../../nodes ../../render ../../sequencer ../../windowmanager @@ -66,8 +67,4 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc index 1d0de322271..d04e45e4ccb 100644 --- a/source/blender/editors/render/render_internal.cc +++ b/source/blender/editors/render/render_internal.cc @@ -56,6 +56,8 @@ #include "BKE_scene.h" #include "BKE_screen.h" +#include "NOD_composite.h" + #include "DEG_depsgraph.h" #include "WM_api.h" @@ -88,7 +90,7 @@ struct RenderJob { Scene *scene; ViewLayer *single_layer; Scene *current_scene; - /* TODO(sergey): Should not be needed once engine will have own + /* TODO(sergey): Should not be needed once engine will have its own * depsgraph and copy-on-write will be implemented. */ Depsgraph *depsgraph; @@ -614,8 +616,14 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL) { image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, &tile_rect, offset_x, offset_y, viewname); } - BKE_image_update_gputexture_delayed( - ima, ibuf, offset_x, offset_y, BLI_rcti_size_x(&tile_rect), BLI_rcti_size_y(&tile_rect)); + ImageTile *image_tile = BKE_image_get_tile(ima, 0); + BKE_image_update_gputexture_delayed(ima, + image_tile, + ibuf, + offset_x, + offset_y, + BLI_rcti_size_x(&tile_rect), + BLI_rcti_size_y(&tile_rect)); /* make jobs timer to send notifier */ *(rj->do_update) = true; @@ -973,7 +981,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even rj->scene = scene; rj->current_scene = rj->scene; rj->single_layer = single_layer; - /* TODO(sergey): Render engine should be using own depsgraph. + /* TODO(sergey): Render engine should be using its own depsgraph. * * NOTE: Currently is only used by ED_update_for_newframe() at the end of the render, so no * need to ensure evaluation here. */ diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 8bd0244c899..fb6742c9fd5 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -72,8 +72,11 @@ #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" + #include "RE_pipeline.h" +#include "BLT_translation.h" + #include "RNA_access.h" #include "RNA_define.h" @@ -632,7 +635,7 @@ static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data) case ID_MC: /* MovieClip */ case ID_MSK: /* Mask */ case ID_LP: /* LightProbe */ - case ID_HA: /* Hair */ + case ID_CV: /* Curves */ case ID_PT: /* PointCloud */ case ID_VO: /* Volume */ case ID_SIM: /* Simulation */ @@ -1326,12 +1329,12 @@ static char *screen_opengl_render_description(struct bContext *UNUSED(C), } if (RNA_boolean_get(ptr, "render_keyed_only")) { - return BLI_strdup( + return BLI_strdup(TIP_( "Render the viewport for the animation range of this scene, but only render keyframes of " - "selected objects"); + "selected objects")); } - return BLI_strdup("Render the viewport for the animation range of this scene"); + return BLI_strdup(TIP_("Render the viewport for the animation range of this scene")); } void RENDER_OT_opengl(wmOperatorType *ot) diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index 79c3b2f7ac6..c1c75e485f7 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -767,7 +767,7 @@ struct ObjectPreviewData { /* The main for the preview, not of the current file. */ Main *pr_main; /* Copy of the object to create the preview for. The copy is for thread safety (and to insert - * it into an own main). */ + * it into its own main). */ Object *object; /* Current frame. */ int cfra; @@ -1061,11 +1061,11 @@ static void shader_preview_texture(ShaderPreview *sp, Tex *tex, Scene *sce, Rend /* Evaluate texture at tex_coord. */ TexResult texres = {0}; BKE_texture_get_value_ex(sce, tex, tex_coord, &texres, img_pool, color_manage); - - rect_float[0] = texres.tr; - rect_float[1] = texres.tg; - rect_float[2] = texres.tb; - rect_float[3] = texres.talpha ? texres.ta : 1.0f; + copy_v4_fl4(rect_float, + texres.trgba[0], + texres.trgba[1], + texres.trgba[2], + texres.talpha ? texres.trgba[3] : 1.0f); rect_float += 4; } diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index 992bba420c1..19ab6841e22 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -64,6 +64,8 @@ #include "BKE_workspace.h" #include "BKE_world.h" +#include "NOD_composite.h" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" diff --git a/source/blender/editors/render/render_update.cc b/source/blender/editors/render/render_update.cc index b2958efde9b..b1e8e3927ef 100644 --- a/source/blender/editors/render/render_update.cc +++ b/source/blender/editors/render/render_update.cc @@ -49,6 +49,8 @@ #include "BKE_paint.h" #include "BKE_scene.h" +#include "NOD_composite.h" + #include "RE_engine.h" #include "RE_pipeline.h" diff --git a/source/blender/editors/scene/CMakeLists.txt b/source/blender/editors/scene/CMakeLists.txt index cd59f06c6e3..ce0c2062766 100644 --- a/source/blender/editors/scene/CMakeLists.txt +++ b/source/blender/editors/scene/CMakeLists.txt @@ -39,8 +39,5 @@ set(LIB bf_blenlib ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() blender_add_lib(bf_editor_scene "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt index d194d0cdbb6..7c5ae4dcd5e 100644 --- a/source/blender/editors/screen/CMakeLists.txt +++ b/source/blender/editors/screen/CMakeLists.txt @@ -57,9 +57,5 @@ set(LIB bf_editor_space_sequencer ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_screen "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 5a2b53163b8..8dd4b940491 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1662,7 +1662,7 @@ void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable) } /* Seek audio to ensure playback in preview range with AV sync. */ - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); /* Notifier caught by top header, for button. */ WM_event_add_notifier(C, NC_SCREEN | ND_ANIMPLAY, NULL); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 6581bffb6bd..4af965621e3 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2608,7 +2608,7 @@ typedef struct RegionMoveData { ARegion *region; ScrArea *area; int bigger, smaller, origval; - int origx, origy; + int orig_xy[2]; int maxsize; AZEdge edge; @@ -2716,8 +2716,7 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event } rmd->area = sad->sa1; rmd->edge = az->edge; - rmd->origx = event->xy[0]; - rmd->origy = event->xy[1]; + copy_v2_v2_int(rmd->orig_xy, event->xy); rmd->maxsize = area_max_regionsize(rmd->area, rmd->region, rmd->edge); /* if not set we do now, otherwise it uses type */ @@ -2804,7 +2803,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) (BLI_rcti_size_x(&rmd->region->v2d.mask) + 1); const int snap_size_threshold = (U.widget_unit * 2) / aspect; if (ELEM(rmd->edge, AE_LEFT_TO_TOPRIGHT, AE_RIGHT_TO_TOPLEFT)) { - delta = event->xy[0] - rmd->origx; + delta = event->xy[0] - rmd->orig_xy[0]; if (rmd->edge == AE_LEFT_TO_TOPRIGHT) { delta = -delta; } @@ -2837,7 +2836,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else { - delta = event->xy[1] - rmd->origy; + delta = event->xy[1] - rmd->orig_xy[1]; if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) { delta = -delta; } @@ -2879,7 +2878,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) } case LEFTMOUSE: if (event->val == KM_RELEASE) { - if (len_manhattan_v2v2_int(event->xy, &rmd->origx) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { + if (len_manhattan_v2v2_int(event->xy, rmd->orig_xy) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { if (rmd->region->flag & RGN_FLAG_HIDDEN) { region_scale_toggle_hidden(C, rmd); } @@ -2986,7 +2985,7 @@ static int frame_offset_exec(bContext *C, wmOperator *op) areas_do_frame_follow(C, false); - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -3047,7 +3046,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op) areas_do_frame_follow(C, true); - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } @@ -3161,7 +3160,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) areas_do_frame_follow(C, true); - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -3225,7 +3224,7 @@ static int marker_jump_exec(bContext *C, wmOperator *op) areas_do_frame_follow(C, true); - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -4626,7 +4625,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con if (scene_eval == NULL) { /* Happens when undo/redo system is used during playback, nothing meaningful we can do here. */ } - else if (scene_eval->id.recalc & ID_RECALC_AUDIO_SEEK) { + else if (scene_eval->id.recalc & ID_RECALC_FRAME_CHANGE) { /* Ignore seek here, the audio will be updated to the scene frame after jump during next * dependency graph update. */ } @@ -4741,7 +4740,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con } if (sad->flag & ANIMPLAY_FLAG_JUMPED) { - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); #ifdef PROFILE_AUDIO_SYNCH old_frame = CFRA; #endif diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 5c8d7a9e9b6..907080626e0 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../gpu ../../imbuf ../../makesdna + ../../nodes ../../makesrna ../../render ../../windowmanager @@ -89,9 +90,5 @@ set(LIB bf_blenlib ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 265746e27cd..4010b87a2ed 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -47,6 +47,8 @@ #include "BKE_object.h" #include "BKE_paint.h" +#include "NOD_texture.h" + #include "WM_api.h" #include "wm_cursors.h" diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index dc2eaacca0c..a912bb5cf3b 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -56,6 +56,8 @@ #include "BKE_paint.h" #include "BKE_undo_system.h" +#include "NOD_texture.h" + #include "DEG_depsgraph.h" #include "UI_interface.h" diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index ca012f20f01..f05cd4c3d5f 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1730,17 +1730,12 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps, normalize_v3(no); } else { -#if 1 /* In case the normalizing per pixel isn't optimal, * we could cache or access from evaluated mesh. */ normal_tri_v3(no, ps->mvert_eval[lt_vtri[0]].co, ps->mvert_eval[lt_vtri[1]].co, ps->mvert_eval[lt_vtri[2]].co); -#else - /* Don't use because some modifiers don't have normal data (subsurf for eg). */ - copy_v3_v3(no, (float *)ps->dm->getTessFaceData(ps->dm, tri_index, CD_NORMAL)); -#endif } if (UNLIKELY(ps->is_flip_object)) { diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index 1dfc4db6ac3..1234a56853c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -428,9 +428,9 @@ void PAINT_OT_weight_sample_group(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO; - /* keyingset to use (dynamic enum) */ + /* Group to use (dynamic enum). */ prop = RNA_def_enum( - ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use"); + ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "Vertex group to set as active"); RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf); RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); ot->prop = prop; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 91e44a0b062..07e26a3d931 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -73,6 +73,8 @@ #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" +#include "NOD_texture.h" + #include "DEG_depsgraph.h" #include "IMB_colormanagement.h" @@ -176,13 +178,8 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - if (ss->shapekey_active || ss->deform_modifiers_active) { - const float(*vert_normals)[3] = BKE_pbvh_get_vert_normals(ss->pbvh); - copy_v3_v3(no, vert_normals[index]); - } - else { - copy_v3_v3(no, ss->vert_normals[index]); - } + const float(*vert_normals)[3] = BKE_pbvh_get_vert_normals(ss->pbvh); + copy_v3_v3(no, vert_normals[index]); break; } case PBVH_BMESH: diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 184d715a347..ba4ba04d548 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -658,7 +658,7 @@ static char *actkeys_paste_description(bContext *UNUSED(C), { /* Custom description if the 'flipped' option is used. */ if (RNA_boolean_get(ptr, "flipped")) { - return BLI_strdup("Paste keyframes from mirrored bones if they exist"); + return BLI_strdup(TIP_("Paste keyframes from mirrored bones if they exist")); } /* Use the default description in the other cases. */ diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 4463856f40a..ba96ac52f1f 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -36,6 +36,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_lib_remap.h" #include "BKE_nla.h" #include "BKE_screen.h" @@ -814,20 +815,15 @@ static void action_refresh(const bContext *C, ScrArea *area) /* XXX re-sizing y-extents of tot should go here? */ } -static void action_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void action_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) { SpaceAction *sact = (SpaceAction *)slink; - if ((ID *)sact->action == old_id) { - sact->action = (bAction *)new_id; - } - - if ((ID *)sact->ads.filter_grp == old_id) { - sact->ads.filter_grp = (Collection *)new_id; - } - if ((ID *)sact->ads.source == old_id) { - sact->ads.source = new_id; - } + BKE_id_remapper_apply(mappings, (ID **)&sact->action, ID_REMAP_APPLY_DEFAULT); + BKE_id_remapper_apply(mappings, (ID **)&sact->ads.filter_grp, ID_REMAP_APPLY_DEFAULT); + BKE_id_remapper_apply(mappings, &sact->ads.source, ID_REMAP_APPLY_DEFAULT); } /** diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index b5f6874fcfc..14cc03e3120 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -40,10 +40,6 @@ set(SRC set(LIB ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) @@ -52,7 +48,7 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_HAIR_NODES) + add_definitions(-DWITH_NEW_CURVES_TYPE) endif() blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index f5107cb13fd..b83396b10d9 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -273,8 +273,8 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) { return true; } -#ifdef WITH_HAIR_NODES - if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (ELEM(type, -1, OB_HAIR))) { +#ifdef WITH_NEW_CURVES_TYPE + if (RNA_struct_is_a(ptr->type, &RNA_Curves) && (ELEM(type, -1, OB_CURVES))) { return true; } #endif @@ -314,7 +314,7 @@ static bool buttons_context_path_modifier(ButsContextPath *path) OB_SURF, OB_LATTICE, OB_GPENCIL, - OB_HAIR, + OB_CURVES, OB_POINTCLOUD, OB_VOLUME)) { ModifierData *md = BKE_object_active_modifier(ob); @@ -845,8 +845,8 @@ const char *buttons_context_dir[] = { "line_style", "collection", "gpencil", -#ifdef WITH_HAIR_NODES - "hair", +#ifdef WITH_NEW_CURVES_TYPE + "curves", #endif #ifdef WITH_POINT_CLOUD "pointcloud", @@ -941,9 +941,9 @@ int /*eContextResult*/ buttons_context(const bContext *C, set_pointer_type(path, result, &RNA_LightProbe); return CTX_RESULT_OK; } -#ifdef WITH_HAIR_NODES - if (CTX_data_equals(member, "hair")) { - set_pointer_type(path, result, &RNA_Hair); +#ifdef WITH_NEW_CURVES_TYPE + if (CTX_data_equals(member, "curves")) { + set_pointer_type(path, result, &RNA_Curves); return CTX_RESULT_OK; } #endif diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 007a9105c76..cf1e7788ff8 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -32,6 +32,7 @@ #include "BKE_context.h" #include "BKE_gpencil_modifier.h" /* Types for registering panels. */ +#include "BKE_lib_remap.h" #include "BKE_modifier.h" #include "BKE_screen.h" #include "BKE_shader_fx.h" @@ -860,54 +861,53 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) } } -static void buttons_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void buttons_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) { SpaceProperties *sbuts = (SpaceProperties *)slink; - if (sbuts->pinid == old_id) { - sbuts->pinid = new_id; - if (new_id == NULL) { - sbuts->flag &= ~SB_PIN_CONTEXT; - } + if (BKE_id_remapper_apply(mappings, &sbuts->pinid, ID_REMAP_APPLY_DEFAULT) == + ID_REMAP_RESULT_SOURCE_UNASSIGNED) { + sbuts->flag &= ~SB_PIN_CONTEXT; } if (sbuts->path) { ButsContextPath *path = sbuts->path; + for (int i = 0; i < path->len; i++) { + switch (BKE_id_remapper_apply(mappings, &path->ptr[i].owner_id, ID_REMAP_APPLY_DEFAULT)) { + case ID_REMAP_RESULT_SOURCE_UNASSIGNED: { + if (i == 0) { + MEM_SAFE_FREE(sbuts->path); + } + else { + memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); + path->len = i; + } + break; + } + case ID_REMAP_RESULT_SOURCE_REMAPPED: { + RNA_id_pointer_create(path->ptr[i].owner_id, &path->ptr[i]); + /* There is no easy way to check/make path downwards valid, just nullify it. + * Next redraw will rebuild this anyway. */ + i++; + memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); + path->len = i; + break; + } - int i; - for (i = 0; i < path->len; i++) { - if (path->ptr[i].owner_id == old_id) { - break; - } - } - - if (i == path->len) { - /* pass */ - } - else if (new_id == NULL) { - if (i == 0) { - MEM_SAFE_FREE(sbuts->path); - } - else { - memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); - path->len = i; + case ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE: + case ID_REMAP_RESULT_SOURCE_UNAVAILABLE: { + /* Nothing to do. */ + break; + } } } - else { - RNA_id_pointer_create(new_id, &path->ptr[i]); - /* There is no easy way to check/make path downwards valid, just nullify it. - * Next redraw will rebuild this anyway. */ - i++; - memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); - path->len = i; - } } if (sbuts->texuser) { ButsContextTexture *ct = sbuts->texuser; - if ((ID *)ct->texture == old_id) { - ct->texture = (Tex *)new_id; - } + BKE_id_remapper_apply(mappings, (ID **)&ct->texture, ID_REMAP_APPLY_DEFAULT); BLI_freelistN(&ct->users); ct->user = NULL; } diff --git a/source/blender/editors/space_clip/CMakeLists.txt b/source/blender/editors/space_clip/CMakeLists.txt index db881dafa6b..8f1a2c3c81e 100644 --- a/source/blender/editors/space_clip/CMakeLists.txt +++ b/source/blender/editors/space_clip/CMakeLists.txt @@ -68,11 +68,6 @@ set(LIB ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - - blender_add_lib(bf_editor_space_clip "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") # Needed so we can use dna_type_offsets.h for defaults initialization. diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index ef522e57d02..b45eabdc7c3 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1082,7 +1082,7 @@ static void change_frame_apply(bContext *C, wmOperator *op) SUBFRA = 0.0f; /* do updates */ - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } @@ -1560,7 +1560,8 @@ static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) clip->proxy.build_size_flag, clip->proxy.quality, true, - NULL); + NULL, + false); } WM_jobs_customdata_set(wm_job, pj, proxy_freejob); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index b6dbda79a2d..da1d2dea653 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -39,6 +39,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" +#include "BKE_lib_remap.h" #include "BKE_movieclip.h" #include "BKE_screen.h" #include "BKE_tracking.h" @@ -1317,23 +1318,18 @@ static void clip_properties_region_listener(const wmRegionListenerParams *params /********************* registration ********************/ -static void clip_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void clip_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) { SpaceClip *sclip = (SpaceClip *)slink; - if (!ELEM(GS(old_id->name), ID_MC, ID_MSK)) { + if (!BKE_id_remapper_has_mapping_for(mappings, FILTER_ID_MC | FILTER_ID_MSK)) { return; } - if ((ID *)sclip->clip == old_id) { - sclip->clip = (MovieClip *)new_id; - id_us_ensure_real(new_id); - } - - if ((ID *)sclip->mask_info.mask == old_id) { - sclip->mask_info.mask = (Mask *)new_id; - id_us_ensure_real(new_id); - } + BKE_id_remapper_apply(mappings, (ID **)&sclip->clip, ID_REMAP_APPLY_ENSURE_REAL); + BKE_id_remapper_apply(mappings, (ID **)&sclip->mask_info.mask, ID_REMAP_APPLY_ENSURE_REAL); } void ED_spacetype_clip(void) diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index ee0406cde30..313887ec8fd 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -1385,7 +1385,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op) if (CFRA != sc->user.framenr) { CFRA = sc->user.framenr; - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt index cbeb2e6f529..1df0c9c4409 100644 --- a/source/blender/editors/space_file/CMakeLists.txt +++ b/source/blender/editors/space_file/CMakeLists.txt @@ -92,10 +92,6 @@ if(WITH_IMAGE_HDR) add_definitions(-DWITH_HDR) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 2d31e8030a4..044d7aba88f 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -524,7 +524,7 @@ static int compare_apply_inverted(int val, const struct FileSortData *sort_data) * 2) If not possible (file names match) and both represent local IDs, sort by ID-type. * 3) If not possible and only one is a local ID, place files representing local IDs first. * - * TODO (not actually implemented, but should be): + * TODO: (not actually implemented, but should be): * 4) If no file represents a local ID, sort by file path, so that files higher up the file system * hierarchy are placed first. */ diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 14f596ae7bf..0a69d0f9b39 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -745,14 +745,17 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) N_("Fonts"), ICON_FILE_FONT, FS_INSERT_LAST); + fsmenu_add_windows_folder(fsmenu, + FS_CATEGORY_SYSTEM_BOOKMARKS, + &FOLDERID_SkyDrive, + N_("OneDrive"), + ICON_URL, + FS_INSERT_LAST); /* These items are just put in path cache for thumbnail views and if bookmarked. */ fsmenu_add_windows_folder( fsmenu, FS_CATEGORY_OTHER, &FOLDERID_UserProfiles, NULL, ICON_COMMUNITY, FS_INSERT_LAST); - - fsmenu_add_windows_folder( - fsmenu, FS_CATEGORY_OTHER, &FOLDERID_SkyDrive, NULL, ICON_URL, FS_INSERT_LAST); } } #elif defined(__APPLE__) diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 97a5f173ffd..470128f61bd 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -33,6 +33,7 @@ #include "BKE_appdir.h" #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_screen.h" @@ -989,7 +990,7 @@ static int /*eContextResult*/ file_context(const bContext *C, return CTX_RESULT_MEMBER_NOT_FOUND; } -static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *UNUSED(new_id)) +static void file_id_remap(ScrArea *area, SpaceLink *sl, const struct IDRemapper *UNUSED(mappings)) { SpaceFile *sfile = (SpaceFile *)sl; diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt index 2a795dd954c..2e6e6971ce9 100644 --- a/source/blender/editors/space_graph/CMakeLists.txt +++ b/source/blender/editors/space_graph/CMakeLists.txt @@ -61,9 +61,5 @@ if(WITH_AUDASPACE) add_definitions(-DWITH_AUDASPACE) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_space_graph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index ed5993c77a7..3a2593cc543 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -437,7 +437,6 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu) for (sel = 0; sel < 2; sel++) { BezTriple *bezt = fcu->bezt, *prevbezt = NULL; int basecol = (sel) ? TH_HANDLE_SEL_FREE : TH_HANDLE_FREE; - const float *fp; uchar col[4]; for (b = 0; b < fcu->totvert; b++, prevbezt = bezt, bezt++) { @@ -452,17 +451,15 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu) /* draw handle with appropriate set of colors if selection is ok */ if ((bezt->f2 & SELECT) == sel) { - fp = bezt->vec[0]; - /* only draw first handle if previous segment had handles */ if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) { UI_GetThemeColor3ubv(basecol + bezt->h1, col); col[3] = fcurve_display_alpha(fcu) * 255; immAttr4ubv(color, col); - immVertex2fv(pos, fp); + immVertex2fv(pos, bezt->vec[0]); immAttr4ubv(color, col); - immVertex2fv(pos, fp + 3); + immVertex2fv(pos, bezt->vec[1]); } /* only draw second handle if this segment is bezier */ @@ -470,33 +467,31 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu) UI_GetThemeColor3ubv(basecol + bezt->h2, col); col[3] = fcurve_display_alpha(fcu) * 255; immAttr4ubv(color, col); - immVertex2fv(pos, fp + 3); + immVertex2fv(pos, bezt->vec[1]); immAttr4ubv(color, col); - immVertex2fv(pos, fp + 6); + immVertex2fv(pos, bezt->vec[2]); } } else { /* only draw first handle if previous segment was had handles, and selection is ok */ if (((bezt->f1 & SELECT) == sel) && ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)))) { - fp = bezt->vec[0]; UI_GetThemeColor3ubv(basecol + bezt->h1, col); col[3] = fcurve_display_alpha(fcu) * 255; immAttr4ubv(color, col); - immVertex2fv(pos, fp); + immVertex2fv(pos, bezt->vec[0]); immAttr4ubv(color, col); - immVertex2fv(pos, fp + 3); + immVertex2fv(pos, bezt->vec[1]); } /* only draw second handle if this segment is bezier, and selection is ok */ if (((bezt->f3 & SELECT) == sel) && (bezt->ipo == BEZT_IPO_BEZ)) { - fp = bezt->vec[1]; UI_GetThemeColor3ubv(basecol + bezt->h2, col); col[3] = fcurve_display_alpha(fcu) * 255; immAttr4ubv(color, col); - immVertex2fv(pos, fp); + immVertex2fv(pos, bezt->vec[0]); immAttr4ubv(color, col); - immVertex2fv(pos, fp + 3); + immVertex2fv(pos, bezt->vec[1]); } } } diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 2afee277847..63de7fb570e 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -586,7 +586,7 @@ static char *graphkeys_paste_description(bContext *UNUSED(C), { /* Custom description if the 'flipped' option is used. */ if (RNA_boolean_get(ptr, "flipped")) { - return BLI_strdup("Paste keyframes from mirrored bones if they exist"); + return BLI_strdup(TIP_("Paste keyframes from mirrored bones if they exist")); } /* Use the default description in the other cases. */ @@ -2353,6 +2353,103 @@ void GRAPH_OT_snap(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Equalize Handles Operator + * \{ */ + +/* Defines for equalize handles tool. */ +static const EnumPropertyItem prop_graphkeys_equalize_handles_sides[] = { + {GRAPHKEYS_EQUALIZE_LEFT, "LEFT", 0, "Left", "Equalize selected keyframes' left handles"}, + {GRAPHKEYS_EQUALIZE_RIGHT, "RIGHT", 0, "Right", "Equalize selected keyframes' right handles"}, + {GRAPHKEYS_EQUALIZE_BOTH, "BOTH", 0, "Both", "Equalize both of a keyframe's handles"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* ------------------- */ + +/* Equalize selected keyframes' bezier handles. */ +static void equalize_graph_keys(bAnimContext *ac, int mode, float handle_length, bool flatten) +{ + /* Filter data. */ + const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_NODUPLIS); + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* Equalize keyframes. */ + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + ANIM_fcurve_equalize_keyframes_loop(ale->key_data, mode, handle_length, flatten); + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); +} + +static int graphkeys_equalize_handles_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + /* Get editor data. */ + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + /* Get equalize mode. */ + int mode = RNA_enum_get(op->ptr, "side"); + float handle_length = RNA_float_get(op->ptr, "handle_length"); + bool flatten = RNA_boolean_get(op->ptr, "flatten"); + + /* Equalize graph keyframes. */ + equalize_graph_keys(&ac, mode, handle_length, flatten); + + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GRAPH_OT_equalize_handles(wmOperatorType *ot) +{ + /* Identifiers */ + ot->name = "Equalize Handles"; + ot->idname = "GRAPH_OT_equalize_handles"; + ot->description = + "Ensure selected keyframes' handles have equal length, optionally making them horizontal"; + + /* API callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = graphkeys_equalize_handles_exec; + ot->poll = graphop_editable_keyframes_poll; + + /* Flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties */ + ot->prop = RNA_def_enum(ot->srna, + "side", + prop_graphkeys_equalize_handles_sides, + 0, + "Side", + "Side of the keyframes' bezier handles to affect"); + RNA_def_float(ot->srna, + "handle_length", + 5.0f, + 0.1f, + FLT_MAX, + "Handle Length", + "Length to make selected keyframes' bezier handles", + 1.0f, + 50.0f); + RNA_def_boolean( + ot->srna, + "flatten", + false, + "Flatten", + "Make the values of the selected keyframes' handles the same as their respective keyframes"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Mirror Keyframes Operator * \{ */ diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index 243a4ee4b95..a59fb63dd22 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -144,6 +144,7 @@ void GRAPH_OT_easing_type(struct wmOperatorType *ot); void GRAPH_OT_frame_jump(struct wmOperatorType *ot); void GRAPH_OT_snap_cursor_value(struct wmOperatorType *ot); void GRAPH_OT_snap(struct wmOperatorType *ot); +void GRAPH_OT_equalize_handles(struct wmOperatorType *ot); void GRAPH_OT_mirror(struct wmOperatorType *ot); /* defines for snap keyframes @@ -158,6 +159,15 @@ enum eGraphKeys_Snap_Mode { GRAPHKEYS_SNAP_VALUE, }; +/* Defines for equalize keyframe handles. + * NOTE: Keep in sync with eEditKeyframes_Equalize (in ED_keyframes_edit.h). + */ +enum eGraphKeys_Equalize_Mode { + GRAPHKEYS_EQUALIZE_LEFT = 1, + GRAPHKEYS_EQUALIZE_RIGHT, + GRAPHKEYS_EQUALIZE_BOTH, +}; + /* defines for mirror keyframes * NOTE: keep in sync with eEditKeyframes_Mirror (in ED_keyframes_edit.h) */ diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index fe4cffcb3b8..0cebc6eb586 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -103,7 +103,7 @@ static void graphview_cursor_apply(bContext *C, wmOperator *op) } SUBFRA = 0.0f; - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); } /* set the cursor value */ @@ -456,6 +456,7 @@ void graphedit_operatortypes(void) /* editing */ WM_operatortype_append(GRAPH_OT_snap); + WM_operatortype_append(GRAPH_OT_equalize_handles); WM_operatortype_append(GRAPH_OT_mirror); WM_operatortype_append(GRAPH_OT_frame_jump); WM_operatortype_append(GRAPH_OT_snap_cursor_value); diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 4b8c983a761..cfaea33605a 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -483,7 +483,7 @@ static char *decimate_desc(bContext *UNUSED(C), wmOperatorType *UNUSED(op), Poin if (RNA_enum_get(ptr, "mode") == DECIM_ERROR) { return BLI_strdup( - "Decimate F-Curves by specifying how much it can deviate from the original curve"); + TIP_("Decimate F-Curves by specifying how much they can deviate from the original curve")); } /* Use default description. */ diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 40c95d4f382..7d5e8836490 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -36,6 +36,7 @@ #include "BKE_context.h" #include "BKE_fcurve.h" +#include "BKE_lib_remap.h" #include "BKE_screen.h" #include "ED_anim_api.h" @@ -796,18 +797,17 @@ static void graph_refresh(const bContext *C, ScrArea *area) graph_refresh_fcurve_colors(C); } -static void graph_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void graph_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) { SpaceGraph *sgraph = (SpaceGraph *)slink; - - if (sgraph->ads) { - if ((ID *)sgraph->ads->filter_grp == old_id) { - sgraph->ads->filter_grp = (Collection *)new_id; - } - if ((ID *)sgraph->ads->source == old_id) { - sgraph->ads->source = new_id; - } + if (!sgraph->ads) { + return; } + + BKE_id_remapper_apply(mappings, (ID **)&sgraph->ads->filter_grp, ID_REMAP_APPLY_DEFAULT); + BKE_id_remapper_apply(mappings, (ID **)&sgraph->ads->source, ID_REMAP_APPLY_DEFAULT); } static int graph_space_subtype_get(ScrArea *area) diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index 7a1bab0ef14..25d0e02ecc0 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -53,10 +53,6 @@ set(LIB bf_editor_uvedit ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_OPENIMAGEIO) add_definitions(-DWITH_OPENIMAGEIO) endif() diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 23d07c9b45b..b1ca8505b8a 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3605,7 +3605,7 @@ static void change_frame_apply(bContext *C, wmOperator *op) SUBFRA = 0.0f; /* do updates */ - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index e81f3b6a490..73993606a14 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -1000,7 +1000,7 @@ void ED_image_undosys_type(UndoType *ut) ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref; - /* NOTE this is actually a confusing case, since it expects a valid context, but only in a + /* NOTE: this is actually a confusing case, since it expects a valid context, but only in a * specific case, see `image_undosys_step_encode` code. We cannot specify * `UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE` though, as it can be called with a NULL context by * current code. */ diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 52cb36b1b8f..eb5b6104a79 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -37,6 +37,7 @@ #include "BKE_context.h" #include "BKE_image.h" #include "BKE_lib_id.h" +#include "BKE_lib_remap.h" #include "BKE_screen.h" #include "RNA_access.h" @@ -983,29 +984,19 @@ static void image_header_region_listener(const wmRegionListenerParams *params) } } -static void image_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void image_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) { SpaceImage *simg = (SpaceImage *)slink; - if (!ELEM(GS(old_id->name), ID_IM, ID_GD, ID_MSK)) { + if (!BKE_id_remapper_has_mapping_for(mappings, FILTER_ID_IM | FILTER_ID_GD | FILTER_ID_MSK)) { return; } - if ((ID *)simg->image == old_id) { - simg->image = (Image *)new_id; - id_us_ensure_real(new_id); - } - - if ((ID *)simg->gpd == old_id) { - simg->gpd = (bGPdata *)new_id; - id_us_min(old_id); - id_us_plus(new_id); - } - - if ((ID *)simg->mask_info.mask == old_id) { - simg->mask_info.mask = (Mask *)new_id; - id_us_ensure_real(new_id); - } + BKE_id_remapper_apply(mappings, (ID **)&simg->image, ID_REMAP_APPLY_ENSURE_REAL); + BKE_id_remapper_apply(mappings, (ID **)&simg->gpd, ID_REMAP_APPLY_UPDATE_REFCOUNT); + BKE_id_remapper_apply(mappings, (ID **)&simg->mask_info.mask, ID_REMAP_APPLY_ENSURE_REAL); } /** diff --git a/source/blender/editors/space_info/CMakeLists.txt b/source/blender/editors/space_info/CMakeLists.txt index 144b21fb9b8..fb17a6c9709 100644 --- a/source/blender/editors/space_info/CMakeLists.txt +++ b/source/blender/editors/space_info/CMakeLists.txt @@ -48,9 +48,5 @@ set(SRC set(LIB ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_space_info "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index 005ae0214cd..a1eacc2bc3a 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -219,7 +219,7 @@ static void stats_object(Object *ob, } break; } - case OB_HAIR: + case OB_CURVES: case OB_POINTCLOUD: case OB_VOLUME: { break; diff --git a/source/blender/editors/space_nla/CMakeLists.txt b/source/blender/editors/space_nla/CMakeLists.txt index 9a94d28c604..5326f1cce2b 100644 --- a/source/blender/editors/space_nla/CMakeLists.txt +++ b/source/blender/editors/space_nla/CMakeLists.txt @@ -47,9 +47,5 @@ set(LIB bf_blenlib ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_space_nla "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 0771153c5f5..962b5151661 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -33,6 +33,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_lib_remap.h" #include "BKE_screen.h" #include "ED_anim_api.h" @@ -577,18 +578,17 @@ static void nla_listener(const wmSpaceTypeListenerParams *params) } } -static void nla_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void nla_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) { SpaceNla *snla = (SpaceNla *)slink; - if (snla->ads) { - if ((ID *)snla->ads->filter_grp == old_id) { - snla->ads->filter_grp = (Collection *)new_id; - } - if ((ID *)snla->ads->source == old_id) { - snla->ads->source = new_id; - } + if (snla->ads == NULL) { + return; } + BKE_id_remapper_apply(mappings, (ID **)&snla->ads->filter_grp, ID_REMAP_APPLY_DEFAULT); + BKE_id_remapper_apply(mappings, (ID **)&snla->ads->source, ID_REMAP_APPLY_DEFAULT); } void ED_spacetype_nla(void) diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 41d6388c947..15e0d04c8fa 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -63,10 +63,6 @@ set(LIB bf_editor_screen ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_COMPOSITOR) add_definitions(-DWITH_COMPOSITOR) endif() diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 82da890fa9b..81c63e9bddb 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -626,7 +626,9 @@ static void node_update_hidden(bNode &node, uiBlock &block) static int node_get_colorid(const bNode &node) { - switch (node.typeinfo->nclass) { + const int nclass = (node.typeinfo->ui_class == nullptr) ? node.typeinfo->nclass : + node.typeinfo->ui_class(&node); + switch (nclass) { case NODE_CLASS_INPUT: return TH_NODE_INPUT; case NODE_CLASS_OUTPUT: @@ -2028,7 +2030,7 @@ static void node_draw_basis(const bContext &C, } UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_4fv(&rect, false, BASIS_RAD, color_outline); + UI_draw_roundbox_4fv(&rect, false, BASIS_RAD + outline_width, color_outline); } float scale; diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 6275e7e4656..a513f5df7a0 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -394,7 +394,7 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) { WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); - if (ntree->type == NTREE_SHADER) { + if (ntree->type == NTREE_SHADER && id != nullptr) { if (GS(id->name) == ID_MA) { WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id); } diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index 73e419d667a..3d3f8378916 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -776,6 +776,18 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, ListBase anim_basepaths = {nullptr, nullptr}; + /* Detach unselected nodes inside frames when the frame is put into the group. Otherwise the + * `parent` pointer becomes dangling. */ + LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { + if (node->parent == nullptr) { + continue; + } + if (node_group_make_use_node(*node->parent, gnode) && + !node_group_make_use_node(*node, gnode)) { + nodeDetachNode(node); + } + } + /* move nodes over */ LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree.nodes) { if (node_group_make_use_node(*node, gnode)) { diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index c161fc70402..f6397baf9f2 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -109,18 +109,28 @@ enum NodeResizeDirection { }; ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT); +/* Nodes draw without dpi - the view zoom is flexible. */ +#define HIDDEN_RAD (0.75f * U.widget_unit) +#define BASIS_RAD (0.2f * U.widget_unit) +#define NODE_DYS (U.widget_unit / 2) +#define NODE_DY U.widget_unit +#define NODE_SOCKDY (0.1f * U.widget_unit) +#define NODE_WIDTH(node) (node.width * UI_DPI_FAC) +#define NODE_HEIGHT(node) (node.height * UI_DPI_FAC) +#define NODE_MARGIN_X (1.2f * U.widget_unit) +#define NODE_SOCKSIZE (0.25f * U.widget_unit) +#define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit) +#define NODE_RESIZE_MARGIN (0.20f * U.widget_unit) +#define NODE_LINK_RESOL 12 + +/* space_node.cc */ + /** * Transform between View2Ds in the tree path. */ float2 space_node_group_offset(const SpaceNode &snode); -float node_socket_calculate_height(const bNodeSocket &socket); -float2 node_link_calculate_multi_input_position(const float2 &socket_position, - int index, - int total_inputs); - int node_get_resize_cursor(NodeResizeDirection directions); -NodeResizeDirection node_get_resize_direction(const bNode *node, int x, int y); /** * Usual convention here would be #node_socket_get_color(), * but that's already used (for setting a color property socket). @@ -130,6 +140,9 @@ void node_socket_color_get(const bContext &C, PointerRNA &node_ptr, const bNodeSocket &sock, float r_color[4]); + +/* node_draw.cc */ + void node_draw_space(const bContext &C, ARegion ®ion); /** @@ -144,9 +157,13 @@ float2 node_to_view(const bNode &node, const float2 &co); void node_to_updated_rect(const bNode &node, rctf &r_rect); float2 node_from_view(const bNode &node, const float2 &co); +/* node_ops.cc */ + void node_operatortypes(); void node_keymap(wmKeyConfig *keyconf); +/* node_select.cc */ + void node_deselect_all(SpaceNode &snode); void node_socket_select(bNode *node, bNodeSocket &sock); void node_socket_deselect(bNode *node, bNodeSocket &sock, bool deselect_node); @@ -165,6 +182,8 @@ void NODE_OT_select_grouped(wmOperatorType *ot); void NODE_OT_select_same_type_step(wmOperatorType *ot); void NODE_OT_find_node(wmOperatorType *ot); +/* node_view.cc */ + bool space_node_view_flag( bContext &C, SpaceNode &snode, ARegion ®ion, int node_flag, int smooth_viewtx); @@ -177,6 +196,10 @@ void NODE_OT_backimage_zoom(wmOperatorType *ot); void NODE_OT_backimage_fit(wmOperatorType *ot); void NODE_OT_backimage_sample(wmOperatorType *ot); +/* drawnode.cc */ + +NodeResizeDirection node_get_resize_direction(const bNode *node, int x, int y); + void nodelink_batch_start(SpaceNode &snode); void nodelink_batch_end(SpaceNode &snode); @@ -215,7 +238,7 @@ void draw_nodespace_back_pix(const bContext &C, SpaceNode &snode, bNodeInstanceKey parent_key); -void node_select_all(ListBase *lb, int action); +/* node_add.cc */ /** * XXX Does some additional initialization on top of #nodeAddNode @@ -232,6 +255,8 @@ void NODE_OT_add_file(wmOperatorType *ot); void NODE_OT_add_mask(wmOperatorType *ot); void NODE_OT_new_node_tree(wmOperatorType *ot); +/* node_group.cc */ + const char *node_group_idname(bContext *C); void NODE_OT_group_make(wmOperatorType *ot); void NODE_OT_group_insert(wmOperatorType *ot); @@ -239,6 +264,8 @@ void NODE_OT_group_ungroup(wmOperatorType *ot); void NODE_OT_group_separate(wmOperatorType *ot); void NODE_OT_group_edit(wmOperatorType *ot); +/* node_relationships.cc */ + void sort_multi_input_socket_links(SpaceNode &snode, bNode &node, bNodeLink *drag_link, @@ -259,6 +286,16 @@ void NODE_OT_link_viewer(wmOperatorType *ot); void NODE_OT_insert_offset(wmOperatorType *ot); +/* node_edit.cc */ + +float2 node_link_calculate_multi_input_position(const float2 &socket_position, + int index, + int total_inputs); + +void node_select_all(ListBase *lb, int action); + +float node_socket_calculate_height(const bNodeSocket &socket); + void snode_set_context(const bContext &C); bool composite_node_active(bContext *C); @@ -314,13 +351,17 @@ void NODE_OT_shader_script_update(wmOperatorType *ot); void NODE_OT_viewer_border(wmOperatorType *ot); void NODE_OT_clear_viewer_border(wmOperatorType *ot); +void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot); +void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot); + +/* node_gizmo.cc */ + void NODE_GGT_backdrop_transform(wmGizmoGroupType *gzgt); void NODE_GGT_backdrop_crop(wmGizmoGroupType *gzgt); void NODE_GGT_backdrop_sun_beams(wmGizmoGroupType *gzgt); void NODE_GGT_backdrop_corner_pin(wmGizmoGroupType *gzgt); -void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot); -void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot); +/* node_geometry_attribute_search.cc */ void node_geometry_add_attribute_search_button(const bContext &C, const bNodeTree &node_tree, @@ -328,22 +369,12 @@ void node_geometry_add_attribute_search_button(const bContext &C, PointerRNA &socket_ptr, uiLayout &layout); -/* Nodes draw without dpi - the view zoom is flexible. */ -#define HIDDEN_RAD (0.75f * U.widget_unit) -#define BASIS_RAD (0.2f * U.widget_unit) -#define NODE_DYS (U.widget_unit / 2) -#define NODE_DY U.widget_unit -#define NODE_SOCKDY (0.1f * U.widget_unit) -#define NODE_WIDTH(node) (node.width * UI_DPI_FAC) -#define NODE_HEIGHT(node) (node.height * UI_DPI_FAC) -#define NODE_MARGIN_X (1.2f * U.widget_unit) -#define NODE_SOCKSIZE (0.25f * U.widget_unit) -#define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit) -#define NODE_RESIZE_MARGIN (0.20f * U.widget_unit) -#define NODE_LINK_RESOL 12 +/* node_context_path.c */ Vector<ui::ContextPathItem> context_path_for_space_node(const bContext &C); +/* link_drag_search.cc */ + void invoke_node_link_drag_add_menu(bContext &C, bNode &node, bNodeSocket &socket, diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index fd9420b173d..bc79925b51d 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -365,10 +365,7 @@ void sort_multi_input_socket_links(SpaceNode &snode, } } -static void snode_autoconnect(Main &bmain, - SpaceNode &snode, - const bool allow_multiple, - const bool replace) +static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace) { bNodeTree *ntree = snode.edittree; Vector<bNode *> sorted_nodes; @@ -441,10 +438,6 @@ static void snode_autoconnect(Main &bmain, } } } - - if (numlinks > 0) { - BKE_ntree_update_main_tree(&bmain, ntree, nullptr); - } } /** \} */ @@ -1304,13 +1297,13 @@ static int node_make_link_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); - snode_autoconnect(bmain, snode, true, replace); + snode_autoconnect(snode, true, replace); /* deselect sockets after linking */ node_deselect_all_input_sockets(snode, false); node_deselect_all_output_sockets(snode, false); - ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); + ED_node_tree_propagate_change(C, &bmain, snode.edittree); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index f794a8ce294..4d0c88a5343 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -31,6 +31,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" +#include "BKE_lib_remap.h" #include "BKE_node.h" #include "BKE_screen.h" @@ -896,9 +897,9 @@ static void node_widgets() WM_gizmogrouptype_append_and_link(gzmap_type, NODE_GGT_backdrop_corner_pin); } -static void node_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void node_id_remap_cb(ID *old_id, ID *new_id, void *user_data) { - SpaceNode *snode = (SpaceNode *)slink; + SpaceNode *snode = static_cast<SpaceNode *>(user_data); if (snode->id == old_id) { /* nasty DNA logic for SpaceNode: @@ -964,6 +965,24 @@ static void node_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, I } } +static void node_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) +{ + /* Although we should be able to perform all the mappings in a single go this lead to issues when + * running the python test cases. Somehow the nodetree/edittree weren't updated to the new + * pointers that generated a SEGFAULT. + * + * To move forward we should perhaps remove snode->edittree and snode->nodetree as they are just + * copies of pointers. All usages should be calling a function that will receive the appropriate + * instance. + * + * We could also move a remap address at a time to use the IDRemapper as that should get closer + * to cleaner code. See {D13615} for more information about this topic. + */ + BKE_id_remapper_iter(mappings, node_id_remap_cb, slink); +} + static int node_space_subtype_get(ScrArea *area) { SpaceNode *snode = (SpaceNode *)area->spacedata.first; diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index cac9d4131f8..d97f48bcb68 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -65,7 +65,9 @@ set(SRC tree/tree_element_id_scene.cc tree/tree_element_nla.cc tree/tree_element_overrides.cc + tree/tree_element_rna.cc tree/tree_element_scene_objects.cc + tree/tree_element_seq.cc tree/tree_element_view_layer.cc outliner_intern.hh @@ -81,7 +83,9 @@ set(SRC tree/tree_element_id_scene.hh tree/tree_element_nla.hh tree/tree_element_overrides.hh + tree/tree_element_rna.hh tree/tree_element_scene_objects.hh + tree/tree_element_seq.hh tree/tree_element_view_layer.hh ) @@ -91,9 +95,5 @@ set(LIB bf_editor_undo ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_space_outliner "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 6de8d9539a9..13c273d1ec9 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -81,6 +81,7 @@ #include "outliner_intern.hh" #include "tree/tree_display.hh" #include "tree/tree_element.hh" +#include "tree/tree_element_rna.hh" using namespace blender::ed::outliner; @@ -1909,20 +1910,20 @@ static void outliner_draw_rnacols(ARegion *region, int sizex) static void outliner_draw_rnabuts( uiBlock *block, ARegion *region, SpaceOutliner *space_outliner, int sizex, ListBase *lb) { - PointerRNA *ptr; + PointerRNA ptr; PropertyRNA *prop; LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); if (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin && te->ys <= region->v2d.cur.ymax) { - if (tselem->type == TSE_RNA_PROPERTY) { - ptr = &te->rnaptr; - prop = reinterpret_cast<PropertyRNA *>(te->directdata); + if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) { + ptr = te_rna_prop->getPointerRNA(); + prop = te_rna_prop->getPropertyRNA(); if (!TSELEM_OPEN(tselem, space_outliner)) { if (RNA_property_type(prop) == PROP_POINTER) { uiBut *but = uiDefAutoButR(block, - ptr, + &ptr, prop, -1, "", @@ -1935,7 +1936,7 @@ static void outliner_draw_rnabuts( } else if (RNA_property_type(prop) == PROP_ENUM) { uiDefAutoButR(block, - ptr, + &ptr, prop, -1, nullptr, @@ -1947,7 +1948,7 @@ static void outliner_draw_rnabuts( } else { uiDefAutoButR(block, - ptr, + &ptr, prop, -1, "", @@ -1959,12 +1960,13 @@ static void outliner_draw_rnabuts( } } } - else if (tselem->type == TSE_RNA_ARRAY_ELEM) { - ptr = &te->rnaptr; - prop = reinterpret_cast<PropertyRNA *>(te->directdata); + else if (TreeElementRNAArrayElement *te_rna_array_elem = + tree_element_cast<TreeElementRNAArrayElement>(te)) { + ptr = te_rna_array_elem->getPointerRNA(); + prop = te_rna_array_elem->getPropertyRNA(); uiDefAutoButR(block, - ptr, + &ptr, prop, te->index, "", @@ -2478,9 +2480,6 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case TSE_POSE_CHANNEL: data.icon = ICON_BONE_DATA; break; - case TSE_PROXY: - data.icon = ICON_GHOST_ENABLED; - break; case TSE_R_LAYER_BASE: data.icon = ICON_RENDERLAYERS; break; @@ -2554,15 +2553,19 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case TSE_SEQUENCE_DUP: data.icon = ICON_SEQ_STRIP_DUPLICATE; break; - case TSE_RNA_STRUCT: - if (RNA_struct_is_ID(te->rnaptr.type)) { - data.drag_id = (ID *)te->rnaptr.data; - data.icon = RNA_struct_ui_icon(te->rnaptr.type); + case TSE_RNA_STRUCT: { + const TreeElementRNAStruct *te_rna_struct = tree_element_cast<TreeElementRNAStruct>(te); + const PointerRNA &ptr = te_rna_struct->getPointerRNA(); + + if (RNA_struct_is_ID(ptr.type)) { + data.drag_id = reinterpret_cast<ID *>(ptr.data); + data.icon = RNA_struct_ui_icon(ptr.type); } else { - data.icon = RNA_struct_ui_icon(te->rnaptr.type); + data.icon = RNA_struct_ui_icon(ptr.type); } break; + } case TSE_LAYER_COLLECTION: case TSE_SCENE_COLLECTION_BASE: case TSE_VIEW_COLLECTION_BASE: { @@ -2629,8 +2632,8 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case OB_LIGHTPROBE: data.icon = ICON_OUTLINER_OB_LIGHTPROBE; break; - case OB_HAIR: - data.icon = ICON_OUTLINER_OB_HAIR; + case OB_CURVES: + data.icon = ICON_OUTLINER_OB_CURVES; break; case OB_POINTCLOUD: data.icon = ICON_OUTLINER_OB_POINTCLOUD; @@ -2743,8 +2746,8 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case ID_GR: data.icon = ICON_OUTLINER_COLLECTION; break; - case ID_HA: - data.icon = ICON_OUTLINER_DATA_HAIR; + case ID_CV: + data.icon = ICON_OUTLINER_DATA_CURVES; break; case ID_PT: data.icon = ICON_OUTLINER_DATA_POINTCLOUD; @@ -3319,8 +3322,9 @@ static void outliner_draw_tree_element(bContext *C, offsx += 2 * ufac; } + const TreeElementRNAStruct *te_rna_struct = tree_element_cast<TreeElementRNAStruct>(te); if (ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION) || - ((tselem->type == TSE_RNA_STRUCT) && RNA_struct_is_ID(te->rnaptr.type))) { + (te_rna_struct && RNA_struct_is_ID(te_rna_struct->getPointerRNA().type))) { const BIFIconID lib_icon = (BIFIconID)UI_icon_from_library(tselem->id); if (lib_icon != ICON_NONE) { UI_icon_draw_alpha( diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index a10dbc94b34..b41b260b14a 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -74,6 +74,9 @@ #include "GPU_material.h" #include "outliner_intern.hh" +#include "tree/tree_element_rna.hh" + +using namespace blender::ed::outliner; static void outliner_show_active(SpaceOutliner *space_outliner, ARegion *region, @@ -1714,11 +1717,6 @@ static void tree_element_to_path(TreeElement *te, short *UNUSED(groupmode)) { ListBase hierarchy = {nullptr, nullptr}; - LinkData *ld; - TreeElement *tem, *temnext; - TreeStoreElem *tse /* , *tsenext */ /* UNUSED */; - PointerRNA *ptr, *nextptr; - PropertyRNA *prop; char *newpath = nullptr; /* optimize tricks: @@ -1738,20 +1736,19 @@ static void tree_element_to_path(TreeElement *te, */ /* step 1: flatten out hierarchy of parents into a flat chain */ - for (tem = te->parent; tem; tem = tem->parent) { - ld = MEM_cnew<LinkData>("LinkData for tree_element_to_path()"); + for (TreeElement *tem = te->parent; tem; tem = tem->parent) { + LinkData *ld = MEM_cnew<LinkData>("LinkData for tree_element_to_path()"); ld->data = tem; BLI_addhead(&hierarchy, ld); } /* step 2: step down hierarchy building the path * (NOTE: addhead in previous loop was needed so that we can loop like this) */ - for (ld = reinterpret_cast<LinkData *>(hierarchy.first); ld; ld = ld->next) { + LISTBASE_FOREACH (LinkData *, ld, &hierarchy) { /* get data */ - tem = (TreeElement *)ld->data; - tse = TREESTORE(tem); - ptr = &tem->rnaptr; - prop = reinterpret_cast<PropertyRNA *>(tem->directdata); + TreeElement *tem = (TreeElement *)ld->data; + TreeElementRNACommon *tem_rna = tree_element_cast<TreeElementRNACommon>(tem); + PointerRNA ptr = tem_rna->getPointerRNA(); /* check if we're looking for first ID, or appending to path */ if (*id) { @@ -1759,19 +1756,19 @@ static void tree_element_to_path(TreeElement *te, * - to prevent memory leaks, we must write to newpath not path, * then free old path + swap them. */ - if (tse->type == TSE_RNA_PROPERTY) { + if (TreeElementRNAProperty *tem_rna_prop = tree_element_cast<TreeElementRNAProperty>(tem)) { + PropertyRNA *prop = tem_rna_prop->getPropertyRNA(); + if (RNA_property_type(prop) == PROP_POINTER) { /* for pointer we just append property name */ - newpath = RNA_path_append(*path, ptr, prop, 0, nullptr); + newpath = RNA_path_append(*path, &ptr, prop, 0, nullptr); } else if (RNA_property_type(prop) == PROP_COLLECTION) { char buf[128], *name; - temnext = (TreeElement *)(ld->next->data); - // tsenext = TREESTORE(temnext); /* UNUSED */ - - nextptr = &temnext->rnaptr; - name = RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf), nullptr); + TreeElement *temnext = (TreeElement *)(ld->next->data); + PointerRNA nextptr = tree_element_cast<TreeElementRNACommon>(temnext)->getPointerRNA(); + name = RNA_struct_name_get_alloc(&nextptr, buf, sizeof(buf), nullptr); if (name) { /* if possible, use name as a key in the path */ @@ -1789,6 +1786,7 @@ static void tree_element_to_path(TreeElement *te, if (temsub == temnext) { break; } + index++; } newpath = RNA_path_append(*path, nullptr, prop, index, nullptr); } @@ -1808,11 +1806,11 @@ static void tree_element_to_path(TreeElement *te, else { /* no ID, so check if entry is RNA-struct, * and if that RNA-struct is an ID datablock to extract info from. */ - if (tse->type == TSE_RNA_STRUCT) { + if (tree_element_cast<TreeElementRNAStruct>(tem)) { /* ptr->data not ptr->owner_id seems to be the one we want, * since ptr->data is sometimes the owner of this ID? */ - if (RNA_struct_is_ID(ptr->type)) { - *id = reinterpret_cast<ID *>(ptr->data); + if (RNA_struct_is_ID(ptr.type)) { + *id = reinterpret_cast<ID *>(ptr.data); /* clear path */ if (*path) { @@ -1827,8 +1825,7 @@ static void tree_element_to_path(TreeElement *te, /* step 3: if we've got an ID, add the current item to the path */ if (*id) { /* add the active property to the path */ - ptr = &te->rnaptr; - prop = reinterpret_cast<PropertyRNA *>(te->directdata); + PropertyRNA *prop = tree_element_cast<TreeElementRNACommon>(te)->getPropertyRNA(); /* array checks */ if (tselem->type == TSE_RNA_ARRAY_ELEM) { @@ -1886,9 +1883,12 @@ static void do_outliner_drivers_editop(SpaceOutliner *space_outliner, short flag = 0; short groupmode = KSP_GROUP_KSNAME; + TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); + PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL; + PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr; + /* check if RNA-property described by this selected element is an animatable prop */ - if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && - RNA_property_animateable(&te->rnaptr, reinterpret_cast<PropertyRNA *>(te->directdata))) { + if (prop && RNA_property_animateable(&ptr, prop)) { /* get id + path + index info from the selected element */ tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); } @@ -1901,8 +1901,7 @@ static void do_outliner_drivers_editop(SpaceOutliner *space_outliner, /* array checks */ if (flag & KSP_FLAG_WHOLE_ARRAY) { /* entire array was selected, so add drivers for all */ - arraylen = RNA_property_array_length(&te->rnaptr, - reinterpret_cast<PropertyRNA *>(te->directdata)); + arraylen = RNA_property_array_length(&ptr, prop); } else { arraylen = array_index; @@ -2084,8 +2083,10 @@ static void do_outliner_keyingset_editop(SpaceOutliner *space_outliner, short groupmode = KSP_GROUP_KSNAME; /* check if RNA-property described by this selected element is an animatable prop */ - if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && - RNA_property_animateable(&te->rnaptr, reinterpret_cast<PropertyRNA *>(te->directdata))) { + const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); + PointerRNA ptr = te_rna->getPointerRNA(); + if (te_rna && te_rna->getPropertyRNA() && + RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) { /* get id + path + index info from the selected element */ tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); } diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index 9065f7f9cc1..efbd8a32716 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -27,6 +27,9 @@ #include "RNA_types.h" +/* Needed for `tree_element_cast()`. */ +#include "tree/tree_element.hh" + #ifdef __cplusplus extern "C" { #endif @@ -54,10 +57,12 @@ class AbstractTreeDisplay; class AbstractTreeElement; } // namespace blender::ed::outliner +namespace outliner = blender::ed::outliner; + struct SpaceOutliner_Runtime { /** Object to create and manage the tree for a specific display type (View Layers, Scenes, * Blender File, etc.). */ - std::unique_ptr<blender::ed::outliner::AbstractTreeDisplay> tree_display; + std::unique_ptr<outliner::AbstractTreeDisplay> tree_display; /** Pointers to tree-store elements, grouped by `(id, type, nr)` * in hash-table for faster searching. */ @@ -90,11 +95,12 @@ typedef struct TreeElement { struct TreeElement *next, *prev, *parent; /** - * Handle to the new C++ object (a derived type of base #AbstractTreeElement) that should replace - * #TreeElement. Step by step, data should be moved to it and operations based on the type should - * become virtual methods of the class hierarchy. + * The new inheritance based representation of the element (a derived type of base + * #AbstractTreeElement) that should eventually replace #TreeElement. Step by step, data should + * be moved to it and operations based on the type should become virtual methods of the class + * hierarchy. */ - std::unique_ptr<blender::ed::outliner::AbstractTreeElement> type; + std::unique_ptr<outliner::AbstractTreeElement> abstract_element; ListBase subtree; int xs, ys; /* Do selection. */ @@ -104,8 +110,7 @@ typedef struct TreeElement { short idcode; /* From TreeStore id. */ short xend; /* Width of item display, for select. */ const char *name; - void *directdata; /* Armature Bones, Base, Sequence, Strip... */ - PointerRNA rnaptr; /* RNA Pointer. */ + void *directdata; /* Armature Bones, Base, ... */ } TreeElement; typedef struct TreeElementIcon { @@ -140,7 +145,7 @@ typedef struct TreeElementIcon { ID_GD, \ ID_LS, \ ID_LP, \ - ID_HA, \ + ID_CV, \ ID_PT, \ ID_VO, \ ID_SIM) || /* Only in 'blendfile' mode ... :/ */ \ @@ -225,7 +230,7 @@ typedef enum { * - not searching into RNA items helps but isn't the complete solution */ -#define SEARCHING_OUTLINER(sov) (sov->search_flags & SO_SEARCH_RECURSIVE) +#define SEARCHING_OUTLINER(sov) ((sov)->search_flags & SO_SEARCH_RECURSIVE) /* is the current element open? if so we also show children */ #define TSELEM_OPEN(telm, sv) \ @@ -686,3 +691,19 @@ int outliner_context(const struct bContext *C, #ifdef __cplusplus } #endif + +namespace blender::ed::outliner { + +/** + * Helper to safely "cast" a #TreeElement to its new C++ #AbstractTreeElement, if possible. + * \return nullptr if the tree-element doesn't match the requested type \a TreeElementT or the + * element doesn't hold a C++ #AbstractTreeElement pendant yet. + */ +template<typename TreeElementT> TreeElementT *tree_element_cast(const TreeElement *te) +{ + static_assert(std::is_base_of_v<AbstractTreeElement, TreeElementT>, + "Requested tree-element type must be an AbstractTreeElement"); + return dynamic_cast<TreeElementT *>(te->abstract_element.get()); +} + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index c2a7bfb9b37..f256475c0da 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -80,6 +80,9 @@ #include "RNA_define.h" #include "outliner_intern.hh" +#include "tree/tree_element_seq.hh" + +using namespace blender::ed::outliner; /** * \note changes to selection are by convention and not essential. @@ -676,7 +679,8 @@ static void tree_element_sequence_activate(bContext *C, TreeElement *te, const eOLSetState set) { - Sequence *seq = (Sequence *)te->directdata; + const TreeElementSequence *te_seq = tree_element_cast<TreeElementSequence>(te); + Sequence *seq = &te_seq->getSequence(); Editing *ed = SEQ_editing_get(scene); if (BLI_findindex(ed->seqbasep, seq) != -1) { @@ -954,7 +958,8 @@ static eOLDrawState tree_element_posegroup_state_get(const ViewLayer *view_layer static eOLDrawState tree_element_sequence_state_get(const Scene *scene, const TreeElement *te) { - const Sequence *seq = (const Sequence *)te->directdata; + const TreeElementSequence *te_seq = tree_element_cast<TreeElementSequence>(te); + const Sequence *seq = &te_seq->getSequence(); const Editing *ed = scene->ed; if (ed && ed->act_seq == seq && seq->flag & SELECT) { @@ -965,7 +970,9 @@ static eOLDrawState tree_element_sequence_state_get(const Scene *scene, const Tr static eOLDrawState tree_element_sequence_dup_state_get(const TreeElement *te) { - const Sequence *seq = (const Sequence *)te->directdata; + const TreeElementSequenceStripDuplicate *te_dup = + tree_element_cast<TreeElementSequenceStripDuplicate>(te); + const Sequence *seq = &te_dup->getSequence(); if (seq->flag & SELECT) { return OL_DRAWSEL_NORMAL; } @@ -1191,7 +1198,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE case ID_AR: case ID_GD: case ID_LP: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: context = BCONTEXT_DATA; @@ -1604,8 +1611,7 @@ static int outliner_item_do_activate_from_cursor(bContext *C, if (!(te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]))) { if (deselect_all) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false); - changed = true; + changed |= outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false); } } /* Don't allow toggle on scene collection */ @@ -1653,17 +1659,19 @@ static int outliner_item_do_activate_from_cursor(bContext *C, changed = true; } - if (changed) { - if (rebuild_tree) { - ED_region_tag_redraw(region); - } - else { - ED_region_tag_redraw_no_rebuild(region); - } + if (!changed) { + return OPERATOR_CANCELLED; + } - ED_outliner_select_sync_from_outliner(C, space_outliner); + if (rebuild_tree) { + ED_region_tag_redraw(region); + } + else { + ED_region_tag_redraw_no_rebuild(region); } + ED_outliner_select_sync_from_outliner(C, space_outliner); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 1c1a4f6f4c2..337649834a4 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -29,8 +29,8 @@ #include "DNA_armature_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" +#include "DNA_curves_types.h" #include "DNA_gpencil_types.h" -#include "DNA_hair_types.h" #include "DNA_light_types.h" #include "DNA_linestyle_types.h" #include "DNA_material_types.h" @@ -96,9 +96,13 @@ #include "SEQ_sequencer.h" #include "outliner_intern.hh" +#include "tree/tree_element_rna.hh" +#include "tree/tree_element_seq.hh" static CLG_LogRef LOG = {"ed.outliner.tools"}; +using namespace blender::ed::outliner; + /* -------------------------------------------------------------------- */ /** \name ID/Library/Data Set/Un-link Utilities * \{ */ @@ -160,7 +164,7 @@ static void get_element_operation_type( case ID_CF: case ID_WS: case ID_LP: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: case ID_SIM: @@ -258,10 +262,10 @@ static void unlink_material_fn(bContext *UNUSED(C), totcol = mb->totcol; matar = mb->mat; } - else if (GS(tsep->id->name) == ID_HA) { - Hair *hair = (Hair *)tsep->id; - totcol = hair->totcol; - matar = hair->mat; + else if (GS(tsep->id->name) == ID_CV) { + Curves *curves = (Curves *)tsep->id; + totcol = curves->totcol; + matar = curves->mat; } else if (GS(tsep->id->name) == ID_PT) { PointCloud *pointcloud = (PointCloud *)tsep->id; @@ -760,38 +764,6 @@ static void id_local_fn(bContext *C, } } -static void object_proxy_to_override_convert_fn(bContext *C, - ReportList *reports, - Scene *UNUSED(scene), - TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) -{ - BLI_assert(TSE_IS_REAL_ID(tselem)); - ID *id_proxy = tselem->id; - BLI_assert(GS(id_proxy->name) == ID_OB); - Object *ob_proxy = (Object *)id_proxy; - Scene *scene = CTX_data_scene(C); - - if (ob_proxy->proxy == nullptr) { - return; - } - - if (!BKE_lib_override_library_proxy_convert( - CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy)) { - BKE_reportf( - reports, - RPT_ERROR_INVALID_INPUT, - "Could not create a library override from proxy '%s' (might use already local data?)", - ob_proxy->id.name + 2); - return; - } - - DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_WINDOW, nullptr); -} - struct OutlinerLibOverrideData { bool do_hierarchy; /** @@ -855,8 +827,9 @@ static void id_override_library_create_fn(bContext *C, if (!ID_IS_LINKED(te->store_elem->id)) { break; } - /* If we'd need to override that aren't ID, but it is not overridable, abort. */ - if (!ID_IS_OVERRIDABLE_LIBRARY(te->store_elem->id)) { + /* If some element in the tree needs to be overridden, but its ID is not overridable, + * abort. */ + if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(te->store_elem->id)) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); BKE_reportf(reports, RPT_WARNING, @@ -869,8 +842,13 @@ static void id_override_library_create_fn(bContext *C, te->store_elem->id->tag |= LIB_TAG_DOIT; } - success = BKE_lib_override_library_create( - bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, nullptr); + success = BKE_lib_override_library_create(bmain, + CTX_data_scene(C), + CTX_data_view_layer(C), + nullptr, + id_root, + id_reference, + nullptr); } else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) { success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != nullptr; @@ -1285,7 +1263,8 @@ static void ebone_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), static void sequence_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *scene_ptr) { - Sequence *seq = (Sequence *)te->directdata; + TreeElementSequence *te_seq = tree_element_cast<TreeElementSequence>(te); + Sequence *seq = &te_seq->getSequence(); Scene *scene = (Scene *)scene_ptr; Editing *ed = SEQ_editing_get(scene); if (BLI_findindex(ed->seqbasep, seq) != -1) { @@ -1336,10 +1315,16 @@ static void data_select_linked_fn(int event, TreeStoreElem *UNUSED(tselem), void *C_v) { + const TreeElementRNAStruct *te_rna_struct = tree_element_cast<TreeElementRNAStruct>(te); + if (!te_rna_struct) { + return; + } + if (event == OL_DOP_SELECT_LINKED) { - if (RNA_struct_is_ID(te->rnaptr.type)) { + const PointerRNA &ptr = te_rna_struct->getPointerRNA(); + if (RNA_struct_is_ID(ptr.type)) { bContext *C = (bContext *)C_v; - ID *id = reinterpret_cast<ID *>(te->rnaptr.data); + ID *id = reinterpret_cast<ID *>(ptr.data); ED_object_select_linked_by_id(C, id); } @@ -1520,7 +1505,6 @@ enum { OL_OP_SELECT_HIERARCHY, OL_OP_REMAP, OL_OP_RENAME, - OL_OP_PROXY_TO_OVERRIDE_CONVERT, }; static const EnumPropertyItem prop_object_op_types[] = { @@ -1533,11 +1517,6 @@ static const EnumPropertyItem prop_object_op_types[] = { "Remap Users", "Make all users of selected data-blocks to use instead a new chosen one"}, {OL_OP_RENAME, "RENAME", 0, "Rename", ""}, - {OL_OP_PROXY_TO_OVERRIDE_CONVERT, - "OBJECT_PROXY_TO_OVERRIDE", - 0, - "Convert Proxy to Override", - "Convert a Proxy object to a full library override, including all its dependencies"}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -1602,15 +1581,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); str = "Rename Object"; } - else if (event == OL_OP_PROXY_TO_OVERRIDE_CONVERT) { - outliner_do_object_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - object_proxy_to_override_convert_fn); - str = "Convert Proxy to Override"; - } else { BLI_assert(0); return OPERATOR_CANCELLED; @@ -1782,7 +1752,6 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, @@ -1824,11 +1793,6 @@ static const EnumPropertyItem prop_id_op_types[] = { 0, "Make Library Override Hierarchy", "Make a local override of this linked data-block, and its hierarchy of dependencies"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT, - "OVERRIDE_LIBRARY_PROXY_CONVERT", - 0, - "Convert Proxy to Override", - "Convert a Proxy object to a full library override, including all its dependencies"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, "OVERRIDE_LIBRARY_RESET", 0, @@ -1901,16 +1865,6 @@ static bool outliner_id_operation_item_poll(bContext *C, return true; } return false; - case OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT: { - if (GS(tselem->id->name) == ID_OB) { - Object *ob = (Object *)tselem->id; - - if ((ob != nullptr) && (ob->proxy != nullptr)) { - return true; - } - } - return false; - } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: @@ -2087,16 +2041,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Overridden Data Hierarchy"); break; } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT: { - outliner_do_object_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - object_proxy_to_override_convert_fn); - ED_undo_push(C, "Convert Proxy to Override"); - break; - } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: { OutlinerLibOverrideData override_data{}; outliner_do_libdata_operation(C, diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index 3454a27ce13..60f5437ad88 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -32,9 +32,9 @@ #include "DNA_camera_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" +#include "DNA_curves_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" -#include "DNA_hair_types.h" #include "DNA_key_types.h" #include "DNA_light_types.h" #include "DNA_lightprobe_types.h" @@ -217,7 +217,7 @@ void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree) if (element->flag & TE_FREE_NAME) { MEM_freeN((void *)element->name); } - element->type = nullptr; + element->abstract_element = nullptr; MEM_delete(element); } @@ -302,10 +302,6 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, /* 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); - } - outliner_add_element(space_outliner, &te->subtree, ob->data, te, TSE_SOME_ID, 0); if (ob->pose) { @@ -777,10 +773,10 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, } break; } - case ID_HA: { - Hair *hair = (Hair *)id; - if (outliner_animdata_test(hair->adt)) { - outliner_add_element(space_outliner, &te->subtree, hair, te, TSE_ANIM_DATA, 0); + case ID_CV: { + Curves *curves = (Curves *)id; + if (outliner_animdata_test(curves->adt)) { + outliner_add_element(space_outliner, &te->subtree, curves, te, TSE_ANIM_DATA, 0); } break; } @@ -860,10 +856,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, te->parent = parent; te->index = index; /* For data arrays. */ - /* New C++ based type handle. Only some support this, eventually this should replace - * `TreeElement` entirely. */ - te->type = AbstractTreeElement::createFromType(type, *te, idv); - if (te->type) { + /* New inheritance based element representation. Not all element types support this yet, + * eventually it should replace #TreeElement entirely. */ + te->abstract_element = AbstractTreeElement::createFromType(type, *te, idv); + if (te->abstract_element) { /* Element types ported to the new design are expected to have their name set at this point! */ BLI_assert(te->name != nullptr); } @@ -887,12 +883,12 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, /* pass */ } else if (type == TSE_SOME_ID) { - if (!te->type) { + if (!te->abstract_element) { BLI_assert_msg(0, "Expected this ID type to be ported to new Outliner tree-element design"); } } else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) { - if (!te->type) { + if (!te->abstract_element) { BLI_assert_msg(0, "Expected override types to be ported to new Outliner tree-element design"); } @@ -903,20 +899,20 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, /* The new type design sets the name already, don't override that here. We need to figure out * how to deal with the idcode for non-TSE_SOME_ID types still. Some rely on it... */ - if (!te->type) { + if (!te->abstract_element) { te->name = id->name + 2; /* Default, can be overridden by Library or non-ID data. */ } te->idcode = GS(id->name); } - if (te->type && te->type->isExpandValid()) { - tree_element_expand(*te->type, *space_outliner); + if (te->abstract_element && te->abstract_element->isExpandValid()) { + tree_element_expand(*te->abstract_element, *space_outliner); } else if (type == TSE_SOME_ID) { /* ID types not (fully) ported to new design yet. */ - if (te->type->expandPoll(*space_outliner)) { + if (te->abstract_element->expandPoll(*space_outliner)) { outliner_add_id_contents(space_outliner, te, tselem, id); - te->type->postExpand(*space_outliner); + te->abstract_element->postExpand(*space_outliner); } } else if (ELEM(type, @@ -925,195 +921,14 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, TSE_NLA, TSE_NLA_ACTION, TSE_NLA_TRACK, - TSE_GP_LAYER)) { - /* Should already use new AbstractTreeElement design. */ - BLI_assert(0); - } - else if (type == TSE_SEQUENCE) { - Sequence *seq = (Sequence *)idv; - - /* - * The idcode is a little hack, but the outliner - * only check te->idcode if te->type is equal to zero, - * so this is "safe". - */ - te->idcode = seq->type; - te->directdata = seq; - te->name = seq->name + 2; - - if (!(seq->type & SEQ_TYPE_EFFECT)) { - /* - * This work like the sequence. - * If the sequence have a name (not default name) - * show it, in other case put the filename. - */ - - if (seq->type == SEQ_TYPE_META) { - LISTBASE_FOREACH (Sequence *, p, &seq->seqbase) { - outliner_add_element(space_outliner, &te->subtree, (void *)p, te, TSE_SEQUENCE, index); - } - } - else { - outliner_add_element( - space_outliner, &te->subtree, (void *)seq->strip, te, TSE_SEQ_STRIP, index); - } - } - } - else if (type == TSE_SEQ_STRIP) { - Strip *strip = (Strip *)idv; - - if (strip->dir[0] != '\0') { - te->name = strip->dir; - } - else { - te->name = IFACE_("Strip None"); - } - te->directdata = strip; - } - else if (type == TSE_SEQUENCE_DUP) { - Sequence *seq = (Sequence *)idv; - - te->idcode = seq->type; - te->directdata = seq; - te->name = seq->strip->stripdata->name; - } - else if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { - PointerRNA *ptr = (PointerRNA *)idv; - - /* Don't display arrays larger, weak but index is stored as a short, - * also the outliner isn't intended for editing such large data-sets. */ - BLI_STATIC_ASSERT(sizeof(te->index) == 2, "Index is no longer short!") - const int tot_limit = SHRT_MAX; - - /* we do lazy build, for speed and to avoid infinite recursion */ - - if (ptr->data == nullptr) { - te->name = IFACE_("(empty)"); - } - else if (type == TSE_RNA_STRUCT) { - /* struct */ - te->name = RNA_struct_name_get_alloc(ptr, nullptr, 0, nullptr); - - if (te->name) { - te->flag |= TE_FREE_NAME; - } - else { - te->name = RNA_struct_ui_name(ptr->type); - } - - /* If searching don't expand RNA entries */ - if (SEARCHING_OUTLINER(space_outliner) && BLI_strcasecmp("RNA", te->name) == 0) { - tselem->flag &= ~TSE_CHILDSEARCH; - } - - PropertyRNA *iterprop = RNA_struct_iterator_property(ptr->type); - int tot = RNA_property_collection_length(ptr, iterprop); - CLAMP_MAX(tot, tot_limit); - - /* auto open these cases */ - if (!parent || (RNA_property_type(reinterpret_cast<PropertyRNA *>(parent->directdata))) == - PROP_POINTER) { - if (!tselem->used) { - tselem->flag &= ~TSE_CLOSED; - } - } - - if (TSELEM_OPEN(tselem, space_outliner)) { - for (int a = 0; a < tot; a++) { - PointerRNA propptr; - RNA_property_collection_lookup_int(ptr, iterprop, a, &propptr); - if (!(RNA_property_flag(reinterpret_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) { - outliner_add_element( - space_outliner, &te->subtree, (void *)ptr, te, TSE_RNA_PROPERTY, a); - } - } - } - else if (tot) { - te->flag |= TE_LAZY_CLOSED; - } - - te->rnaptr = *ptr; - } - else if (type == TSE_RNA_PROPERTY) { - /* property */ - PointerRNA propptr; - PropertyRNA *iterprop = RNA_struct_iterator_property(ptr->type); - RNA_property_collection_lookup_int(ptr, iterprop, index, &propptr); - - PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(propptr.data); - PropertyType proptype = RNA_property_type(prop); - - te->name = RNA_property_ui_name(prop); - te->directdata = prop; - te->rnaptr = *ptr; - - /* If searching don't expand RNA entries */ - if (SEARCHING_OUTLINER(space_outliner) && BLI_strcasecmp("RNA", te->name) == 0) { - tselem->flag &= ~TSE_CHILDSEARCH; - } - - if (proptype == PROP_POINTER) { - PointerRNA pptr = RNA_property_pointer_get(ptr, prop); - - if (pptr.data) { - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_add_element( - space_outliner, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, -1); - } - else { - te->flag |= TE_LAZY_CLOSED; - } - } - } - else if (proptype == PROP_COLLECTION) { - int tot = RNA_property_collection_length(ptr, prop); - CLAMP_MAX(tot, tot_limit); - - if (TSELEM_OPEN(tselem, space_outliner)) { - for (int a = 0; a < tot; a++) { - PointerRNA pptr; - RNA_property_collection_lookup_int(ptr, prop, a, &pptr); - outliner_add_element( - space_outliner, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, a); - } - } - else if (tot) { - te->flag |= TE_LAZY_CLOSED; - } - } - else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { - int tot = RNA_property_array_length(ptr, prop); - CLAMP_MAX(tot, tot_limit); - - if (TSELEM_OPEN(tselem, space_outliner)) { - for (int a = 0; a < tot; a++) { - outliner_add_element( - space_outliner, &te->subtree, (void *)ptr, te, TSE_RNA_ARRAY_ELEM, a); - } - } - else if (tot) { - te->flag |= TE_LAZY_CLOSED; - } - } - } - else if (type == TSE_RNA_ARRAY_ELEM) { - PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(parent->directdata); - - te->directdata = prop; - te->rnaptr = *ptr; - te->index = index; - - char c = RNA_property_array_item_char(prop, index); - - te->name = reinterpret_cast<char *>(MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName")); - if (c) { - sprintf((char *)te->name, " %c", c); - } - else { - sprintf((char *)te->name, " %d", index + 1); - } - te->flag |= TE_FREE_NAME; - } + TSE_GP_LAYER, + TSE_RNA_STRUCT, + TSE_RNA_PROPERTY, + TSE_RNA_ARRAY_ELEM, + TSE_SEQUENCE, + TSE_SEQ_STRIP, + TSE_SEQUENCE_DUP)) { + BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design"); } if (tree_element_warnings_get(te, nullptr, nullptr)) { diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc index ea07f1d4611..0fb17fa3f47 100644 --- a/source/blender/editors/space_outliner/space_outliner.cc +++ b/source/blender/editors/space_outliner/space_outliner.cc @@ -31,6 +31,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_lib_remap.h" #include "BKE_outliner_treehash.h" #include "BKE_screen.h" @@ -405,45 +406,49 @@ static SpaceLink *outliner_duplicate(SpaceLink *sl) return (SpaceLink *)space_outliner_new; } -static void outliner_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id) +static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRemapper *mappings) { SpaceOutliner *space_outliner = (SpaceOutliner *)slink; - /* Some early out checks. */ - if (!TREESTORE_ID_TYPE(old_id)) { - return; /* ID type is not used by outliner. */ - } + BKE_id_remapper_apply(mappings, (ID **)&space_outliner->search_tse.id, ID_REMAP_APPLY_DEFAULT); - if (space_outliner->search_tse.id == old_id) { - space_outliner->search_tse.id = new_id; + if (!space_outliner->treestore) { + return; } - if (space_outliner->treestore) { - TreeStoreElem *tselem; - BLI_mempool_iter iter; - bool changed = false; - - BLI_mempool_iternew(space_outliner->treestore, &iter); - while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { - if (tselem->id == old_id) { - tselem->id = new_id; + TreeStoreElem *tselem; + BLI_mempool_iter iter; + bool changed = false; + bool unassigned = false; + + BLI_mempool_iternew(space_outliner->treestore, &iter); + while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { + switch (BKE_id_remapper_apply(mappings, &tselem->id, ID_REMAP_APPLY_DEFAULT)) { + case ID_REMAP_RESULT_SOURCE_REMAPPED: changed = true; - } + break; + case ID_REMAP_RESULT_SOURCE_UNASSIGNED: + changed = true; + unassigned = true; + break; + case ID_REMAP_RESULT_SOURCE_UNAVAILABLE: + case ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE: + break; } + } - /* Note that the Outliner may not be the active editor of the area, and hence not initialized. - * So runtime data might not have been created yet. */ - if (space_outliner->runtime && space_outliner->runtime->treehash && changed) { - /* rebuild hash table, because it depends on ids too */ - /* postpone a full rebuild because this can be called many times on-free */ - space_outliner->storeflag |= SO_TREESTORE_REBUILD; - - if (new_id == nullptr) { - /* Redraw is needed when removing data for multiple outlines show the same data. - * without this, the stale data won't get fully flushed when this outliner - * is not the active outliner the user is interacting with. See T85976. */ - ED_area_tag_redraw(area); - } + /* Note that the Outliner may not be the active editor of the area, and hence not initialized. + * So runtime data might not have been created yet. */ + if (space_outliner->runtime && space_outliner->runtime->treehash && changed) { + /* rebuild hash table, because it depends on ids too */ + /* postpone a full rebuild because this can be called many times on-free */ + space_outliner->storeflag |= SO_TREESTORE_REBUILD; + + if (unassigned) { + /* Redraw is needed when removing data for multiple outlines show the same data. + * without this, the stale data won't get fully flushed when this outliner + * is not the active outliner the user is interacting with. See T85976. */ + ED_area_tag_redraw(area); } } } diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index ea78f70f86d..5685d8964f5 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -33,7 +33,9 @@ #include "tree_element_id.hh" #include "tree_element_nla.hh" #include "tree_element_overrides.hh" +#include "tree_element_rna.hh" #include "tree_element_scene_objects.hh" +#include "tree_element_seq.hh" #include "tree_element_view_layer.hh" #include "../outliner_intern.hh" @@ -86,6 +88,23 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i case TSE_LIBRARY_OVERRIDE: return std::make_unique<TreeElementOverridesProperty>( legacy_te, *static_cast<TreeElementOverridesData *>(idv)); + case TSE_RNA_STRUCT: + return std::make_unique<TreeElementRNAStruct>(legacy_te, + *reinterpret_cast<PointerRNA *>(idv)); + case TSE_RNA_PROPERTY: + return std::make_unique<TreeElementRNAProperty>( + legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index); + case TSE_RNA_ARRAY_ELEM: + return std::make_unique<TreeElementRNAArrayElement>( + legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index); + case TSE_SEQUENCE: + return std::make_unique<TreeElementSequence>(legacy_te, *reinterpret_cast<Sequence *>(idv)); + case TSE_SEQ_STRIP: + return std::make_unique<TreeElementSequenceStrip>(legacy_te, + *reinterpret_cast<Strip *>(idv)); + case TSE_SEQUENCE_DUP: + return std::make_unique<TreeElementSequenceStripDuplicate>( + legacy_te, *reinterpret_cast<Sequence *>(idv)); default: break; } diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc index afbbd171cf4..3289cb8ac76 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -69,7 +69,7 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t case ID_LP: case ID_GD: case ID_WS: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: case ID_SIM: diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.cc b/source/blender/editors/space_outliner/tree/tree_element_rna.cc new file mode 100644 index 00000000000..7a9f1b6f0fa --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_rna.cc @@ -0,0 +1,268 @@ +/* + * 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 spoutliner + */ + +#include <climits> +#include <iostream> + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_outliner_types.h" +#include "DNA_space_types.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "../outliner_intern.hh" + +#include "tree_element_rna.hh" + +namespace blender::ed::outliner { + +/* Don't display arrays larger, weak but index is stored as a short, + * also the outliner isn't intended for editing such large data-sets. */ +BLI_STATIC_ASSERT(sizeof(TreeElement::index) == 2, "Index is no longer short!") + +/* -------------------------------------------------------------------- */ +/* Common functionality (#TreeElementRNACommon Base Class) */ + +TreeElementRNACommon::TreeElementRNACommon(TreeElement &legacy_te, PointerRNA &rna_ptr) + : AbstractTreeElement(legacy_te), rna_ptr_(rna_ptr) +{ + /* Create an empty tree-element. */ + if (!isRNAValid()) { + legacy_te_.name = IFACE_("(empty)"); + return; + } +} + +bool TreeElementRNACommon::isExpandValid() const +{ + return true; +} + +bool TreeElementRNACommon::isRNAValid() const +{ + return rna_ptr_.data != nullptr; +} + +bool TreeElementRNACommon::expandPoll(const SpaceOutliner &) const +{ + return isRNAValid(); +} + +const PointerRNA &TreeElementRNACommon::getPointerRNA() const +{ + return rna_ptr_; +} + +PropertyRNA *TreeElementRNACommon::getPropertyRNA() const +{ + return nullptr; +} + +/* -------------------------------------------------------------------- */ +/* RNA Struct */ + +TreeElementRNAStruct::TreeElementRNAStruct(TreeElement &legacy_te, PointerRNA &rna_ptr) + : TreeElementRNACommon(legacy_te, rna_ptr) +{ + BLI_assert(legacy_te.store_elem->type == TSE_RNA_STRUCT); + + if (!isRNAValid()) { + return; + } + + legacy_te_.name = RNA_struct_name_get_alloc(&rna_ptr, nullptr, 0, nullptr); + if (legacy_te_.name) { + legacy_te_.flag |= TE_FREE_NAME; + } + else { + legacy_te_.name = RNA_struct_ui_name(rna_ptr.type); + } +} + +void TreeElementRNAStruct::expand(SpaceOutliner &space_outliner) const +{ + TreeStoreElem &tselem = *TREESTORE(&legacy_te_); + PointerRNA ptr = rna_ptr_; + + /* If searching don't expand RNA entries */ + if (SEARCHING_OUTLINER(&space_outliner) && BLI_strcasecmp("RNA", legacy_te_.name) == 0) { + tselem.flag &= ~TSE_CHILDSEARCH; + } + + PropertyRNA *iterprop = RNA_struct_iterator_property(ptr.type); + int tot = RNA_property_collection_length(&ptr, iterprop); + CLAMP_MAX(tot, max_index); + + TreeElementRNAProperty *parent_prop_te = legacy_te_.parent ? + tree_element_cast<TreeElementRNAProperty>( + legacy_te_.parent) : + nullptr; + /* auto open these cases */ + if (!parent_prop_te || (RNA_property_type(parent_prop_te->getPropertyRNA()) == PROP_POINTER)) { + if (!tselem.used) { + tselem.flag &= ~TSE_CLOSED; + } + } + + if (TSELEM_OPEN(&tselem, &space_outliner)) { + for (int index = 0; index < tot; index++) { + PointerRNA propptr; + RNA_property_collection_lookup_int(&ptr, iterprop, index, &propptr); + if (!(RNA_property_flag(reinterpret_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) { + outliner_add_element( + &space_outliner, &legacy_te_.subtree, &ptr, &legacy_te_, TSE_RNA_PROPERTY, index); + } + } + } + else if (tot) { + legacy_te_.flag |= TE_LAZY_CLOSED; + } +} + +/* -------------------------------------------------------------------- */ +/* RNA Property */ + +TreeElementRNAProperty::TreeElementRNAProperty(TreeElement &legacy_te, + PointerRNA &rna_ptr, + const int index) + : TreeElementRNACommon(legacy_te, rna_ptr) +{ + BLI_assert(legacy_te.store_elem->type == TSE_RNA_PROPERTY); + + if (!isRNAValid()) { + return; + } + + PointerRNA propptr; + PropertyRNA *iterprop = RNA_struct_iterator_property(rna_ptr.type); + RNA_property_collection_lookup_int(&rna_ptr, iterprop, index, &propptr); + + PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(propptr.data); + + legacy_te_.name = RNA_property_ui_name(prop); + rna_prop_ = prop; +} + +void TreeElementRNAProperty::expand(SpaceOutliner &space_outliner) const +{ + TreeStoreElem &tselem = *TREESTORE(&legacy_te_); + PointerRNA rna_ptr = rna_ptr_; + PropertyType proptype = RNA_property_type(rna_prop_); + + /* If searching don't expand RNA entries */ + if (SEARCHING_OUTLINER(&space_outliner) && BLI_strcasecmp("RNA", legacy_te_.name) == 0) { + tselem.flag &= ~TSE_CHILDSEARCH; + } + + if (proptype == PROP_POINTER) { + PointerRNA pptr = RNA_property_pointer_get(&rna_ptr, rna_prop_); + + if (pptr.data) { + if (TSELEM_OPEN(&tselem, &space_outliner)) { + outliner_add_element( + &space_outliner, &legacy_te_.subtree, &pptr, &legacy_te_, TSE_RNA_STRUCT, -1); + } + else { + legacy_te_.flag |= TE_LAZY_CLOSED; + } + } + } + else if (proptype == PROP_COLLECTION) { + int tot = RNA_property_collection_length(&rna_ptr, rna_prop_); + CLAMP_MAX(tot, max_index); + + if (TSELEM_OPEN(&tselem, &space_outliner)) { + for (int index = 0; index < tot; index++) { + PointerRNA pptr; + RNA_property_collection_lookup_int(&rna_ptr, rna_prop_, index, &pptr); + outliner_add_element( + &space_outliner, &legacy_te_.subtree, &pptr, &legacy_te_, TSE_RNA_STRUCT, index); + } + } + else if (tot) { + legacy_te_.flag |= TE_LAZY_CLOSED; + } + } + else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { + int tot = RNA_property_array_length(&rna_ptr, rna_prop_); + CLAMP_MAX(tot, max_index); + + if (TSELEM_OPEN(&tselem, &space_outliner)) { + for (int index = 0; index < tot; index++) { + outliner_add_element(&space_outliner, + &legacy_te_.subtree, + &rna_ptr, + &legacy_te_, + TSE_RNA_ARRAY_ELEM, + index); + } + } + else if (tot) { + legacy_te_.flag |= TE_LAZY_CLOSED; + } + } +} + +PropertyRNA *TreeElementRNAProperty::getPropertyRNA() const +{ + return rna_prop_; +} + +/* -------------------------------------------------------------------- */ +/* RNA Array Element */ + +TreeElementRNAArrayElement::TreeElementRNAArrayElement(TreeElement &legacy_te, + PointerRNA &rna_ptr, + const int index) + : TreeElementRNACommon(legacy_te, rna_ptr) +{ + BLI_assert(legacy_te.store_elem->type == TSE_RNA_ARRAY_ELEM); + + BLI_assert(legacy_te.parent && (legacy_te.parent->store_elem->type == TSE_RNA_PROPERTY)); + legacy_te_.index = index; + + char c = RNA_property_array_item_char(TreeElementRNAArrayElement::getPropertyRNA(), index); + + legacy_te_.name = reinterpret_cast<char *>( + MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName")); + if (c) { + sprintf((char *)legacy_te_.name, " %c", c); + } + else { + sprintf((char *)legacy_te_.name, " %d", index + 1); + } + legacy_te_.flag |= TE_FREE_NAME; +} + +PropertyRNA *TreeElementRNAArrayElement::getPropertyRNA() const +{ + /* Forward query to the parent (which is expected to be a #TreeElementRNAProperty). */ + const TreeElementRNAProperty *parent_prop_te = tree_element_cast<TreeElementRNAProperty>( + legacy_te_.parent); + return parent_prop_te ? parent_prop_te->getPropertyRNA() : nullptr; +} + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.hh b/source/blender/editors/space_outliner/tree/tree_element_rna.hh new file mode 100644 index 00000000000..1f107ddbf88 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_rna.hh @@ -0,0 +1,86 @@ +/* + * 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 spoutliner + */ + +#pragma once + +#include <limits> + +#include "RNA_types.h" + +#include "tree_element.hh" + +struct PointerRNA; + +namespace blender::ed::outliner { + +/** + * Base class for common behavior of RNA tree elements. + */ +class TreeElementRNACommon : public AbstractTreeElement { + protected: + constexpr static int max_index = std::numeric_limits<short>::max(); + PointerRNA rna_ptr_; + + public: + TreeElementRNACommon(TreeElement &legacy_te, PointerRNA &rna_ptr); + bool isExpandValid() const override; + bool expandPoll(const SpaceOutliner &) const override; + + const PointerRNA &getPointerRNA() const; + /** + * If this element represents a property or is part of a property (array element), this returns + * the property. Otherwise nullptr. + */ + virtual PropertyRNA *getPropertyRNA() const; + + bool isRNAValid() const; +}; + +/* -------------------------------------------------------------------- */ + +class TreeElementRNAStruct : public TreeElementRNACommon { + public: + TreeElementRNAStruct(TreeElement &legacy_te, PointerRNA &rna_ptr); + void expand(SpaceOutliner &space_outliner) const override; +}; + +/* -------------------------------------------------------------------- */ + +class TreeElementRNAProperty : public TreeElementRNACommon { + private: + PropertyRNA *rna_prop_ = nullptr; + + public: + TreeElementRNAProperty(TreeElement &legacy_te, PointerRNA &rna_ptr, int index); + void expand(SpaceOutliner &space_outliner) const override; + + PropertyRNA *getPropertyRNA() const override; +}; + +/* -------------------------------------------------------------------- */ + +class TreeElementRNAArrayElement : public TreeElementRNACommon { + public: + TreeElementRNAArrayElement(TreeElement &legacy_te, PointerRNA &rna_ptr, int index); + + PropertyRNA *getPropertyRNA() const override; +}; + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_seq.cc b/source/blender/editors/space_outliner/tree/tree_element_seq.cc new file mode 100644 index 00000000000..8d0b4c140c7 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_seq.cc @@ -0,0 +1,111 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup spoutliner + */ + +#include "DNA_outliner_types.h" +#include "DNA_sequence_types.h" + +#include "BLI_listbase.h" + +#include "BLT_translation.h" + +#include "../outliner_intern.hh" +#include "tree_element_seq.hh" + +namespace blender::ed::outliner { + +TreeElementSequence::TreeElementSequence(TreeElement &legacy_te, Sequence &sequence) + : AbstractTreeElement(legacy_te), sequence_(sequence) +{ + BLI_assert(legacy_te.store_elem->type == TSE_SEQUENCE); + + /* + * The idcode is a little hack, but the outliner + * only check te->idcode if te->type is equal to zero, + * so this is "safe". + */ + legacy_te.idcode = sequence_.type; + legacy_te.name = sequence_.name + 2; +} + +bool TreeElementSequence::expandPoll(const SpaceOutliner & /*space_outliner*/) const +{ + return !(sequence_.type & SEQ_TYPE_EFFECT); +} + +void TreeElementSequence::expand(SpaceOutliner &space_outliner) const +{ + /* + * This work like the sequence. + * If the sequence have a name (not default name) + * show it, in other case put the filename. + */ + + if (sequence_.type == SEQ_TYPE_META) { + LISTBASE_FOREACH (Sequence *, child, &sequence_.seqbase) { + outliner_add_element( + &space_outliner, &legacy_te_.subtree, child, &legacy_te_, TSE_SEQUENCE, 0); + } + } + else { + outliner_add_element( + &space_outliner, &legacy_te_.subtree, sequence_.strip, &legacy_te_, TSE_SEQ_STRIP, 0); + } +} + +Sequence &TreeElementSequence::getSequence() const +{ + return sequence_; +} + +/* -------------------------------------------------------------------- */ +/* Strip */ + +TreeElementSequenceStrip::TreeElementSequenceStrip(TreeElement &legacy_te, Strip &strip) + : AbstractTreeElement(legacy_te) +{ + BLI_assert(legacy_te.store_elem->type == TSE_SEQ_STRIP); + + if (strip.dir[0] != '\0') { + legacy_te_.name = strip.dir; + } + else { + legacy_te_.name = IFACE_("Strip None"); + } +} + +/* -------------------------------------------------------------------- */ +/* Strip Duplicate */ + +TreeElementSequenceStripDuplicate::TreeElementSequenceStripDuplicate(TreeElement &legacy_te, + Sequence &sequence) + : AbstractTreeElement(legacy_te), sequence_(sequence) +{ + BLI_assert(legacy_te.store_elem->type == TSE_SEQUENCE_DUP); + + legacy_te_.idcode = sequence.type; + legacy_te_.name = sequence.strip->stripdata->name; +} + +Sequence &TreeElementSequenceStripDuplicate::getSequence() const +{ + return sequence_; +} + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_seq.hh b/source/blender/editors/space_outliner/tree/tree_element_seq.hh new file mode 100644 index 00000000000..2b334b5b7fa --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_seq.hh @@ -0,0 +1,60 @@ +/* + * 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 spoutliner + */ + +#pragma once + +#include "tree_element.hh" + +struct Sequence; +struct Strip; + +namespace blender::ed::outliner { + +class TreeElementSequence : public AbstractTreeElement { + Sequence &sequence_; + + public: + TreeElementSequence(TreeElement &legacy_te, Sequence &sequence); + + bool expandPoll(const SpaceOutliner &) const override; + void expand(SpaceOutliner &) const override; + + Sequence &getSequence() const; +}; + +/* -------------------------------------------------------------------- */ + +class TreeElementSequenceStrip : public AbstractTreeElement { + public: + TreeElementSequenceStrip(TreeElement &legacy_te, Strip &strip); +}; + +/* -------------------------------------------------------------------- */ + +class TreeElementSequenceStripDuplicate : public AbstractTreeElement { + Sequence &sequence_; + + public: + TreeElementSequenceStripDuplicate(TreeElement &legacy_te, Sequence &sequence); + + Sequence &getSequence() const; +}; + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index bf8cf89699d..d93dc0ac0c0 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -70,9 +70,5 @@ if(WITH_AUDASPACE) ) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_space_sequencer "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 9f31e55439d..aef6b30986d 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -59,6 +59,7 @@ #include "SEQ_add.h" #include "SEQ_effects.h" +#include "SEQ_iterator.h" #include "SEQ_proxy.h" #include "SEQ_relations.h" #include "SEQ_render.h" @@ -601,29 +602,28 @@ static IMB_Proxy_Size seq_get_proxy_size_flags(bContext *C) return proxy_sizes; } -static void seq_build_proxy(bContext *C, Sequence *seq) +static void seq_build_proxy(bContext *C, SeqCollection *movie_strips) { if (U.sequencer_proxy_setup != USER_SEQ_PROXY_SETUP_AUTOMATIC) { return; } - /* Enable and set proxy size. */ - SEQ_proxy_set(seq, true); - seq->strip->proxy->build_size_flags = seq_get_proxy_size_flags(C); - seq->strip->proxy->build_flags |= SEQ_PROXY_SKIP_EXISTING; - - /* Build proxy. */ - GSet *file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); wmJob *wm_job = ED_seq_proxy_wm_job_get(C); ProxyJob *pj = ED_seq_proxy_job_get(C, wm_job); - SEQ_proxy_rebuild_context(pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue); - BLI_gset_free(file_list, MEM_freeN); + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, movie_strips) { + /* Enable and set proxy size. */ + SEQ_proxy_set(seq, true); + seq->strip->proxy->build_size_flags = seq_get_proxy_size_flags(C); + seq->strip->proxy->build_flags |= SEQ_PROXY_SKIP_EXISTING; + SEQ_proxy_rebuild_context(pj->main, pj->depsgraph, pj->scene, seq, NULL, &pj->queue, true); + } if (!WM_jobs_is_running(wm_job)) { G.is_break = false; WM_jobs_start(CTX_wm_manager(C), wm_job); } - ED_area_tag_redraw(CTX_wm_area(C)); } @@ -637,12 +637,14 @@ static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene, } SEQ_transform_set_right_handle_frame(seq_sound, SEQ_transform_get_right_handle_frame(seq_movie)); + SEQ_transform_set_left_handle_frame(seq_sound, SEQ_transform_get_left_handle_frame(seq_movie)); SEQ_time_update_sequence(scene, seqbase, seq_sound); } static void sequencer_add_movie_multiple_strips(bContext *C, wmOperator *op, - SeqLoadData *load_data) + SeqLoadData *load_data, + SeqCollection *r_movie_strips) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -657,61 +659,32 @@ static void sequencer_add_movie_multiple_strips(bContext *C, BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; - double video_start_offset = -1; - double audio_start_offset = 0; - - if (RNA_boolean_get(op->ptr, "sound")) { - SoundStreamInfo sound_info; - if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) { - audio_start_offset = video_start_offset = sound_info.start; - } - } load_data->channel++; - seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); + seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data); load_data->channel--; if (seq_movie == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); } else { if (RNA_boolean_get(op->ptr, "sound")) { - int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS; - - int video_frame_offset = video_start_offset * FPS; - int audio_frame_offset = audio_start_offset * FPS; - - double video_frame_remainder = video_start_offset * FPS - video_frame_offset; - double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset; - - double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS; - - video_frame_offset -= minimum_frame_offset; - audio_frame_offset -= minimum_frame_offset; - - load_data->start_frame += audio_frame_offset; - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); - - int min_startdisp = 0, max_enddisp = 0; - if (seq_sound != NULL) { - min_startdisp = MIN2(seq_movie->startdisp, seq_sound->startdisp); - max_enddisp = MAX2(seq_movie->enddisp, seq_sound->enddisp); - } - - load_data->start_frame += max_enddisp - min_startdisp - audio_frame_offset; + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound); } - else { - load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; - } - sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound); + + load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); - seq_build_proxy(C, seq_movie); + SEQ_collection_append_strip(seq_movie, r_movie_strips); } } RNA_END; } -static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoadData *load_data) +static bool sequencer_add_movie_single_strip(bContext *C, + wmOperator *op, + SeqLoadData *load_data, + SeqCollection *r_movie_strips) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -719,18 +692,9 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; - double video_start_offset = -1; - double audio_start_offset = 0; - - if (RNA_boolean_get(op->ptr, "sound")) { - SoundStreamInfo sound_info; - if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) { - audio_start_offset = video_start_offset = sound_info.start; - } - } load_data->channel++; - seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); + seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data); load_data->channel--; if (seq_movie == NULL) { @@ -738,26 +702,12 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa return false; } if (RNA_boolean_get(op->ptr, "sound")) { - int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS; - - int video_frame_offset = video_start_offset * FPS; - int audio_frame_offset = audio_start_offset * FPS; - - double video_frame_remainder = video_start_offset * FPS - video_frame_offset; - double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset; - - double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS; - - video_frame_offset -= minimum_frame_offset; - audio_frame_offset -= minimum_frame_offset; - - load_data->start_frame += audio_frame_offset; - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound); } - sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound); seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); - seq_build_proxy(C, seq_movie); + SEQ_collection_append_strip(seq_movie, r_movie_strips); return true; } @@ -774,25 +724,30 @@ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) ED_sequencer_deselect_all(scene); } + SeqCollection *movie_strips = SEQ_collection_create(__func__); const int tot_files = RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); if (tot_files > 1) { - sequencer_add_movie_multiple_strips(C, op, &load_data); + sequencer_add_movie_multiple_strips(C, op, &load_data, movie_strips); } else { - if (!sequencer_add_movie_single_strip(C, op, &load_data)) { - sequencer_add_cancel(C, op); - return OPERATOR_CANCELLED; - } + sequencer_add_movie_single_strip(C, op, &load_data, movie_strips); } - /* Free custom data. */ - sequencer_add_cancel(C, op); + if (SEQ_collection_len(movie_strips) == 0) { + SEQ_collection_free(movie_strips); + return OPERATOR_CANCELLED; + } + seq_build_proxy(C, movie_strips); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + /* Free custom data. */ + sequencer_add_cancel(C, op); + SEQ_collection_free(movie_strips); + return OPERATOR_FINISHED; } @@ -896,7 +851,7 @@ static void sequencer_add_sound_multiple_strips(bContext *C, RNA_string_get(&itemptr, "name", file_only); BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only); BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); - Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); if (seq == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); } @@ -914,7 +869,7 @@ static bool sequencer_add_sound_single_strip(bContext *C, wmOperator *op, SeqLoa Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_ensure(scene); - Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); if (seq == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); return false; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 3b5e16d84a9..b2f13c612ac 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2213,7 +2213,7 @@ static int sequencer_strip_jump_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_sequencer/sequencer_proxy.c b/source/blender/editors/space_sequencer/sequencer_proxy.c index 0a8eb7cb88f..fb561025da2 100644 --- a/source/blender/editors/space_sequencer/sequencer_proxy.c +++ b/source/blender/editors/space_sequencer/sequencer_proxy.c @@ -85,7 +85,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports) } bool success = SEQ_proxy_rebuild_context( - pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue); + pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue, false); if (!success && (seq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) != 0) { BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping", seq->name); @@ -137,7 +137,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) short stop = 0, do_update; float progress; - SEQ_proxy_rebuild_context(bmain, depsgraph, scene, seq, file_list, &queue); + SEQ_proxy_rebuild_context(bmain, depsgraph, scene, seq, file_list, &queue, false); for (link = queue.first; link; link = link->next) { struct SeqIndexBuildContext *context = link->data; diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index b93f421ff5c..b294fdf4820 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -39,6 +39,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_lib_id.h" +#include "BKE_lib_remap.h" #include "BKE_screen.h" #include "BKE_sequencer_offscreen.h" @@ -988,19 +989,12 @@ static void sequencer_buttons_region_listener(const wmRegionListenerParams *para } } -static void sequencer_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void sequencer_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) { SpaceSeq *sseq = (SpaceSeq *)slink; - - if (!ELEM(GS(old_id->name), ID_GD)) { - return; - } - - if ((ID *)sseq->gpd == old_id) { - sseq->gpd = (bGPdata *)new_id; - id_us_min(old_id); - id_us_plus(new_id); - } + BKE_id_remapper_apply(mappings, (ID **)&sseq->gpd, ID_REMAP_APPLY_DEFAULT); } /* ************************************* */ diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 02f7f1d71c4..357b3e0a8b4 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -18,6 +18,7 @@ #include "BLI_listbase.h" +#include "BKE_lib_remap.h" #include "BKE_screen.h" #include "ED_screen.h" @@ -171,21 +172,23 @@ static void spreadsheet_keymap(wmKeyConfig *keyconf) 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) +static void spreadsheet_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const IDRemapper *mappings) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink; LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { - if (context->type == SPREADSHEET_CONTEXT_OBJECT) { - SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context; - if ((ID *)object_context->object == old_id) { - if (new_id && GS(new_id->name) == ID_OB) { - object_context->object = (Object *)new_id; - } - else { - object_context->object = nullptr; - } - } + if (context->type != SPREADSHEET_CONTEXT_OBJECT) { + continue; } + SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context; + + if (object_context->object != nullptr && GS(object_context->object->id.name) != ID_OB) { + object_context->object = nullptr; + continue; + } + + BKE_id_remapper_apply(mappings, ((ID **)&object_context->object), ID_REMAP_APPLY_DEFAULT); } } @@ -551,7 +554,7 @@ static void spreadsheet_footer_region_draw(const bContext *C, ARegion *region) UI_LAYOUT_HEADER, UI_HEADER_OFFSET, region->winy - (region->winy - UI_UNIT_Y) / 2.0f, - region->sizex, + region->winx, 1, 0, style); 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 b9b03732a40..83302f94c85 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -474,6 +474,11 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, r_fields.add("Viewer", std::move(field)); } } + if (const geo_log::GenericValueLog *generic_value_log = + dynamic_cast<const geo_log::GenericValueLog *>(value_log)) { + fn::GPointer value = generic_value_log->value(); + r_fields.add("Viewer", fn::make_constant_field(*value.type(), value.get())); + } } } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index f4b5ff819ed..d9837b7c1a6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -127,6 +127,28 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); } + if (data.type().is<int8_t>()) { + const int8_t value = data.get<int8_t>(real_index); + const std::string value_str = std::to_string(value); + uiBut *but = uiDefIconTextBut(params.block, + UI_BTYPE_LABEL, + 0, + ICON_NONE, + value_str.c_str(), + params.xmin, + params.ymin, + params.width, + params.height, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + /* Right-align Integers. */ + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); + UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); + } else if (data.type().is<float>()) { const float value = data.get<float>(real_index); std::stringstream ss; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc index 56722104b4f..c409defd3f4 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -243,7 +243,7 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel) uiItemR(layout, filter_ptr, "value_string", 0, IFACE_("Value"), ICON_NONE); break; case SPREADSHEET_VALUE_TYPE_UNKNOWN: - uiItemL(layout, IFACE_("Unkown column type"), ICON_ERROR); + uiItemL(layout, IFACE_("Unknown column type"), ICON_ERROR); break; } } diff --git a/source/blender/editors/space_text/CMakeLists.txt b/source/blender/editors/space_text/CMakeLists.txt index abd7620ea2b..a85c69caa50 100644 --- a/source/blender/editors/space_text/CMakeLists.txt +++ b/source/blender/editors/space_text/CMakeLists.txt @@ -61,8 +61,5 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() blender_add_lib(bf_editor_space_text "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index f449ce50ae3..7339d8248c8 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -32,6 +32,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_lib_id.h" +#include "BKE_lib_remap.h" #include "BKE_screen.h" #include "ED_screen.h" @@ -401,18 +402,12 @@ static void text_properties_region_draw(const bContext *C, ARegion *region) } } -static void text_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id) +static void text_id_remap(ScrArea *UNUSED(area), + SpaceLink *slink, + const struct IDRemapper *mappings) { SpaceText *stext = (SpaceText *)slink; - - if (!ELEM(GS(old_id->name), ID_TXT)) { - return; - } - - if ((ID *)stext->text == old_id) { - stext->text = (Text *)new_id; - id_us_ensure_real(new_id); - } + BKE_id_remapper_apply(mappings, (ID **)&stext->text, ID_REMAP_APPLY_ENSURE_REAL); } /********************* registration ********************/ diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 8fb55ed9b46..27941b881b8 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -79,10 +79,8 @@ static void text_font_end(const TextDrawContext *UNUSED(tdc)) static int text_font_draw(const TextDrawContext *tdc, int x, int y, const char *str) { - int columns; - BLF_position(tdc->font_id, x, y, 0); - columns = BLF_draw_mono(tdc->font_id, str, BLF_DRAW_STR_DUMMY_MAX, tdc->cwidth_px); + const int columns = BLF_draw_mono(tdc->font_id, str, BLF_DRAW_STR_DUMMY_MAX, tdc->cwidth_px); return tdc->cwidth_px * columns; } @@ -90,18 +88,17 @@ static int text_font_draw(const TextDrawContext *tdc, int x, int y, const char * static int text_font_draw_character(const TextDrawContext *tdc, int x, int y, char c) { BLF_position(tdc->font_id, x, y, 0); - BLF_draw(tdc->font_id, &c, 1); + BLF_draw_mono(tdc->font_id, &c, 1, tdc->cwidth_px); return tdc->cwidth_px; } -static int text_font_draw_character_utf8(const TextDrawContext *tdc, int x, int y, const char *c) +static int text_font_draw_character_utf8( + const TextDrawContext *tdc, int x, int y, const char *c, const int c_len) { - int columns; - - const size_t len = BLI_str_utf8_size_safe(c); + BLI_assert(c_len == BLI_str_utf8_size_safe(c)); BLF_position(tdc->font_id, x, y, 0); - columns = BLF_draw_mono(tdc->font_id, c, len, tdc->cwidth_px); + const int columns = BLF_draw_mono(tdc->font_id, c, c_len, tdc->cwidth_px); return tdc->cwidth_px * columns; } @@ -463,13 +460,15 @@ static int text_draw_wrapped(const SpaceText *st, } /* Draw the visible portion of text on the overshot line */ - for (a = fstart, ma = mstart; ma < mend; a++, ma += BLI_str_utf8_size_safe(str + ma)) { + for (a = fstart, ma = mstart; ma < mend; a++) { if (use_syntax) { if (fmt_prev != format[a]) { format_draw_color(tdc, fmt_prev = format[a]); } } - x += text_font_draw_character_utf8(tdc, x, y, str + ma); + const int c_len = BLI_str_utf8_size_safe(str + ma); + x += text_font_draw_character_utf8(tdc, x, y, str + ma, c_len); + ma += c_len; fpos++; } y -= TXT_LINE_HEIGHT(st); @@ -491,15 +490,16 @@ static int text_draw_wrapped(const SpaceText *st, } /* Draw the remaining text */ - for (a = fstart, ma = mstart; str[ma] && y > clip_min_y; - a++, ma += BLI_str_utf8_size_safe(str + ma)) { + for (a = fstart, ma = mstart; str[ma] && y > clip_min_y; a++) { if (use_syntax) { if (fmt_prev != format[a]) { format_draw_color(tdc, fmt_prev = format[a]); } } - x += text_font_draw_character_utf8(tdc, x, y, str + ma); + const int c_len = BLI_str_utf8_size_safe(str + ma); + x += text_font_draw_character_utf8(tdc, x, y, str + ma, c_len); + ma += c_len; } flatten_string_free(&fs); @@ -559,8 +559,9 @@ static void text_draw(const SpaceText *st, if (format[a] != fmt_prev) { format_draw_color(tdc, fmt_prev = format[a]); } - x += text_font_draw_character_utf8(tdc, x, y, in + str_shift); - str_shift += BLI_str_utf8_size_safe(in + str_shift); + const int c_len = BLI_str_utf8_size_safe(in + str_shift); + x += text_font_draw_character_utf8(tdc, x, y, in + str_shift, c_len); + str_shift += c_len; } } else { diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c index d40229332fd..e4c11bc8668 100644 --- a/source/blender/editors/space_userpref/userpref_ops.c +++ b/source/blender/editors/space_userpref/userpref_ops.c @@ -30,6 +30,7 @@ #ifdef WIN32 # include "BLI_winstuff.h" #endif +#include "BLI_path_util.h" #include "BKE_context.h" #include "BKE_main.h" @@ -142,16 +143,20 @@ static void PREFERENCES_OT_autoexec_path_remove(wmOperatorType *ot) static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *op) { - char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL); + char *path = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL); + char dirname[FILE_MAXFILE]; + + BLI_path_slash_rstrip(path); + BLI_split_file_part(path, dirname, sizeof(dirname)); /* NULL is a valid directory path here. A library without path will be created then. */ - BKE_preferences_asset_library_add(&U, NULL, directory); + BKE_preferences_asset_library_add(&U, dirname, path); U.runtime.is_dirty = true; /* There's no dedicated notifier for the Preferences. */ WM_main_add_notifier(NC_WINDOW, NULL); - MEM_freeN(directory); + MEM_freeN(path); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 19f869ed50b..7e8b013192f 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -60,8 +60,17 @@ set(SRC view3d_gizmo_tool_generic.c view3d_header.c view3d_iterators.c + view3d_navigate.c + view3d_navigate_dolly.c view3d_navigate_fly.c + view3d_navigate_move.c + view3d_navigate_ndof.c + view3d_navigate_roll.c + view3d_navigate_rotate.c + view3d_navigate_smoothview.c view3d_navigate_walk.c + view3d_navigate_zoom.c + view3d_navigate_zoom_border.c view3d_ops.c view3d_placement.c view3d_project.c @@ -71,6 +80,7 @@ set(SRC view3d_view.c view3d_intern.h + view3d_navigate.h ) set(LIB @@ -83,11 +93,6 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() - -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 8822ea6af3b..51107499d3f 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -48,6 +48,7 @@ #include "BKE_idprop.h" #include "BKE_lattice.h" #include "BKE_layer.h" +#include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_mball.h" #include "BKE_mesh.h" @@ -88,6 +89,7 @@ #include "DEG_depsgraph_build.h" #include "view3d_intern.h" /* own include */ +#include "view3d_navigate.h" /* ******************** manage regions ********************* */ @@ -242,7 +244,6 @@ void ED_view3d_shade_update(Main *bmain, View3D *v3d, ScrArea *area) for (region = area->regionbase.first; region; region = region->next) { if ((region->regiontype == RGN_TYPE_WINDOW) && region->regiondata) { ED_view3d_stop_render_preview(wm, region); - break; } } } @@ -1813,50 +1814,54 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes return CTX_RESULT_MEMBER_NOT_FOUND; } -static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id) +static void view3d_id_remap_v3d_ob_centers(View3D *v3d, const struct IDRemapper *mappings) { - View3D *v3d; - ARegion *region; - bool is_local = false; - - if (!ELEM(GS(old_id->name), ID_OB, ID_MA, ID_IM, ID_MC)) { - return; + if (BKE_id_remapper_apply(mappings, (ID **)&v3d->ob_center, ID_REMAP_APPLY_DEFAULT) == + ID_REMAP_RESULT_SOURCE_UNASSIGNED) { + /* Otherwise, bonename may remain valid... + * We could be smart and check this, too? */ + v3d->ob_center_bone[0] = '\0'; } +} - for (v3d = (View3D *)slink; v3d; v3d = v3d->localvd, is_local = true) { - if ((ID *)v3d->camera == old_id) { - v3d->camera = (Object *)new_id; - if (!new_id) { - /* 3D view might be inactive, in that case needs to use slink->regionbase */ - ListBase *regionbase = (slink == area->spacedata.first) ? &area->regionbase : - &slink->regionbase; - for (region = regionbase->first; region; region = region->next) { - if (region->regiontype == RGN_TYPE_WINDOW) { - RegionView3D *rv3d = is_local ? ((RegionView3D *)region->regiondata)->localvd : - region->regiondata; - if (rv3d && (rv3d->persp == RV3D_CAMOB)) { - rv3d->persp = RV3D_PERSP; - } - } +static void view3d_id_remap_v3d(ScrArea *area, + SpaceLink *slink, + View3D *v3d, + const struct IDRemapper *mappings, + const bool is_local) +{ + ARegion *region; + if (BKE_id_remapper_apply(mappings, (ID **)&v3d->camera, ID_REMAP_APPLY_DEFAULT) == + ID_REMAP_RESULT_SOURCE_UNASSIGNED) { + /* 3D view might be inactive, in that case needs to use slink->regionbase */ + ListBase *regionbase = (slink == area->spacedata.first) ? &area->regionbase : + &slink->regionbase; + for (region = regionbase->first; region; region = region->next) { + if (region->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = is_local ? ((RegionView3D *)region->regiondata)->localvd : + region->regiondata; + if (rv3d && (rv3d->persp == RV3D_CAMOB)) { + rv3d->persp = RV3D_PERSP; } } } + } +} - /* Values in local-view aren't used, see: T52663 */ - if (is_local == false) { - if ((ID *)v3d->ob_center == old_id) { - v3d->ob_center = (Object *)new_id; - /* Otherwise, bonename may remain valid... - * We could be smart and check this, too? */ - if (new_id == NULL) { - v3d->ob_center_bone[0] = '\0'; - } - } - } +static void view3d_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRemapper *mappings) +{ - if (is_local) { - break; - } + if (!BKE_id_remapper_has_mapping_for( + mappings, FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_IM | FILTER_ID_MC)) { + return; + } + + View3D *view3d = (View3D *)slink; + view3d_id_remap_v3d(area, slink, view3d, mappings, false); + view3d_id_remap_v3d_ob_centers(view3d, mappings); + if (view3d->localvd != NULL) { + /* Object centers in local-view aren't used, see: T52663 */ + view3d_id_remap_v3d(area, slink, view3d->localvd, mappings, true); } } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 243d4033cbc..bfc18eed847 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1764,7 +1764,7 @@ static void view3d_panel_transform(const bContext *C, Panel *panel) v3d_transform_butsR(col, &obptr); /* Dimensions and editmode are mostly the same check. */ - if (OB_TYPE_SUPPORT_EDITMODE(ob->type) || ELEM(ob->type, OB_VOLUME, OB_HAIR, OB_POINTCLOUD)) { + if (OB_TYPE_SUPPORT_EDITMODE(ob->type) || ELEM(ob->type, OB_VOLUME, OB_CURVES, OB_POINTCLOUD)) { View3D *v3d = CTX_wm_view3d(C); v3d_object_dimension_buts(NULL, col, v3d, ob); } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index b1f19581543..3f639e8ee1a 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1036,7 +1036,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d) negate_v3_v3(o, rv3d->ofs); GPU_blend(GPU_BLEND_ALPHA); - GPU_depth_mask(false); /* don't overwrite zbuf */ + GPU_depth_mask(false); /* Don't overwrite the Z-buffer. */ GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -1258,11 +1258,7 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y /* 6 is the maximum size of the axis roll text. */ /* increase size for unicode languages (Chinese in utf-8...) */ -#ifdef WITH_INTERNATIONAL char tmpstr[96 + 6]; -#else - char tmpstr[32 + 6]; -#endif BLF_enable(font_id, BLF_SHADOW); BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f}); @@ -1689,9 +1685,9 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, G.f |= G_FLAG_RENDER_VIEWPORT; { - /* free images which can have changed on frame-change - * warning! can be slow so only free animated images - campbell */ - BKE_image_free_anim_gputextures(G.main); /* XXX :((( */ + /* Free images which can have changed on frame-change. + * WARNING(@campbellbarton): can be slow so only free animated images. */ + BKE_image_free_anim_gputextures(G.main); } GPU_matrix_push_projection(); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 80089815284..4c7a7cb4c61 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -23,70 +23,39 @@ * 3D view manipulation/operators. */ -#include <float.h> -#include <math.h> -#include <stdio.h> -#include <string.h> - #include "DNA_armature_types.h" #include "DNA_camera_types.h" -#include "DNA_curve_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" #include "DNA_world_types.h" #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" -#include "BLI_dial_2d.h" #include "BLI_math.h" -#include "BLI_utildefines.h" #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_camera.h" -#include "BKE_context.h" -#include "BKE_gpencil_geom.h" -#include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_object.h" -#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" -#include "BKE_vfont.h" -#include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" #include "WM_api.h" #include "WM_message.h" -#include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" -#include "ED_armature.h" -#include "ED_mesh.h" -#include "ED_particle.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_transform_snap_object_context.h" -#include "ED_view3d.h" - -#include "UI_resources.h" - -#include "PIL_time.h" #include "view3d_intern.h" /* own include */ -enum { - HAS_TRANSLATE = (1 << 0), - HAS_ROTATE = (1 << 0), -}; - /* test for unlocked camera view in quad view */ static bool view3d_camera_user_poll(bContext *C) { @@ -115,3052 +84,6 @@ static bool view3d_lock_poll(bContext *C) return false; } -static bool view3d_pan_poll(bContext *C) -{ - if (ED_operator_region_view3d_active(C)) { - const RegionView3D *rv3d = CTX_wm_region_view3d(C); - return !(RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_LOCATION); - } - return false; -} - -static bool view3d_zoom_or_dolly_poll(bContext *C) -{ - if (ED_operator_region_view3d_active(C)) { - const RegionView3D *rv3d = CTX_wm_region_view3d(C); - return !(RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ZOOM_AND_DOLLY); - } - return false; -} - -/* -------------------------------------------------------------------- */ -/** \name Generic View Operator Properties - * \{ */ - -enum eV3D_OpPropFlag { - V3D_OP_PROP_MOUSE_CO = (1 << 0), - V3D_OP_PROP_DELTA = (1 << 1), - V3D_OP_PROP_USE_ALL_REGIONS = (1 << 2), - V3D_OP_PROP_USE_MOUSE_INIT = (1 << 3), -}; - -static void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPropFlag flag) -{ - if (flag & V3D_OP_PROP_MOUSE_CO) { - PropertyRNA *prop; - prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Region Position X", "", 0, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN); - prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Region Position Y", "", 0, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN); - } - if (flag & V3D_OP_PROP_DELTA) { - RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX); - } - if (flag & V3D_OP_PROP_USE_ALL_REGIONS) { - PropertyRNA *prop; - prop = RNA_def_boolean( - ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - } - if (flag & V3D_OP_PROP_USE_MOUSE_INIT) { - WM_operator_properties_use_cursor_init(ot); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Generic View Operator Custom-Data - * \{ */ - -typedef struct ViewOpsData { - /** Context pointers (assigned by #viewops_data_alloc). */ - Main *bmain; - Scene *scene; - ScrArea *area; - ARegion *region; - View3D *v3d; - RegionView3D *rv3d; - Depsgraph *depsgraph; - - /** Needed for continuous zoom. */ - wmTimer *timer; - - /** Viewport state on initialization, don't change afterwards. */ - struct { - float dist; - float camzoom; - float quat[4]; - /** #wmEvent.xy. */ - int event_xy[2]; - /** Offset to use when #VIEWOPS_FLAG_USE_MOUSE_INIT is not set. - * so we can simulate pressing in the middle of the screen. */ - int event_xy_offset[2]; - /** #wmEvent.type that triggered the operator. */ - int event_type; - float ofs[3]; - /** Initial distance to 'ofs'. */ - float zfac; - - /** Trackball rotation only. */ - float trackvec[3]; - /** Dolly only. */ - float mousevec[3]; - - /** - * #RegionView3D.persp set after auto-perspective is applied. - * If we want the value before running the operator, add a separate member. - */ - char persp; - - /** Used for roll */ - Dial *dial; - } init; - - /** Previous state (previous modal event handled). */ - struct { - int event_xy[2]; - /** For operators that use time-steps (continuous zoom). */ - double time; - } prev; - - /** Current state. */ - struct { - /** Working copy of #RegionView3D.viewquat, needed for rotation calculation - * so we can apply snap to the 3D Viewport while keeping the unsnapped rotation - * here to use when snap is disabled and for continued calculation. */ - float viewquat[4]; - } curr; - - float reverse; - bool axis_snap; /* view rotate only */ - - /** Use for orbit selection and auto-dist. */ - float dyn_ofs[3]; - bool use_dyn_ofs; -} ViewOpsData; - -/** - * Size of the sphere being dragged for trackball rotation within the view bounds. - * also affects speed (smaller is faster). - */ -#define TRACKBALLSIZE (1.1f) - -static void calctrackballvec(const rcti *rect, const int event_xy[2], float r_dir[3]) -{ - const float radius = TRACKBALLSIZE; - const float t = radius / (float)M_SQRT2; - const float size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)}; - /* Aspect correct so dragging in a non-square view doesn't squash the direction. - * So diagonal motion rotates the same direction the cursor is moving. */ - const float size_min = min_ff(size[0], size[1]); - const float aspect[2] = {size_min / size[0], size_min / size[1]}; - - /* Normalize x and y. */ - r_dir[0] = (event_xy[0] - BLI_rcti_cent_x(rect)) / ((size[0] * aspect[0]) / 2.0); - r_dir[1] = (event_xy[1] - BLI_rcti_cent_y(rect)) / ((size[1] * aspect[1]) / 2.0); - const float d = len_v2(r_dir); - if (d < t) { - /* Inside sphere. */ - r_dir[2] = sqrtf(square_f(radius) - square_f(d)); - } - else { - /* On hyperbola. */ - r_dir[2] = square_f(t) / d; - } -} - -/** - * Allocate and fill in context pointers for #ViewOpsData - */ -static void viewops_data_alloc(bContext *C, wmOperator *op) -{ - ViewOpsData *vod = MEM_callocN(sizeof(ViewOpsData), "viewops data"); - - /* store data */ - op->customdata = vod; - vod->bmain = CTX_data_main(C); - vod->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - vod->scene = CTX_data_scene(C); - vod->area = CTX_wm_area(C); - vod->region = CTX_wm_region(C); - vod->v3d = vod->area->spacedata.first; - vod->rv3d = vod->region->regiondata; -} - -void view3d_orbit_apply_dyn_ofs(float r_ofs[3], - const float ofs_old[3], - const float viewquat_old[4], - const float viewquat_new[4], - const float dyn_ofs[3]) -{ - float q[4]; - invert_qt_qt_normalized(q, viewquat_old); - mul_qt_qtqt(q, q, viewquat_new); - - invert_qt_normalized(q); - - sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs); - mul_qt_v3(q, r_ofs); - add_v3_v3(r_ofs, dyn_ofs); -} - -static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3]) -{ - static float lastofs[3] = {0, 0, 0}; - bool is_set = false; - - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); - View3D *v3d = CTX_wm_view3d(C); - Object *ob_act_eval = OBACT(view_layer_eval); - Object *ob_act = DEG_get_original_object(ob_act_eval); - - if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) && - /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */ - ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0) { - /* in case of sculpting use last average stroke position as a rotation - * center, in other cases it's not clear what rotation center shall be - * so just rotate around object origin - */ - if (ob_act->mode & - (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { - float stroke[3]; - BKE_paint_stroke_get_average(scene, ob_act_eval, stroke); - copy_v3_v3(lastofs, stroke); - } - else { - copy_v3_v3(lastofs, ob_act_eval->obmat[3]); - } - is_set = true; - } - else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) { - Curve *cu = ob_act_eval->data; - EditFont *ef = cu->editfont; - - zero_v3(lastofs); - for (int i = 0; i < 4; i++) { - add_v2_v2(lastofs, ef->textcurs[i]); - } - mul_v2_fl(lastofs, 1.0f / 4.0f); - - mul_m4_v3(ob_act_eval->obmat, lastofs); - - is_set = true; - } - else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) { - /* object mode use boundbox centers */ - Base *base_eval; - uint tot = 0; - float select_center[3]; - - zero_v3(select_center); - for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) { - if (BASE_SELECTED(v3d, base_eval)) { - /* use the boundbox if we can */ - Object *ob_eval = base_eval->object; - - if (ob_eval->runtime.bb && !(ob_eval->runtime.bb->flag & BOUNDBOX_DIRTY)) { - float cent[3]; - - BKE_boundbox_calc_center_aabb(ob_eval->runtime.bb, cent); - - mul_m4_v3(ob_eval->obmat, cent); - add_v3_v3(select_center, cent); - } - else { - add_v3_v3(select_center, ob_eval->obmat[3]); - } - tot++; - } - } - if (tot) { - mul_v3_fl(select_center, 1.0f / (float)tot); - copy_v3_v3(lastofs, select_center); - is_set = true; - } - } - else { - /* If there's no selection, lastofs is unmodified and last value since static */ - is_set = calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, lastofs, NULL); - } - - copy_v3_v3(r_dyn_ofs, lastofs); - - return is_set; -} - -enum eViewOpsFlag { - /** When enabled, rotate around the selection. */ - VIEWOPS_FLAG_ORBIT_SELECT = (1 << 0), - /** When enabled, use the depth under the cursor for navigation. */ - VIEWOPS_FLAG_DEPTH_NAVIGATE = (1 << 1), - /** - * When enabled run #ED_view3d_persp_ensure this may switch out of camera view - * when orbiting or switch from orthographic to perspective when auto-perspective is enabled. - * Some operations don't require this (view zoom/pan or NDOF where subtle rotation is common - * so we don't want it to trigger auto-perspective). */ - VIEWOPS_FLAG_PERSP_ENSURE = (1 << 2), - /** When set, ignore any options that depend on initial cursor location. */ - VIEWOPS_FLAG_USE_MOUSE_INIT = (1 << 3), -}; - -static enum eViewOpsFlag viewops_flag_from_args(bool use_select, bool use_depth) -{ - enum eViewOpsFlag flag = 0; - if (use_select) { - flag |= VIEWOPS_FLAG_ORBIT_SELECT; - } - if (use_depth) { - flag |= VIEWOPS_FLAG_DEPTH_NAVIGATE; - } - - return flag; -} - -static enum eViewOpsFlag viewops_flag_from_prefs(void) -{ - return viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0, - (U.uiflag & USER_DEPTH_NAVIGATE) != 0); -} - -/** - * Calculate the values for #ViewOpsData - */ -static void viewops_data_create(bContext *C, - wmOperator *op, - const wmEvent *event, - enum eViewOpsFlag viewops_flag) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewOpsData *vod = op->customdata; - RegionView3D *rv3d = vod->rv3d; - - /* Could do this more nicely. */ - if ((viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) == 0) { - viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE; - } - - /* we need the depth info before changing any viewport options */ - if (viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE) { - float fallback_depth_pt[3]; - - view3d_operator_needs_opengl(C); /* needed for zbuf drawing */ - - negate_v3_v3(fallback_depth_pt, rv3d->ofs); - - vod->use_dyn_ofs = ED_view3d_autodist( - depsgraph, vod->region, vod->v3d, event->mval, vod->dyn_ofs, true, fallback_depth_pt); - } - else { - vod->use_dyn_ofs = false; - } - - if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) { - if (ED_view3d_persp_ensure(depsgraph, vod->v3d, vod->region)) { - /* If we're switching from camera view to the perspective one, - * need to tag viewport update, so camera view and borders are properly updated. */ - ED_region_tag_redraw(vod->region); - } - } - - /* set the view from the camera, if view locking is enabled. - * we may want to make this optional but for now its needed always */ - ED_view3d_camera_lock_init(depsgraph, vod->v3d, vod->rv3d); - - vod->init.persp = rv3d->persp; - vod->init.dist = rv3d->dist; - vod->init.camzoom = rv3d->camzoom; - copy_qt_qt(vod->init.quat, rv3d->viewquat); - copy_v2_v2_int(vod->init.event_xy, event->xy); - copy_v2_v2_int(vod->prev.event_xy, event->xy); - - if (viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) { - zero_v2_int(vod->init.event_xy_offset); - } - else { - /* Simulate the event starting in the middle of the region. */ - vod->init.event_xy_offset[0] = BLI_rcti_cent_x(&vod->region->winrct) - event->xy[0]; - vod->init.event_xy_offset[1] = BLI_rcti_cent_y(&vod->region->winrct) - event->xy[1]; - } - - vod->init.event_type = event->type; - copy_v3_v3(vod->init.ofs, rv3d->ofs); - - copy_qt_qt(vod->curr.viewquat, rv3d->viewquat); - - if (viewops_flag & VIEWOPS_FLAG_ORBIT_SELECT) { - float ofs[3]; - if (view3d_orbit_calc_center(C, ofs) || (vod->use_dyn_ofs == false)) { - vod->use_dyn_ofs = true; - negate_v3_v3(vod->dyn_ofs, ofs); - viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE; - } - } - - if (viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE) { - if (vod->use_dyn_ofs) { - if (rv3d->is_persp) { - float my_origin[3]; /* original G.vd->ofs */ - float my_pivot[3]; /* view */ - float dvec[3]; - - /* locals for dist correction */ - float mat[3][3]; - float upvec[3]; - - 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 won't go bad when dist was zero. */ - - /* remove dist value */ - upvec[0] = upvec[1] = 0; - upvec[2] = rv3d->dist; - copy_m3_m4(mat, rv3d->viewinv); - - mul_m3_v3(mat, upvec); - sub_v3_v3v3(my_pivot, rv3d->ofs, upvec); - negate_v3(my_pivot); /* ofs is flipped */ - - /* find a new ofs value that is along the view axis - * (rather than the mouse location) */ - closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin); - vod->init.dist = rv3d->dist = len_v3v3(my_pivot, dvec); - - negate_v3_v3(rv3d->ofs, dvec); - } - else { - const float mval_region_mid[2] = {(float)vod->region->winx / 2.0f, - (float)vod->region->winy / 2.0f}; - - ED_view3d_win_to_3d(vod->v3d, vod->region, vod->dyn_ofs, mval_region_mid, rv3d->ofs); - negate_v3(rv3d->ofs); - } - negate_v3(vod->dyn_ofs); - copy_v3_v3(vod->init.ofs, rv3d->ofs); - } - } - - /* For dolly */ - ED_view3d_win_to_vector(vod->region, (const float[2]){UNPACK2(event->mval)}, vod->init.mousevec); - - { - int event_xy_offset[2]; - add_v2_v2v2_int(event_xy_offset, event->xy, vod->init.event_xy_offset); - - /* For rotation with trackball rotation. */ - calctrackballvec(&vod->region->winrct, event_xy_offset, vod->init.trackvec); - } - - { - float tvec[3]; - negate_v3_v3(tvec, rv3d->ofs); - vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL); - } - - vod->reverse = 1.0f; - if (rv3d->persmat[2][1] < 0.0f) { - vod->reverse = -1.0f; - } - - rv3d->rflag |= RV3D_NAVIGATING; -} - -static void viewops_data_free(bContext *C, wmOperator *op) -{ - ARegion *region; - if (op->customdata) { - ViewOpsData *vod = op->customdata; - region = vod->region; - vod->rv3d->rflag &= ~RV3D_NAVIGATING; - - if (vod->timer) { - WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer); - } - - if (vod->init.dial) { - MEM_freeN(vod->init.dial); - } - - MEM_freeN(vod); - op->customdata = NULL; - } - else { - region = CTX_wm_region(C); - } - - /* Need to redraw because drawing code uses RV3D_NAVIGATING to draw - * faster while navigation operator runs. */ - ED_region_tag_redraw(region); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Rotate Operator - * \{ */ - -enum { - VIEW_PASS = 0, - VIEW_APPLY, - VIEW_CONFIRM, -}; - -/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */ -enum { - VIEW_MODAL_CONFIRM = 1, /* used for all view operations */ - VIEWROT_MODAL_AXIS_SNAP_ENABLE = 2, - VIEWROT_MODAL_AXIS_SNAP_DISABLE = 3, - VIEWROT_MODAL_SWITCH_ZOOM = 4, - VIEWROT_MODAL_SWITCH_MOVE = 5, - VIEWROT_MODAL_SWITCH_ROTATE = 6, -}; - -void viewrotate_modal_keymap(wmKeyConfig *keyconf) -{ - static const EnumPropertyItem modal_items[] = { - {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, - - {VIEWROT_MODAL_AXIS_SNAP_ENABLE, "AXIS_SNAP_ENABLE", 0, "Axis Snap", ""}, - {VIEWROT_MODAL_AXIS_SNAP_DISABLE, "AXIS_SNAP_DISABLE", 0, "Axis Snap (Off)", ""}, - - {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"}, - {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"}, - - {0, NULL, 0, NULL, NULL}, - }; - - wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Rotate Modal"); - - /* this function is called for each spacetype, only needs to add map once */ - if (keymap && keymap->modal_items) { - return; - } - - keymap = WM_modalkeymap_ensure(keyconf, "View3D Rotate Modal", modal_items); - - /* disabled mode switching for now, can re-implement better, later on */ -#if 0 - WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); - WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); - WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); -#endif - - /* assign map to operators */ - WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate"); -} - -static void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4]) -{ - if (vod->use_dyn_ofs) { - RegionView3D *rv3d = vod->rv3d; - view3d_orbit_apply_dyn_ofs( - rv3d->ofs, vod->init.ofs, vod->init.quat, viewquat_new, vod->dyn_ofs); - } -} - -static void viewrotate_apply_snap(ViewOpsData *vod) -{ - const float axis_limit = DEG2RADF(45 / 3); - - RegionView3D *rv3d = vod->rv3d; - - float viewquat_inv[4]; - float zaxis[3] = {0, 0, 1}; - float zaxis_best[3]; - int x, y, z; - bool found = false; - - invert_qt_qt_normalized(viewquat_inv, vod->curr.viewquat); - - mul_qt_v3(viewquat_inv, zaxis); - normalize_v3(zaxis); - - for (x = -1; x < 2; x++) { - for (y = -1; y < 2; y++) { - for (z = -1; z < 2; z++) { - if (x || y || z) { - float zaxis_test[3] = {x, y, z}; - - normalize_v3(zaxis_test); - - if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) { - copy_v3_v3(zaxis_best, zaxis_test); - found = true; - } - } - } - } - } - - if (found) { - - /* find the best roll */ - float quat_roll[4], quat_final[4], quat_best[4], quat_snap[4]; - float viewquat_align[4]; /* viewquat aligned to zaxis_best */ - float viewquat_align_inv[4]; /* viewquat aligned to zaxis_best */ - float best_angle = axis_limit; - int j; - - /* viewquat_align is the original viewquat aligned to the snapped axis - * for testing roll */ - rotation_between_vecs_to_quat(viewquat_align, zaxis_best, zaxis); - normalize_qt(viewquat_align); - mul_qt_qtqt(viewquat_align, vod->curr.viewquat, viewquat_align); - normalize_qt(viewquat_align); - invert_qt_qt_normalized(viewquat_align_inv, viewquat_align); - - vec_to_quat(quat_snap, zaxis_best, OB_NEGZ, OB_POSY); - normalize_qt(quat_snap); - invert_qt_normalized(quat_snap); - - /* check if we can find the roll */ - found = false; - - /* find best roll */ - for (j = 0; j < 8; j++) { - float angle; - float xaxis1[3] = {1, 0, 0}; - float xaxis2[3] = {1, 0, 0}; - float quat_final_inv[4]; - - axis_angle_to_quat(quat_roll, zaxis_best, (float)j * DEG2RADF(45.0f)); - normalize_qt(quat_roll); - - mul_qt_qtqt(quat_final, quat_snap, quat_roll); - normalize_qt(quat_final); - - /* compare 2 vector angles to find the least roll */ - invert_qt_qt_normalized(quat_final_inv, quat_final); - mul_qt_v3(viewquat_align_inv, xaxis1); - mul_qt_v3(quat_final_inv, xaxis2); - angle = angle_v3v3(xaxis1, xaxis2); - - if (angle <= best_angle) { - found = true; - best_angle = angle; - copy_qt_qt(quat_best, quat_final); - } - } - - if (found) { - /* lock 'quat_best' to an axis view if we can */ - ED_view3d_quat_to_axis_view(quat_best, 0.01f, &rv3d->view, &rv3d->view_axis_roll); - if (rv3d->view != RV3D_VIEW_USER) { - ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_best); - } - } - else { - copy_qt_qt(quat_best, viewquat_align); - } - - copy_qt_qt(rv3d->viewquat, quat_best); - - viewrotate_apply_dyn_ofs(vod, rv3d->viewquat); - - if (U.uiflag & USER_AUTOPERSP) { - if (RV3D_VIEW_IS_AXIS(rv3d->view)) { - if (rv3d->persp == RV3D_PERSP) { - rv3d->persp = RV3D_ORTHO; - } - } - } - } - else if (U.uiflag & USER_AUTOPERSP) { - rv3d->persp = vod->init.persp; - } -} - -static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2]) -{ - RegionView3D *rv3d = vod->rv3d; - - rv3d->view = RV3D_VIEW_USER; /* need to reset every time because of view snapping */ - - if (U.flag & USER_TRACKBALL) { - float axis[3], q1[4], dvec[3], newvec[3]; - float angle; - - { - const int event_xy_offset[2] = { - event_xy[0] + vod->init.event_xy_offset[0], - event_xy[1] + vod->init.event_xy_offset[1], - }; - calctrackballvec(&vod->region->winrct, event_xy_offset, newvec); - } - - sub_v3_v3v3(dvec, newvec, vod->init.trackvec); - - angle = (len_v3(dvec) / (2.0f * TRACKBALLSIZE)) * (float)M_PI; - - /* Before applying the sensitivity this is rotating 1:1, - * where the cursor would match the surface of a sphere in the view. */ - angle *= U.view_rotate_sensitivity_trackball; - - /* Allow for rotation beyond the interval [-pi, pi] */ - angle = angle_wrap_rad(angle); - - /* This relation is used instead of the actual angle between vectors - * so that the angle of rotation is linearly proportional to - * the distance that the mouse is dragged. */ - - cross_v3_v3v3(axis, vod->init.trackvec, newvec); - axis_angle_to_quat(q1, axis, angle); - - mul_qt_qtqt(vod->curr.viewquat, q1, vod->init.quat); - - viewrotate_apply_dyn_ofs(vod, vod->curr.viewquat); - } - else { - float quat_local_x[4], quat_global_z[4]; - float m[3][3]; - float m_inv[3][3]; - const float zvec_global[3] = {0.0f, 0.0f, 1.0f}; - float xaxis[3]; - - /* Radians per-pixel. */ - const float sensitivity = U.view_rotate_sensitivity_turntable / U.dpi_fac; - - /* Get the 3x3 matrix and its inverse from the quaternion */ - quat_to_mat3(m, vod->curr.viewquat); - invert_m3_m3(m_inv, m); - - /* Avoid Gimbal Lock - * - * Even though turn-table mode is in use, this can occur when the user exits the camera view - * or when aligning the view to a rotated object. - * - * We have gimbal lock when the user's view is rotated +/- 90 degrees along the view axis. - * In this case the vertical rotation is the same as the sideways turntable motion. - * Making it impossible to get out of the gimbal locked state without resetting the view. - * - * The logic below lets the user exit out of this state without any abrupt 'fix' - * which would be disorienting. - * - * This works by blending two horizons: - * - Rotated-horizon: `cross_v3_v3v3(xaxis, zvec_global, m_inv[2])` - * When only this is used, this turntable rotation works - but it's side-ways - * (as if the entire turn-table has been placed on its side) - * While there is no gimbal lock, it's also awkward to use. - * - Un-rotated-horizon: `m_inv[0]` - * When only this is used, the turntable rotation can have gimbal lock. - * - * The solution used here is to blend between these two values, - * so the severity of the gimbal lock is used to blend the rotated horizon. - * Blending isn't essential, it just makes the transition smoother. - * - * This allows sideways turn-table rotation on a Z axis that isn't world-space Z, - * While up-down turntable rotation eventually corrects gimbal lock. */ -#if 1 - if (len_squared_v3v3(zvec_global, m_inv[2]) > 0.001f) { - float fac; - cross_v3_v3v3(xaxis, zvec_global, m_inv[2]); - if (dot_v3v3(xaxis, m_inv[0]) < 0) { - negate_v3(xaxis); - } - fac = angle_normalized_v3v3(zvec_global, m_inv[2]) / (float)M_PI; - fac = fabsf(fac - 0.5f) * 2; - fac = fac * fac; - interp_v3_v3v3(xaxis, xaxis, m_inv[0], fac); - } - else { - copy_v3_v3(xaxis, m_inv[0]); - } -#else - copy_v3_v3(xaxis, m_inv[0]); -#endif - - /* Determine the direction of the x vector (for rotating up and down) */ - /* This can likely be computed directly from the quaternion. */ - - /* Perform the up/down rotation */ - axis_angle_to_quat(quat_local_x, xaxis, sensitivity * -(event_xy[1] - vod->prev.event_xy[1])); - mul_qt_qtqt(quat_local_x, vod->curr.viewquat, quat_local_x); - - /* Perform the orbital rotation */ - axis_angle_to_quat_single( - quat_global_z, 'Z', sensitivity * vod->reverse * (event_xy[0] - vod->prev.event_xy[0])); - mul_qt_qtqt(vod->curr.viewquat, quat_local_x, quat_global_z); - - viewrotate_apply_dyn_ofs(vod, vod->curr.viewquat); - } - - /* avoid precision loss over time */ - normalize_qt(vod->curr.viewquat); - - /* use a working copy so view rotation locking doesn't overwrite the locked - * rotation back into the view we calculate with */ - copy_qt_qt(rv3d->viewquat, vod->curr.viewquat); - - /* Check for view snap, - * NOTE: don't apply snap to `vod->viewquat` so the view won't jam up. */ - if (vod->axis_snap) { - viewrotate_apply_snap(vod); - } - vod->prev.event_xy[0] = event_xy[0]; - vod->prev.event_xy[1] = event_xy[1]; - - ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, rv3d); - - ED_region_tag_redraw(vod->region); -} - -static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod = op->customdata; - short event_code = VIEW_PASS; - bool use_autokey = false; - int ret = OPERATOR_RUNNING_MODAL; - - /* execute the events */ - if (event->type == MOUSEMOVE) { - event_code = VIEW_APPLY; - } - else if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case VIEW_MODAL_CONFIRM: - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_AXIS_SNAP_ENABLE: - vod->axis_snap = true; - event_code = VIEW_APPLY; - break; - case VIEWROT_MODAL_AXIS_SNAP_DISABLE: - vod->rv3d->persp = vod->init.persp; - vod->axis_snap = false; - event_code = VIEW_APPLY; - break; - case VIEWROT_MODAL_SWITCH_ZOOM: - WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_MOVE: - WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - } - } - else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { - event_code = VIEW_CONFIRM; - } - - if (event_code == VIEW_APPLY) { - viewrotate_apply(vod, event->xy); - if (ED_screen_animation_playing(CTX_wm_manager(C))) { - use_autokey = true; - } - } - else if (event_code == VIEW_CONFIRM) { - use_autokey = true; - ret = OPERATOR_FINISHED; - } - - if (use_autokey) { - ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, true); - } - - if (ret & OPERATOR_FINISHED) { - viewops_data_free(C, op); - } - - return ret; -} - -static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod; - - const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); - - /* makes op->customdata */ - viewops_data_alloc(C, op); - vod = op->customdata; - - /* poll should check but in some cases fails, see poll func for details */ - if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_ROTATION) { - viewops_data_free(C, op); - return OPERATOR_PASS_THROUGH; - } - - ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); - - viewops_data_create(C, - op, - event, - viewops_flag_from_prefs() | VIEWOPS_FLAG_PERSP_ENSURE | - (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); - - if (ELEM(event->type, MOUSEPAN, MOUSEROTATE)) { - /* Rotate direction we keep always same */ - int event_xy[2]; - - if (event->type == MOUSEPAN) { - if (event->is_direction_inverted) { - event_xy[0] = 2 * event->xy[0] - event->prev_xy[0]; - event_xy[1] = 2 * event->xy[1] - event->prev_xy[1]; - } - else { - copy_v2_v2_int(event_xy, event->prev_xy); - } - } - else { - /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */ - copy_v2_v2_int(event_xy, event->prev_xy); - } - - viewrotate_apply(vod, event_xy); - - viewops_data_free(C, op); - - return OPERATOR_FINISHED; - } - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static void viewrotate_cancel(bContext *C, wmOperator *op) -{ - viewops_data_free(C, op); -} - -void VIEW3D_OT_rotate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Rotate View"; - ot->description = "Rotate the view"; - ot->idname = "VIEW3D_OT_rotate"; - - /* api callbacks */ - ot->invoke = viewrotate_invoke; - ot->modal = viewrotate_modal; - ot->poll = ED_operator_region_view3d_active; - ot->cancel = viewrotate_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; - - view3d_operator_properties_common(ot, V3D_OP_PROP_USE_MOUSE_INIT); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name NDOF Utility Functions - * \{ */ - -#ifdef WITH_INPUT_NDOF -static bool ndof_has_translate(const wmNDOFMotionData *ndof, - const View3D *v3d, - const RegionView3D *rv3d) -{ - return !is_zero_v3(ndof->tvec) && (!ED_view3d_offset_lock_check(v3d, rv3d)); -} - -static bool ndof_has_rotate(const wmNDOFMotionData *ndof, const RegionView3D *rv3d) -{ - return !is_zero_v3(ndof->rvec) && ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0); -} - -/** - * \param depth_pt: A point to calculate the depth (in perspective mode) - */ -static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth_pt[3]) -{ - float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND; - - if (rv3d->is_persp) { - speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL); - } - - return speed; -} - -static float view3d_ndof_pan_speed_calc_from_dist(RegionView3D *rv3d, const float dist) -{ - float viewinv[4]; - float tvec[3]; - - BLI_assert(dist >= 0.0f); - - copy_v3_fl3(tvec, 0.0f, 0.0f, dist); - /* rv3d->viewinv isn't always valid */ -# if 0 - mul_mat3_m4_v3(rv3d->viewinv, tvec); -# else - invert_qt_qt_normalized(viewinv, rv3d->viewquat); - mul_qt_v3(viewinv, tvec); -# endif - - return view3d_ndof_pan_speed_calc_ex(rv3d, tvec); -} - -static float view3d_ndof_pan_speed_calc(RegionView3D *rv3d) -{ - float tvec[3]; - negate_v3_v3(tvec, rv3d->ofs); - - return view3d_ndof_pan_speed_calc_ex(rv3d, tvec); -} - -/** - * Zoom and pan in the same function since sometimes zoom is interpreted as dolly (pan forward). - * - * \param has_zoom: zoom, otherwise dolly, - * often `!rv3d->is_persp` since it doesn't make sense to dolly in ortho. - */ -static void view3d_ndof_pan_zoom(const struct wmNDOFMotionData *ndof, - ScrArea *area, - ARegion *region, - const bool has_translate, - const bool has_zoom) -{ - RegionView3D *rv3d = region->regiondata; - float view_inv[4]; - float pan_vec[3]; - - if (has_translate == false && has_zoom == false) { - return; - } - - WM_event_ndof_pan_get(ndof, pan_vec, false); - - if (has_zoom) { - /* zoom with Z */ - - /* Zoom! - * velocity should be proportional to the linear velocity attained by rotational motion - * of same strength [got that?] proportional to `arclength = radius * angle`. - */ - - pan_vec[2] = 0.0f; - - /* "zoom in" or "translate"? depends on zoom mode in user settings? */ - if (ndof->tvec[2]) { - float zoom_distance = rv3d->dist * ndof->dt * ndof->tvec[2]; - - if (U.ndof_flag & NDOF_ZOOM_INVERT) { - zoom_distance = -zoom_distance; - } - - rv3d->dist += zoom_distance; - } - } - else { - /* dolly with Z */ - - /* all callers must check */ - if (has_translate) { - BLI_assert(ED_view3d_offset_lock_check((View3D *)area->spacedata.first, rv3d) == false); - } - } - - if (has_translate) { - const float speed = view3d_ndof_pan_speed_calc(rv3d); - - mul_v3_fl(pan_vec, speed * ndof->dt); - - /* transform motion from view to world coordinates */ - invert_qt_qt_normalized(view_inv, rv3d->viewquat); - mul_qt_v3(view_inv, pan_vec); - - /* move center of view opposite of hand motion (this is camera mode, not object mode) */ - sub_v3_v3(rv3d->ofs, pan_vec); - - if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { - view3d_boxview_sync(area, region); - } - } -} - -static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof, - ScrArea *area, - ARegion *region, - ViewOpsData *vod, - const bool apply_dyn_ofs) -{ - View3D *v3d = area->spacedata.first; - RegionView3D *rv3d = region->regiondata; - - float view_inv[4]; - - BLI_assert((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0); - - ED_view3d_persp_ensure(vod->depsgraph, v3d, region); - - rv3d->view = RV3D_VIEW_USER; - - invert_qt_qt_normalized(view_inv, rv3d->viewquat); - - if (U.ndof_flag & NDOF_TURNTABLE) { - float rot[3]; - - /* Turntable view code adapted for 3D mouse use. */ - float angle, quat[4]; - float xvec[3] = {1, 0, 0}; - - /* only use XY, ignore Z */ - WM_event_ndof_rotate_get(ndof, rot); - - /* Determine the direction of the x vector (for rotating up and down) */ - mul_qt_v3(view_inv, xvec); - - /* Perform the up/down rotation */ - angle = ndof->dt * rot[0]; - axis_angle_to_quat(quat, xvec, angle); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat); - - /* Perform the orbital rotation */ - angle = ndof->dt * rot[1]; - - /* update the onscreen doo-dad */ - rv3d->rot_angle = angle; - rv3d->rot_axis[0] = 0; - rv3d->rot_axis[1] = 0; - rv3d->rot_axis[2] = 1; - - axis_angle_to_quat_single(quat, 'Z', angle); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat); - } - else { - float quat[4]; - float axis[3]; - float angle = WM_event_ndof_to_axis_angle(ndof, axis); - - /* transform rotation axis from view to world coordinates */ - mul_qt_v3(view_inv, axis); - - /* update the onscreen doo-dad */ - rv3d->rot_angle = angle; - copy_v3_v3(rv3d->rot_axis, axis); - - axis_angle_to_quat(quat, axis, angle); - - /* apply rotation */ - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat); - } - - if (apply_dyn_ofs) { - viewrotate_apply_dyn_ofs(vod, rv3d->viewquat); - } -} - -void view3d_ndof_fly(const wmNDOFMotionData *ndof, - View3D *v3d, - RegionView3D *rv3d, - const bool use_precision, - const short protectflag, - bool *r_has_translate, - bool *r_has_rotate) -{ - bool has_translate = ndof_has_translate(ndof, v3d, rv3d); - bool has_rotate = ndof_has_rotate(ndof, rv3d); - - float view_inv[4]; - invert_qt_qt_normalized(view_inv, rv3d->viewquat); - - rv3d->rot_angle = 0.0f; /* disable onscreen rotation doo-dad */ - - if (has_translate) { - /* ignore real 'dist' since fly has its own speed settings, - * also its overwritten at this point. */ - float speed = view3d_ndof_pan_speed_calc_from_dist(rv3d, 1.0f); - float trans[3], trans_orig_y; - - if (use_precision) { - speed *= 0.2f; - } - - WM_event_ndof_pan_get(ndof, trans, false); - mul_v3_fl(trans, speed * ndof->dt); - trans_orig_y = trans[1]; - - if (U.ndof_flag & NDOF_FLY_HELICOPTER) { - trans[1] = 0.0f; - } - - /* transform motion from view to world coordinates */ - mul_qt_v3(view_inv, trans); - - if (U.ndof_flag & NDOF_FLY_HELICOPTER) { - /* replace world z component with device y (yes it makes sense) */ - trans[2] = trans_orig_y; - } - - if (rv3d->persp == RV3D_CAMOB) { - /* respect camera position locks */ - if (protectflag & OB_LOCK_LOCX) { - trans[0] = 0.0f; - } - if (protectflag & OB_LOCK_LOCY) { - trans[1] = 0.0f; - } - if (protectflag & OB_LOCK_LOCZ) { - trans[2] = 0.0f; - } - } - - if (!is_zero_v3(trans)) { - /* move center of view opposite of hand motion - * (this is camera mode, not object mode) */ - sub_v3_v3(rv3d->ofs, trans); - has_translate = true; - } - else { - has_translate = false; - } - } - - if (has_rotate) { - const float turn_sensitivity = 1.0f; - - float rotation[4]; - float axis[3]; - float angle = turn_sensitivity * WM_event_ndof_to_axis_angle(ndof, axis); - - if (fabsf(angle) > 0.0001f) { - has_rotate = true; - - if (use_precision) { - angle *= 0.2f; - } - - /* transform rotation axis from view to world coordinates */ - mul_qt_v3(view_inv, axis); - - /* apply rotation to view */ - axis_angle_to_quat(rotation, axis, angle); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation); - - if (U.ndof_flag & NDOF_LOCK_HORIZON) { - /* force an upright viewpoint - * TODO: make this less... sudden */ - float view_horizon[3] = {1.0f, 0.0f, 0.0f}; /* view +x */ - float view_direction[3] = {0.0f, 0.0f, -1.0f}; /* view -z (into screen) */ - - /* find new inverse since viewquat has changed */ - invert_qt_qt_normalized(view_inv, rv3d->viewquat); - /* could apply reverse rotation to existing view_inv to save a few cycles */ - - /* transform view vectors to world coordinates */ - mul_qt_v3(view_inv, view_horizon); - mul_qt_v3(view_inv, view_direction); - - /* find difference between view & world horizons - * true horizon lives in world xy plane, so look only at difference in z */ - angle = -asinf(view_horizon[2]); - - /* rotate view so view horizon = world horizon */ - axis_angle_to_quat(rotation, view_direction, angle); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation); - } - - rv3d->view = RV3D_VIEW_USER; - } - else { - has_rotate = false; - } - } - - *r_has_translate = has_translate; - *r_has_rotate = has_rotate; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name NDOF Orbit/Translate Operator - * \{ */ - -static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - if (event->type != NDOF_MOTION) { - return OPERATOR_CANCELLED; - } - - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewOpsData *vod; - View3D *v3d; - RegionView3D *rv3d; - char xform_flag = 0; - - const wmNDOFMotionData *ndof = event->customdata; - - viewops_data_alloc(C, op); - viewops_data_create( - C, op, event, viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0, false)); - vod = op->customdata; - - ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); - - v3d = vod->v3d; - rv3d = vod->rv3d; - - /* off by default, until changed later this function */ - rv3d->rot_angle = 0.0f; - - ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false); - - if (ndof->progress != P_FINISHING) { - const bool has_rotation = ndof_has_rotate(ndof, rv3d); - /* if we can't rotate, fallback to translate (locked axis views) */ - const bool has_translate = ndof_has_translate(ndof, v3d, rv3d) && - (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION); - const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp; - - if (has_translate || has_zoom) { - view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, has_zoom); - xform_flag |= HAS_TRANSLATE; - } - - if (has_rotation) { - view3d_ndof_orbit(ndof, vod->area, vod->region, vod, true); - xform_flag |= HAS_ROTATE; - } - } - - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - if (xform_flag) { - ED_view3d_camera_lock_autokey( - v3d, rv3d, C, xform_flag & HAS_ROTATE, xform_flag & HAS_TRANSLATE); - } - - ED_region_tag_redraw(vod->region); - - viewops_data_free(C, op); - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "NDOF Orbit View"; - ot->description = "Orbit the view using the 3D mouse"; - ot->idname = "VIEW3D_OT_ndof_orbit"; - - /* api callbacks */ - ot->invoke = ndof_orbit_invoke; - ot->poll = ED_operator_view3d_active; - - /* flags */ - ot->flag = 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name NDOF Orbit/Zoom Operator - * \{ */ - -static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - if (event->type != NDOF_MOTION) { - return OPERATOR_CANCELLED; - } - - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewOpsData *vod; - View3D *v3d; - RegionView3D *rv3d; - char xform_flag = 0; - - const wmNDOFMotionData *ndof = event->customdata; - - viewops_data_alloc(C, op); - viewops_data_create( - C, op, event, viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0, false)); - - vod = op->customdata; - - ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); - - v3d = vod->v3d; - rv3d = vod->rv3d; - - /* off by default, until changed later this function */ - rv3d->rot_angle = 0.0f; - - ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false); - - if (ndof->progress == P_FINISHING) { - /* pass */ - } - else if ((rv3d->persp == RV3D_ORTHO) && RV3D_VIEW_IS_AXIS(rv3d->view)) { - /* if we can't rotate, fallback to translate (locked axis views) */ - const bool has_translate = ndof_has_translate(ndof, v3d, rv3d); - const bool has_zoom = (ndof->tvec[2] != 0.0f) && ED_view3d_offset_lock_check(v3d, rv3d); - - if (has_translate || has_zoom) { - view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, true); - xform_flag |= HAS_TRANSLATE; - } - } - else { - /* NOTE: based on feedback from T67579, users want to have pan and orbit enabled at once. - * It's arguable that orbit shouldn't pan (since we have a pan only operator), - * so if there are users who like to separate orbit/pan operations - it can be a preference. */ - const bool is_orbit_around_pivot = (U.ndof_flag & NDOF_MODE_ORBIT) || - ED_view3d_offset_lock_check(v3d, rv3d); - const bool has_rotation = ndof_has_rotate(ndof, rv3d); - bool has_translate, has_zoom; - - if (is_orbit_around_pivot) { - /* Orbit preference or forced lock (Z zooms). */ - has_translate = !is_zero_v2(ndof->tvec) && ndof_has_translate(ndof, v3d, rv3d); - has_zoom = (ndof->tvec[2] != 0.0f); - } - else { - /* Free preference (Z translates). */ - has_translate = ndof_has_translate(ndof, v3d, rv3d); - has_zoom = false; - } - - /* Rotation first because dynamic offset resets offset otherwise (and disables panning). */ - if (has_rotation) { - const float dist_backup = rv3d->dist; - if (!is_orbit_around_pivot) { - ED_view3d_distance_set(rv3d, 0.0f); - } - view3d_ndof_orbit(ndof, vod->area, vod->region, vod, is_orbit_around_pivot); - xform_flag |= HAS_ROTATE; - if (!is_orbit_around_pivot) { - ED_view3d_distance_set(rv3d, dist_backup); - } - } - - if (has_translate || has_zoom) { - view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, has_zoom); - xform_flag |= HAS_TRANSLATE; - } - } - - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - if (xform_flag) { - ED_view3d_camera_lock_autokey( - v3d, rv3d, C, xform_flag & HAS_ROTATE, xform_flag & HAS_TRANSLATE); - } - - ED_region_tag_redraw(vod->region); - - viewops_data_free(C, op); - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "NDOF Orbit View with Zoom"; - ot->description = "Orbit and zoom the view using the 3D mouse"; - ot->idname = "VIEW3D_OT_ndof_orbit_zoom"; - - /* api callbacks */ - ot->invoke = ndof_orbit_zoom_invoke; - ot->poll = ED_operator_view3d_active; - - /* flags */ - ot->flag = 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name NDOF Pan/Zoom Operator - * \{ */ - -static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) -{ - if (event->type != NDOF_MOTION) { - return OPERATOR_CANCELLED; - } - - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - const wmNDOFMotionData *ndof = event->customdata; - char xform_flag = 0; - - const bool has_translate = ndof_has_translate(ndof, v3d, rv3d); - const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp; - - /* we're panning here! so erase any leftover rotation from other operators */ - rv3d->rot_angle = 0.0f; - - if (!(has_translate || has_zoom)) { - return OPERATOR_CANCELLED; - } - - ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false); - - if (ndof->progress != P_FINISHING) { - ScrArea *area = CTX_wm_area(C); - ARegion *region = CTX_wm_region(C); - - if (has_translate || has_zoom) { - view3d_ndof_pan_zoom(ndof, area, region, has_translate, has_zoom); - xform_flag |= HAS_TRANSLATE; - } - } - - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - if (xform_flag) { - ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, xform_flag & HAS_TRANSLATE); - } - - ED_region_tag_redraw(CTX_wm_region(C)); - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "NDOF Pan View"; - ot->description = "Pan the view with the 3D mouse"; - ot->idname = "VIEW3D_OT_ndof_pan"; - - /* api callbacks */ - ot->invoke = ndof_pan_invoke; - ot->poll = ED_operator_view3d_active; - - /* flags */ - ot->flag = 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name NDOF Transform All Operator - * \{ */ - -/** - * wraps #ndof_orbit_zoom but never restrict to orbit. - */ -static int ndof_all_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - /* weak!, but it works */ - const int ndof_flag = U.ndof_flag; - int ret; - - U.ndof_flag &= ~NDOF_MODE_ORBIT; - - ret = ndof_orbit_zoom_invoke(C, op, event); - - U.ndof_flag = ndof_flag; - - return ret; -} - -void VIEW3D_OT_ndof_all(struct wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "NDOF Transform View"; - ot->description = "Pan and rotate the view with the 3D mouse"; - ot->idname = "VIEW3D_OT_ndof_all"; - - /* api callbacks */ - ot->invoke = ndof_all_invoke; - ot->poll = ED_operator_view3d_active; - - /* flags */ - ot->flag = 0; -} - -#endif /* WITH_INPUT_NDOF */ - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Move (Pan) Operator - * \{ */ - -/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */ - -void viewmove_modal_keymap(wmKeyConfig *keyconf) -{ - static const EnumPropertyItem modal_items[] = { - {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, - - {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"}, - {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"}, - - {0, NULL, 0, NULL, NULL}, - }; - - wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Move Modal"); - - /* this function is called for each spacetype, only needs to add map once */ - if (keymap && keymap->modal_items) { - return; - } - - keymap = WM_modalkeymap_ensure(keyconf, "View3D Move Modal", modal_items); - - /* items for modal map */ - WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM); - - /* disabled mode switching for now, can re-implement better, later on */ -#if 0 - WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); - WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); - WM_modalkeymap_add_item( - keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); -#endif - - /* assign map to operators */ - WM_modalkeymap_assign(keymap, "VIEW3D_OT_move"); -} - -static void viewmove_apply(ViewOpsData *vod, int x, int y) -{ - if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) { - vod->rv3d->ofs_lock[0] -= ((vod->prev.event_xy[0] - x) * 2.0f) / (float)vod->region->winx; - vod->rv3d->ofs_lock[1] -= ((vod->prev.event_xy[1] - y) * 2.0f) / (float)vod->region->winy; - } - else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) { - const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f; - vod->rv3d->camdx += (vod->prev.event_xy[0] - x) / (vod->region->winx * zoomfac); - vod->rv3d->camdy += (vod->prev.event_xy[1] - y) / (vod->region->winy * zoomfac); - CLAMP(vod->rv3d->camdx, -1.0f, 1.0f); - CLAMP(vod->rv3d->camdy, -1.0f, 1.0f); - } - else { - float dvec[3]; - float mval_f[2]; - - mval_f[0] = x - vod->prev.event_xy[0]; - mval_f[1] = y - vod->prev.event_xy[1]; - ED_view3d_win_to_delta(vod->region, mval_f, dvec, vod->init.zfac); - - add_v3_v3(vod->rv3d->ofs, dvec); - - if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { - view3d_boxview_sync(vod->area, vod->region); - } - } - - vod->prev.event_xy[0] = x; - vod->prev.event_xy[1] = y; - - ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); - - ED_region_tag_redraw(vod->region); -} - -static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - - ViewOpsData *vod = op->customdata; - short event_code = VIEW_PASS; - bool use_autokey = false; - int ret = OPERATOR_RUNNING_MODAL; - - /* execute the events */ - if (event->type == MOUSEMOVE) { - event_code = VIEW_APPLY; - } - else if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case VIEW_MODAL_CONFIRM: - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_ZOOM: - WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_ROTATE: - WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - } - } - else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { - event_code = VIEW_CONFIRM; - } - - if (event_code == VIEW_APPLY) { - viewmove_apply(vod, event->xy[0], event->xy[1]); - if (ED_screen_animation_playing(CTX_wm_manager(C))) { - use_autokey = true; - } - } - else if (event_code == VIEW_CONFIRM) { - use_autokey = true; - ret = OPERATOR_FINISHED; - } - - if (use_autokey) { - ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); - } - - if (ret & OPERATOR_FINISHED) { - viewops_data_free(C, op); - } - - return ret; -} - -static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod; - - const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); - - /* makes op->customdata */ - viewops_data_alloc(C, op); - vod = op->customdata; - if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_LOCATION) { - viewops_data_free(C, op); - return OPERATOR_PASS_THROUGH; - } - - viewops_data_create(C, - op, - event, - (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) | - (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); - - ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); - - if (event->type == MOUSEPAN) { - /* invert it, trackpad scroll follows same principle as 2d windows this way */ - viewmove_apply( - vod, 2 * event->xy[0] - event->prev_xy[0], 2 * event->xy[1] - event->prev_xy[1]); - - viewops_data_free(C, op); - - return OPERATOR_FINISHED; - } - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static void viewmove_cancel(bContext *C, wmOperator *op) -{ - viewops_data_free(C, op); -} - -void VIEW3D_OT_move(wmOperatorType *ot) -{ - - /* identifiers */ - ot->name = "Pan View"; - ot->description = "Move the view"; - ot->idname = "VIEW3D_OT_move"; - - /* api callbacks */ - ot->invoke = viewmove_invoke; - ot->modal = viewmove_modal; - ot->poll = ED_operator_region_view3d_active; - ot->cancel = viewmove_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; - - /* properties */ - view3d_operator_properties_common(ot, V3D_OP_PROP_USE_MOUSE_INIT); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Zoom Operator - * \{ */ - -/* #viewdolly_modal_keymap has an exact copy of this, apply fixes to both. */ -void viewzoom_modal_keymap(wmKeyConfig *keyconf) -{ - static const EnumPropertyItem modal_items[] = { - {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, - - {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"}, - {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"}, - - {0, NULL, 0, NULL, NULL}, - }; - - wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Zoom Modal"); - - /* this function is called for each spacetype, only needs to add map once */ - if (keymap && keymap->modal_items) { - return; - } - - keymap = WM_modalkeymap_ensure(keyconf, "View3D Zoom Modal", modal_items); - - /* disabled mode switching for now, can re-implement better, later on */ -#if 0 - WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); - WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); - WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); -#endif - - /* assign map to operators */ - WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom"); -} - -/** - * \param zoom_xy: Optionally zoom to window location - * (coords compatible w/ #wmEvent.xy). Use when not NULL. - */ -static void view_zoom_to_window_xy_camera(Scene *scene, - Depsgraph *depsgraph, - View3D *v3d, - ARegion *region, - float dfac, - const int zoom_xy[2]) -{ - RegionView3D *rv3d = region->regiondata; - const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom); - const float zoomfac_new = clamp_f( - zoomfac * (1.0f / dfac), RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR); - const float camzoom_new = BKE_screen_view3d_zoom_from_fac(zoomfac_new); - - if (zoom_xy != NULL) { - float zoomfac_px; - rctf camera_frame_old; - rctf camera_frame_new; - - const float pt_src[2] = {zoom_xy[0], zoom_xy[1]}; - float pt_dst[2]; - float delta_px[2]; - - ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_old, false); - BLI_rctf_translate(&camera_frame_old, region->winrct.xmin, region->winrct.ymin); - - rv3d->camzoom = camzoom_new; - CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); - - ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_new, false); - BLI_rctf_translate(&camera_frame_new, region->winrct.xmin, region->winrct.ymin); - - BLI_rctf_transform_pt_v(&camera_frame_new, &camera_frame_old, pt_dst, pt_src); - sub_v2_v2v2(delta_px, pt_dst, pt_src); - - /* translate the camera offset using pixel space delta - * mapped back to the camera (same logic as panning in camera view) */ - zoomfac_px = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f; - - rv3d->camdx += delta_px[0] / (region->winx * zoomfac_px); - rv3d->camdy += delta_px[1] / (region->winy * zoomfac_px); - CLAMP(rv3d->camdx, -1.0f, 1.0f); - CLAMP(rv3d->camdy, -1.0f, 1.0f); - } - else { - rv3d->camzoom = camzoom_new; - CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); - } -} - -/** - * \param zoom_xy: Optionally zoom to window location - * (coords compatible w/ #wmEvent.xy). Use when not NULL. - */ -static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoom_xy[2]) -{ - RegionView3D *rv3d = region->regiondata; - const float dist_new = rv3d->dist * dfac; - - if (zoom_xy != NULL) { - float dvec[3]; - float tvec[3]; - float tpos[3]; - float mval_f[2]; - - float zfac; - - negate_v3_v3(tpos, rv3d->ofs); - - mval_f[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f; - mval_f[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f; - - /* Project cursor position into 3D space */ - zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); - - /* Calculate view target position for dolly */ - add_v3_v3v3(tvec, tpos, dvec); - negate_v3(tvec); - - /* Offset to target position and dolly */ - copy_v3_v3(rv3d->ofs, tvec); - rv3d->dist = dist_new; - - /* Calculate final offset */ - madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac); - } - else { - rv3d->dist = dist_new; - } -} - -static float viewzoom_scale_value(const rcti *winrct, - const eViewZoom_Style viewzoom, - const bool zoom_invert, - const bool zoom_invert_force, - const int xy_curr[2], - const int xy_init[2], - const float val, - const float val_orig, - double *r_timer_lastdraw) -{ - float zfac; - - if (viewzoom == USER_ZOOM_CONTINUE) { - double time = PIL_check_seconds_timer(); - float time_step = (float)(time - *r_timer_lastdraw); - float fac; - - if (U.uiflag & USER_ZOOM_HORIZ) { - fac = (float)(xy_init[0] - xy_curr[0]); - } - else { - fac = (float)(xy_init[1] - xy_curr[1]); - } - - fac /= U.dpi_fac; - - if (zoom_invert != zoom_invert_force) { - fac = -fac; - } - - zfac = 1.0f + ((fac / 20.0f) * time_step); - *r_timer_lastdraw = time; - } - else if (viewzoom == USER_ZOOM_SCALE) { - /* method which zooms based on how far you move the mouse */ - - const int ctr[2] = { - BLI_rcti_cent_x(winrct), - BLI_rcti_cent_y(winrct), - }; - float len_new = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_curr) / U.dpi_fac); - float len_old = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_init) / U.dpi_fac); - - /* intentionally ignore 'zoom_invert' for scale */ - if (zoom_invert_force) { - SWAP(float, len_new, len_old); - } - - zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val; - } - else { /* USER_ZOOM_DOLLY */ - float len_new = 5 * U.dpi_fac; - float len_old = 5 * U.dpi_fac; - - if (U.uiflag & USER_ZOOM_HORIZ) { - len_new += (winrct->xmax - (xy_curr[0])) / U.dpi_fac; - len_old += (winrct->xmax - (xy_init[0])) / U.dpi_fac; - } - else { - len_new += (winrct->ymax - (xy_curr[1])) / U.dpi_fac; - len_old += (winrct->ymax - (xy_init[1])) / U.dpi_fac; - } - - if (zoom_invert != zoom_invert_force) { - SWAP(float, len_new, len_old); - } - - zfac = val_orig * (2.0f * ((len_new / max_ff(len_old, 1.0f)) - 1.0f) + 1.0f) / val; - } - - return zfac; -} - -static float viewzoom_scale_value_offset(const rcti *winrct, - const eViewZoom_Style viewzoom, - const bool zoom_invert, - const bool zoom_invert_force, - const int xy_curr[2], - const int xy_init[2], - const int xy_offset[2], - const float val, - const float val_orig, - double *r_timer_lastdraw) -{ - const int xy_curr_offset[2] = { - xy_curr[0] + xy_offset[0], - xy_curr[1] + xy_offset[1], - }; - const int xy_init_offset[2] = { - xy_init[0] + xy_offset[0], - xy_init[1] + xy_offset[1], - }; - return viewzoom_scale_value(winrct, - viewzoom, - zoom_invert, - zoom_invert_force, - xy_curr_offset, - xy_init_offset, - val, - val_orig, - r_timer_lastdraw); -} - -static void viewzoom_apply_camera(ViewOpsData *vod, - const int xy[2], - const eViewZoom_Style viewzoom, - const bool zoom_invert, - const bool zoom_to_pos) -{ - float zfac; - float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->init.camzoom) * 2.0f; - float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f; - - zfac = viewzoom_scale_value_offset(&vod->region->winrct, - viewzoom, - zoom_invert, - true, - xy, - vod->init.event_xy, - vod->init.event_xy_offset, - zoomfac, - zoomfac_prev, - &vod->prev.time); - - if (!ELEM(zfac, 1.0f, 0.0f)) { - /* calculate inverted, then invert again (needed because of camera zoom scaling) */ - zfac = 1.0f / zfac; - view_zoom_to_window_xy_camera(vod->scene, - vod->depsgraph, - vod->v3d, - vod->region, - zfac, - zoom_to_pos ? vod->prev.event_xy : NULL); - } - - ED_region_tag_redraw(vod->region); -} - -static void viewzoom_apply_3d(ViewOpsData *vod, - const int xy[2], - const eViewZoom_Style viewzoom, - const bool zoom_invert, - const bool zoom_to_pos) -{ - float zfac; - float dist_range[2]; - - ED_view3d_dist_range_get(vod->v3d, dist_range); - - zfac = viewzoom_scale_value_offset(&vod->region->winrct, - viewzoom, - zoom_invert, - false, - xy, - vod->init.event_xy, - vod->init.event_xy_offset, - vod->rv3d->dist, - vod->init.dist, - &vod->prev.time); - - if (zfac != 1.0f) { - const float zfac_min = dist_range[0] / vod->rv3d->dist; - const float zfac_max = dist_range[1] / vod->rv3d->dist; - CLAMP(zfac, zfac_min, zfac_max); - - view_zoom_to_window_xy_3d(vod->region, zfac, zoom_to_pos ? vod->prev.event_xy : NULL); - } - - /* these limits were in old code too */ - CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]); - - if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { - view3d_boxview_sync(vod->area, vod->region); - } - - ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); - - ED_region_tag_redraw(vod->region); -} - -static void viewzoom_apply(ViewOpsData *vod, - const int xy[2], - const eViewZoom_Style viewzoom, - const bool zoom_invert, - const bool zoom_to_pos) -{ - if ((vod->rv3d->persp == RV3D_CAMOB) && - (vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0) { - viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert, zoom_to_pos); - } - else { - viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert, zoom_to_pos); - } -} - -static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod = op->customdata; - short event_code = VIEW_PASS; - bool use_autokey = false; - int ret = OPERATOR_RUNNING_MODAL; - - /* execute the events */ - if (event->type == TIMER && event->customdata == vod->timer) { - /* continuous zoom */ - event_code = VIEW_APPLY; - } - else if (event->type == MOUSEMOVE) { - event_code = VIEW_APPLY; - } - else if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case VIEW_MODAL_CONFIRM: - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_MOVE: - WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_ROTATE: - WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - } - } - else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { - event_code = VIEW_CONFIRM; - } - - if (event_code == VIEW_APPLY) { - const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); - viewzoom_apply(vod, - event->xy, - (eViewZoom_Style)U.viewzoom, - (U.uiflag & USER_ZOOM_INVERT) != 0, - (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); - if (ED_screen_animation_playing(CTX_wm_manager(C))) { - use_autokey = true; - } - } - else if (event_code == VIEW_CONFIRM) { - use_autokey = true; - ret = OPERATOR_FINISHED; - } - - if (use_autokey) { - ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); - } - - if (ret & OPERATOR_FINISHED) { - viewops_data_free(C, op); - } - - return ret; -} - -static int viewzoom_exec(bContext *C, wmOperator *op) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - View3D *v3d; - RegionView3D *rv3d; - ScrArea *area; - ARegion *region; - bool use_cam_zoom; - float dist_range[2]; - - const int delta = RNA_int_get(op->ptr, "delta"); - const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); - - if (op->customdata) { - ViewOpsData *vod = op->customdata; - - area = vod->area; - region = vod->region; - } - else { - area = CTX_wm_area(C); - region = CTX_wm_region(C); - } - - v3d = area->spacedata.first; - rv3d = region->regiondata; - - use_cam_zoom = (rv3d->persp == RV3D_CAMOB) && - !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d)); - - int zoom_xy_buf[2]; - const int *zoom_xy = NULL; - if (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) { - zoom_xy_buf[0] = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") : - region->winx / 2; - zoom_xy_buf[1] = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") : - region->winy / 2; - zoom_xy = zoom_xy_buf; - } - - ED_view3d_dist_range_get(v3d, dist_range); - - if (delta < 0) { - const float step = 1.2f; - /* this min and max is also in viewmove() */ - if (use_cam_zoom) { - view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); - } - else { - if (rv3d->dist < dist_range[1]) { - view_zoom_to_window_xy_3d(region, step, zoom_xy); - } - } - } - else { - const float step = 1.0f / 1.2f; - if (use_cam_zoom) { - view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); - } - else { - if (rv3d->dist > dist_range[0]) { - view_zoom_to_window_xy_3d(region, step, zoom_xy); - } - } - } - - if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { - view3d_boxview_sync(area, region); - } - - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true); - - ED_region_tag_redraw(region); - - viewops_data_free(C, op); - - return OPERATOR_FINISHED; -} - -/* viewdolly_invoke() copied this function, changes here may apply there */ -static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod; - - const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); - - /* makes op->customdata */ - viewops_data_alloc(C, op); - viewops_data_create(C, - op, - event, - (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) | - (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); - vod = op->customdata; - - ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); - - /* if one or the other zoom position aren't set, set from event */ - if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { - RNA_int_set(op->ptr, "mx", event->xy[0]); - RNA_int_set(op->ptr, "my", event->xy[1]); - } - - if (RNA_struct_property_is_set(op->ptr, "delta")) { - viewzoom_exec(C, op); - } - else { - if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { - - if (U.uiflag & USER_ZOOM_HORIZ) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; - } - else { - /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */ - vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] - - event->prev_xy[0]; - } - viewzoom_apply(vod, - event->prev_xy, - USER_ZOOM_DOLLY, - (U.uiflag & USER_ZOOM_INVERT) != 0, - (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); - ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); - - viewops_data_free(C, op); - return OPERATOR_FINISHED; - } - - if (U.viewzoom == USER_ZOOM_CONTINUE) { - /* needs a timer to continue redrawing */ - vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f); - vod->prev.time = PIL_check_seconds_timer(); - } - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_FINISHED; -} - -static void viewzoom_cancel(bContext *C, wmOperator *op) -{ - viewops_data_free(C, op); -} - -void VIEW3D_OT_zoom(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Zoom View"; - ot->description = "Zoom in/out in the view"; - ot->idname = "VIEW3D_OT_zoom"; - - /* api callbacks */ - ot->invoke = viewzoom_invoke; - ot->exec = viewzoom_exec; - ot->modal = viewzoom_modal; - ot->poll = view3d_zoom_or_dolly_poll; - ot->cancel = viewzoom_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; - - /* properties */ - view3d_operator_properties_common( - ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Dolly Operator - * - * Like zoom but translates the view offset along the view direction - * which avoids #RegionView3D.dist approaching zero. - * \{ */ - -/* This is an exact copy of #viewzoom_modal_keymap. */ -void viewdolly_modal_keymap(wmKeyConfig *keyconf) -{ - static const EnumPropertyItem modal_items[] = { - {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, - - {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"}, - {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"}, - - {0, NULL, 0, NULL, NULL}, - }; - - wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Dolly Modal"); - - /* this function is called for each spacetype, only needs to add map once */ - if (keymap && keymap->modal_items) { - return; - } - - keymap = WM_modalkeymap_ensure(keyconf, "View3D Dolly Modal", modal_items); - - /* disabled mode switching for now, can re-implement better, later on */ -#if 0 - WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); - WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); - WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); -#endif - - /* assign map to operators */ - WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly"); -} - -static bool viewdolly_offset_lock_check(bContext *C, wmOperator *op) -{ - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - if (ED_view3d_offset_lock_check(v3d, rv3d)) { - BKE_report(op->reports, RPT_WARNING, "Cannot dolly when the view offset is locked"); - return true; - } - return false; -} - -static void view_dolly_to_vector_3d(ARegion *region, - const float orig_ofs[3], - const float dvec[3], - float dfac) -{ - RegionView3D *rv3d = region->regiondata; - madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac)); -} - -static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert) -{ - float zfac = 1.0; - - { - float len1, len2; - - if (U.uiflag & USER_ZOOM_HORIZ) { - len1 = (vod->region->winrct.xmax - xy[0]) + 5; - len2 = (vod->region->winrct.xmax - vod->init.event_xy[0]) + 5; - } - else { - len1 = (vod->region->winrct.ymax - xy[1]) + 5; - len2 = (vod->region->winrct.ymax - vod->init.event_xy[1]) + 5; - } - if (zoom_invert) { - SWAP(float, len1, len2); - } - - zfac = 1.0f + ((len1 - len2) * 0.01f * vod->rv3d->dist); - } - - if (zfac != 1.0f) { - view_dolly_to_vector_3d(vod->region, vod->init.ofs, vod->init.mousevec, zfac); - } - - if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { - view3d_boxview_sync(vod->area, vod->region); - } - - ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); - - ED_region_tag_redraw(vod->region); -} - -static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod = op->customdata; - short event_code = VIEW_PASS; - bool use_autokey = false; - int ret = OPERATOR_RUNNING_MODAL; - - /* execute the events */ - if (event->type == MOUSEMOVE) { - event_code = VIEW_APPLY; - } - else if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case VIEW_MODAL_CONFIRM: - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_MOVE: - WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_ROTATE: - WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - } - } - else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { - event_code = VIEW_CONFIRM; - } - - if (event_code == VIEW_APPLY) { - viewdolly_apply(vod, event->xy, (U.uiflag & USER_ZOOM_INVERT) != 0); - if (ED_screen_animation_playing(CTX_wm_manager(C))) { - use_autokey = true; - } - } - else if (event_code == VIEW_CONFIRM) { - use_autokey = true; - ret = OPERATOR_FINISHED; - } - - if (use_autokey) { - ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); - } - - if (ret & OPERATOR_FINISHED) { - viewops_data_free(C, op); - } - - return ret; -} - -static int viewdolly_exec(bContext *C, wmOperator *op) -{ - View3D *v3d; - RegionView3D *rv3d; - ScrArea *area; - ARegion *region; - float mousevec[3]; - - const int delta = RNA_int_get(op->ptr, "delta"); - - if (op->customdata) { - ViewOpsData *vod = op->customdata; - - area = vod->area; - region = vod->region; - copy_v3_v3(mousevec, vod->init.mousevec); - } - else { - area = CTX_wm_area(C); - region = CTX_wm_region(C); - negate_v3_v3(mousevec, ((RegionView3D *)region->regiondata)->viewinv[2]); - normalize_v3(mousevec); - } - - v3d = area->spacedata.first; - rv3d = region->regiondata; - - const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); - - /* overwrite the mouse vector with the view direction (zoom into the center) */ - if ((use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) == 0) { - normalize_v3_v3(mousevec, rv3d->viewinv[2]); - negate_v3(mousevec); - } - - view_dolly_to_vector_3d(region, rv3d->ofs, mousevec, delta < 0 ? 1.8f : 0.2f); - - if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { - view3d_boxview_sync(area, region); - } - - ED_view3d_camera_lock_sync(CTX_data_ensure_evaluated_depsgraph(C), v3d, rv3d); - - ED_region_tag_redraw(region); - - viewops_data_free(C, op); - - return OPERATOR_FINISHED; -} - -/* copied from viewzoom_invoke(), changes here may apply there */ -static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod; - - if (viewdolly_offset_lock_check(C, op)) { - return OPERATOR_CANCELLED; - } - - /* makes op->customdata */ - viewops_data_alloc(C, op); - vod = op->customdata; - - /* poll should check but in some cases fails, see poll func for details */ - if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_ROTATION) { - viewops_data_free(C, op); - return OPERATOR_PASS_THROUGH; - } - - ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); - - /* needs to run before 'viewops_data_create' so the backup 'rv3d->ofs' is correct */ - /* switch from camera view when: */ - if (vod->rv3d->persp != RV3D_PERSP) { - if (vod->rv3d->persp == RV3D_CAMOB) { - /* ignore rv3d->lpersp because dolly only makes sense in perspective mode */ - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_persp_switch_from_camera(depsgraph, vod->v3d, vod->rv3d, RV3D_PERSP); - } - else { - vod->rv3d->persp = RV3D_PERSP; - } - ED_region_tag_redraw(vod->region); - } - - const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); - - viewops_data_create(C, - op, - event, - (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) | - (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); - - /* if one or the other zoom position aren't set, set from event */ - if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { - RNA_int_set(op->ptr, "mx", event->xy[0]); - RNA_int_set(op->ptr, "my", event->xy[1]); - } - - if (RNA_struct_property_is_set(op->ptr, "delta")) { - viewdolly_exec(C, op); - } - else { - /* overwrite the mouse vector with the view direction (zoom into the center) */ - if ((use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) == 0) { - negate_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]); - normalize_v3(vod->init.mousevec); - } - - if (event->type == MOUSEZOOM) { - /* Bypass Zoom invert flag for track pads (pass false always) */ - - if (U.uiflag & USER_ZOOM_HORIZ) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; - } - else { - /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */ - vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] - - event->prev_xy[0]; - } - viewdolly_apply(vod, event->prev_xy, (U.uiflag & USER_ZOOM_INVERT) == 0); - - viewops_data_free(C, op); - return OPERATOR_FINISHED; - } - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_FINISHED; -} - -static void viewdolly_cancel(bContext *C, wmOperator *op) -{ - viewops_data_free(C, op); -} - -void VIEW3D_OT_dolly(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Dolly View"; - ot->description = "Dolly in/out in the view"; - ot->idname = "VIEW3D_OT_dolly"; - - /* api callbacks */ - ot->invoke = viewdolly_invoke; - ot->exec = viewdolly_exec; - ot->modal = viewdolly_modal; - ot->poll = ED_operator_region_view3d_active; - ot->cancel = viewdolly_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; - - /* properties */ - view3d_operator_properties_common( - ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View All Operator - * - * Move & Zoom the view to fit all of its contents. - * \{ */ - -static bool view3d_object_skip_minmax(const View3D *v3d, - const RegionView3D *rv3d, - const Object *ob, - const bool skip_camera, - bool *r_only_center) -{ - BLI_assert(ob->id.orig_id == NULL); - *r_only_center = false; - - if (skip_camera && (ob == v3d->camera)) { - return true; - } - - if ((ob->type == OB_EMPTY) && (ob->empty_drawtype == OB_EMPTY_IMAGE) && - !BKE_object_empty_image_frame_is_visible_in_view3d(ob, rv3d)) { - *r_only_center = true; - return false; - } - - return false; -} - -static void view3d_object_calc_minmax(Depsgraph *depsgraph, - Scene *scene, - Object *ob_eval, - const bool only_center, - float min[3], - float max[3]) -{ - /* Account for duplis. */ - if (BKE_object_minmax_dupli(depsgraph, scene, ob_eval, min, max, false) == 0) { - /* Use if duplis aren't found. */ - if (only_center) { - minmax_v3v3_v3(min, max, ob_eval->obmat[3]); - } - else { - BKE_object_minmax(ob_eval, min, max, false); - } - } -} - -static void view3d_from_minmax(bContext *C, - View3D *v3d, - ARegion *region, - const float min[3], - const float max[3], - bool ok_dist, - const int smooth_viewtx) -{ - RegionView3D *rv3d = region->regiondata; - float afm[3]; - float size; - - ED_view3d_smooth_view_force_finish(C, v3d, region); - - /* SMOOTHVIEW */ - float new_ofs[3]; - float new_dist; - - sub_v3_v3v3(afm, max, min); - size = max_fff(afm[0], afm[1], afm[2]); - - if (ok_dist) { - char persp; - - if (rv3d->is_persp) { - if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) { - persp = RV3D_CAMOB; - } - else { - persp = RV3D_PERSP; - } - } - else { /* ortho */ - if (size < 0.0001f) { - /* bounding box was a single point so do not zoom */ - ok_dist = false; - } - else { - /* adjust zoom so it looks nicer */ - persp = RV3D_ORTHO; - } - } - - if (ok_dist) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - new_dist = ED_view3d_radius_to_dist( - v3d, region, depsgraph, persp, true, (size / 2) * VIEW3D_MARGIN); - if (rv3d->is_persp) { - /* don't zoom closer than the near clipping plane */ - new_dist = max_ff(new_dist, v3d->clip_start * 1.5f); - } - } - } - - mid_v3_v3v3(new_ofs, min, max); - negate_v3(new_ofs); - - if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) { - rv3d->persp = RV3D_PERSP; - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .camera_old = v3d->camera, - .ofs = new_ofs, - .dist = ok_dist ? &new_dist : NULL, - }); - } - else { - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .ofs = new_ofs, - .dist = ok_dist ? &new_dist : NULL, - }); - } - - /* Smooth-view does view-lock #RV3D_BOXVIEW copy. */ -} - -/** - * Same as #view3d_from_minmax but for all regions (except cameras). - */ -static void view3d_from_minmax_multi(bContext *C, - View3D *v3d, - const float min[3], - const float max[3], - const bool ok_dist, - const int smooth_viewtx) -{ - ScrArea *area = CTX_wm_area(C); - ARegion *region; - for (region = area->regionbase.first; region; region = region->next) { - if (region->regiontype == RGN_TYPE_WINDOW) { - RegionView3D *rv3d = region->regiondata; - /* when using all regions, don't jump out of camera view, - * but _do_ allow locked cameras to be moved */ - if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { - view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); - } - } - } -} - -static int view3d_all_exec(bContext *C, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); - Base *base_eval; - const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions"); - const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) || - /* any one of the regions may be locked */ - (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA)); - const bool center = RNA_boolean_get(op->ptr, "center"); - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - float min[3], max[3]; - bool changed = false; - - if (center) { - /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */ - View3DCursor *cursor = &scene->cursor; - zero_v3(min); - zero_v3(max); - zero_v3(cursor->location); - float mat3[3][3]; - unit_m3(mat3); - BKE_scene_cursor_mat3_to_rot(cursor, mat3, false); - } - else { - INIT_MINMAX(min, max); - } - - for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) { - if (BASE_VISIBLE(v3d, base_eval)) { - bool only_center = false; - Object *ob = DEG_get_original_object(base_eval->object); - if (view3d_object_skip_minmax(v3d, rv3d, ob, skip_camera, &only_center)) { - continue; - } - view3d_object_calc_minmax(depsgraph, scene, base_eval->object, only_center, min, max); - changed = true; - } - } - - if (center) { - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - WM_msg_publish_rna_prop(mbus, &scene->id, &scene->cursor, View3DCursor, location); - - DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); - } - - if (!changed) { - ED_region_tag_redraw(region); - /* TODO: should this be cancel? - * I think no, because we always move the cursor, with or without - * object, but in this case there is no change in the scene, - * only the cursor so I choice a ED_region_tag like - * view3d_smooth_view do for the center_cursor. - * See bug T22640. - */ - return OPERATOR_FINISHED; - } - - if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { - /* This is an approximation, see function documentation for details. */ - ED_view3d_clipping_clamp_minmax(rv3d, min, max); - } - - if (use_all_regions) { - view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx); - } - else { - view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx); - } - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_view_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Frame All"; - ot->description = "View all objects in scene"; - ot->idname = "VIEW3D_OT_view_all"; - - /* api callbacks */ - ot->exec = view3d_all_exec; - ot->poll = ED_operator_region_view3d_active; - - /* flags */ - ot->flag = 0; - - /* properties */ - view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS); - RNA_def_boolean(ot->srna, "center", 0, "Center", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Frame Selected Operator - * - * Move & Zoom the view to fit selected contents. - * \{ */ - -static int viewselected_exec(bContext *C, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); - Object *ob_eval = OBACT(view_layer_eval); - Object *obedit = CTX_data_edit_object(C); - const bGPdata *gpd_eval = ob_eval && (ob_eval->type == OB_GPENCIL) ? ob_eval->data : NULL; - const bool is_gp_edit = gpd_eval ? GPENCIL_ANY_MODE(gpd_eval) : false; - const bool is_face_map = ((is_gp_edit == false) && region->gizmo_map && - WM_gizmomap_is_any_selected(region->gizmo_map)); - float min[3], max[3]; - bool ok = false, ok_dist = true; - const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions"); - const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) || - /* any one of the regions may be locked */ - (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA)); - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - INIT_MINMAX(min, max); - if (is_face_map) { - ob_eval = NULL; - } - - if (ob_eval && (ob_eval->mode & OB_MODE_WEIGHT_PAINT)) { - /* hard-coded exception, we look for the one selected armature */ - /* this is weak code this way, we should make a generic - * active/selection callback interface once... */ - Base *base_eval; - for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) { - if (BASE_SELECTED_EDITABLE(v3d, base_eval)) { - if (base_eval->object->type == OB_ARMATURE) { - if (base_eval->object->mode & OB_MODE_POSE) { - break; - } - } - } - } - if (base_eval) { - ob_eval = base_eval->object; - } - } - - if (is_gp_edit) { - CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { - /* we're only interested in selected points here... */ - if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) { - ok |= BKE_gpencil_stroke_minmax(gps, true, min, max); - } - if (gps->editcurve != NULL) { - for (int i = 0; i < gps->editcurve->tot_curve_points; i++) { - BezTriple *bezt = &gps->editcurve->curve_points[i].bezt; - if ((bezt->f1 & SELECT)) { - minmax_v3v3_v3(min, max, bezt->vec[0]); - ok = true; - } - if ((bezt->f2 & SELECT)) { - minmax_v3v3_v3(min, max, bezt->vec[1]); - ok = true; - } - if ((bezt->f3 & SELECT)) { - minmax_v3v3_v3(min, max, bezt->vec[2]); - ok = true; - } - } - } - } - CTX_DATA_END; - - if ((ob_eval) && (ok)) { - mul_m4_v3(ob_eval->obmat, min); - mul_m4_v3(ob_eval->obmat, max); - } - } - else if (is_face_map) { - ok = WM_gizmomap_minmax(region->gizmo_map, true, true, min, max); - } - else if (obedit) { - /* only selected */ - FOREACH_OBJECT_IN_MODE_BEGIN (view_layer_eval, v3d, obedit->type, obedit->mode, ob_eval_iter) { - ok |= ED_view3d_minmax_verts(ob_eval_iter, min, max); - } - FOREACH_OBJECT_IN_MODE_END; - } - else if (ob_eval && (ob_eval->mode & OB_MODE_POSE)) { - FOREACH_OBJECT_IN_MODE_BEGIN ( - view_layer_eval, v3d, ob_eval->type, ob_eval->mode, ob_eval_iter) { - ok |= BKE_pose_minmax(ob_eval_iter, min, max, true, true); - } - FOREACH_OBJECT_IN_MODE_END; - } - else if (BKE_paint_select_face_test(ob_eval)) { - ok = paintface_minmax(ob_eval, min, max); - } - else if (ob_eval && (ob_eval->mode & OB_MODE_PARTICLE_EDIT)) { - ok = PE_minmax(depsgraph, scene, CTX_data_view_layer(C), min, max); - } - else if (ob_eval && (ob_eval->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | - OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) { - BKE_paint_stroke_get_average(scene, ob_eval, min); - copy_v3_v3(max, min); - ok = true; - ok_dist = 0; /* don't zoom */ - } - else { - Base *base_eval; - for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) { - if (BASE_SELECTED(v3d, base_eval)) { - bool only_center = false; - Object *ob = DEG_get_original_object(base_eval->object); - if (view3d_object_skip_minmax(v3d, rv3d, ob, skip_camera, &only_center)) { - continue; - } - view3d_object_calc_minmax(depsgraph, scene, base_eval->object, only_center, min, max); - ok = 1; - } - } - } - - if (ok == 0) { - return OPERATOR_FINISHED; - } - - if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { - /* This is an approximation, see function documentation for details. */ - ED_view3d_clipping_clamp_minmax(rv3d, min, max); - } - - if (use_all_regions) { - view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx); - } - else { - view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); - } - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_view_selected(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Frame Selected"; - ot->description = "Move the view to the selection center"; - ot->idname = "VIEW3D_OT_view_selected"; - - /* api callbacks */ - ot->exec = viewselected_exec; - ot->poll = view3d_zoom_or_dolly_poll; - - /* flags */ - ot->flag = 0; - - /* properties */ - view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS); -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name View Lock Clear Operator * \{ */ @@ -3256,103 +179,6 @@ void VIEW3D_OT_view_lock_to_active(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name View Center Cursor Operator - * \{ */ - -static int viewcenter_cursor_exec(bContext *C, wmOperator *op) -{ - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - Scene *scene = CTX_data_scene(C); - - if (rv3d) { - ARegion *region = CTX_wm_region(C); - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - ED_view3d_smooth_view_force_finish(C, v3d, region); - - /* non camera center */ - float new_ofs[3]; - negate_v3_v3(new_ofs, scene->cursor.location); - ED_view3d_smooth_view( - C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs}); - - /* Smooth view does view-lock #RV3D_BOXVIEW copy. */ - } - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_view_center_cursor(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Center View to Cursor"; - ot->description = "Center the view so that the cursor is in the middle of the view"; - ot->idname = "VIEW3D_OT_view_center_cursor"; - - /* api callbacks */ - ot->exec = viewcenter_cursor_exec; - ot->poll = view3d_pan_poll; - - /* flags */ - ot->flag = 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Center Pick Operator - * \{ */ - -static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - ARegion *region = CTX_wm_region(C); - - if (rv3d) { - struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - float new_ofs[3]; - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - ED_view3d_smooth_view_force_finish(C, v3d, region); - - view3d_operator_needs_opengl(C); - - if (ED_view3d_autodist(depsgraph, region, v3d, event->mval, new_ofs, false, NULL)) { - /* pass */ - } - else { - /* fallback to simple pan */ - negate_v3_v3(new_ofs, rv3d->ofs); - ED_view3d_win_to_3d_int(v3d, region, new_ofs, event->mval, new_ofs); - } - negate_v3(new_ofs); - ED_view3d_smooth_view( - C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs}); - } - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_view_center_pick(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Center View to Mouse"; - ot->description = "Center the view to the Z-depth position under the mouse cursor"; - ot->idname = "VIEW3D_OT_view_center_pick"; - - /* api callbacks */ - ot->invoke = viewcenter_pick_invoke; - ot->poll = view3d_pan_poll; - - /* flags */ - ot->flag = 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Frame Camera Bounds Operator * \{ */ @@ -3591,189 +417,6 @@ void VIEW3D_OT_clear_render_border(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Border Zoom Operator - * \{ */ - -static int view3d_zoom_border_exec(bContext *C, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - /* Zooms in on a border drawn by the user */ - rcti rect; - float dvec[3], vb[2], xscale, yscale; - float dist_range[2]; - - /* SMOOTHVIEW */ - float new_dist; - float new_ofs[3]; - - /* ZBuffer depth vars */ - float depth_close = FLT_MAX; - float cent[2], p[3]; - - /* NOTE: otherwise opengl won't work. */ - view3d_operator_needs_opengl(C); - - /* get box select values using rna */ - WM_operator_properties_border_to_rcti(op, &rect); - - /* check if zooming in/out view */ - const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out"); - - 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, NULL); - { - /* avoid allocating the whole depth buffer */ - ViewDepths depth_temp = {0}; - - /* avoid view3d_update_depths() for speed. */ - view3d_depths_rect_create(region, &rect, &depth_temp); - - /* find the closest Z pixel */ - depth_close = view3d_depth_near(&depth_temp); - - MEM_SAFE_FREE(depth_temp.depths); - } - - /* Resize border to the same ratio as the window. */ - { - const float region_aspect = (float)region->winx / (float)region->winy; - if (((float)BLI_rcti_size_x(&rect) / (float)BLI_rcti_size_y(&rect)) < region_aspect) { - BLI_rcti_resize_x(&rect, (int)(BLI_rcti_size_y(&rect) * region_aspect)); - } - else { - BLI_rcti_resize_y(&rect, (int)(BLI_rcti_size_x(&rect) / region_aspect)); - } - } - - cent[0] = (((float)rect.xmin) + ((float)rect.xmax)) / 2; - cent[1] = (((float)rect.ymin) + ((float)rect.ymax)) / 2; - - if (rv3d->is_persp) { - float p_corner[3]; - - /* 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; - } - /* convert border to 3d coordinates */ - if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) || - (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) { - return OPERATOR_CANCELLED; - } - - sub_v3_v3v3(dvec, p, p_corner); - negate_v3_v3(new_ofs, p); - - new_dist = len_v3(dvec); - - /* Account for the lens, without this a narrow lens zooms in too close. */ - new_dist *= (v3d->lens / DEFAULT_SENSOR_WIDTH); - - /* ignore dist_range min */ - dist_range[0] = v3d->clip_start * 1.5f; - } - else { /* orthographic */ - /* find the current window width and height */ - vb[0] = region->winx; - vb[1] = region->winy; - - new_dist = rv3d->dist; - - /* convert the drawn rectangle into 3d space */ - if (depth_close != FLT_MAX && - ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) { - negate_v3_v3(new_ofs, p); - } - else { - float mval_f[2]; - float zfac; - - /* We can't use the depth, fallback to the old way that doesn't set the center depth */ - copy_v3_v3(new_ofs, rv3d->ofs); - - { - float tvec[3]; - negate_v3_v3(tvec, new_ofs); - zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL); - } - - mval_f[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f; - mval_f[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f; - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); - /* center the view to the center of the rectangle */ - sub_v3_v3(new_ofs, dvec); - } - - /* work out the ratios, so that everything selected fits when we zoom */ - xscale = (BLI_rcti_size_x(&rect) / vb[0]); - yscale = (BLI_rcti_size_y(&rect) / vb[1]); - new_dist *= max_ff(xscale, yscale); - } - - if (!zoom_in) { - sub_v3_v3v3(dvec, new_ofs, rv3d->ofs); - new_dist = rv3d->dist * (rv3d->dist / new_dist); - add_v3_v3v3(new_ofs, rv3d->ofs, dvec); - } - - /* clamp after because we may have been zooming out */ - CLAMP(new_dist, dist_range[0], dist_range[1]); - - /* TODO(campbell): 'is_camera_lock' not currently working well. */ - const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d); - if ((rv3d->persp == RV3D_CAMOB) && (is_camera_lock == false)) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP); - } - - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .ofs = new_ofs, - .dist = &new_dist, - }); - - if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { - view3d_boxview_sync(CTX_wm_area(C), region); - } - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_zoom_border(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Zoom to Border"; - ot->description = "Zoom in the view to the nearest object contained in the border"; - ot->idname = "VIEW3D_OT_zoom_border"; - - /* api callbacks */ - ot->invoke = WM_gesture_box_invoke; - ot->exec = view3d_zoom_border_exec; - ot->modal = WM_gesture_box_modal; - ot->cancel = WM_gesture_box_cancel; - - ot->poll = view3d_zoom_or_dolly_poll; - - /* flags */ - ot->flag = 0; - - /* properties */ - WM_operator_properties_gesture_box_zoom(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Set Camera Zoom 1:1 Operator * * Sets the view to 1:1 camera/render-pixel. @@ -3830,838 +473,6 @@ void VIEW3D_OT_zoom_camera_1_to_1(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name View Axis Operator - * \{ */ - -static const EnumPropertyItem prop_view_items[] = { - {RV3D_VIEW_LEFT, "LEFT", ICON_TRIA_LEFT, "Left", "View from the left"}, - {RV3D_VIEW_RIGHT, "RIGHT", ICON_TRIA_RIGHT, "Right", "View from the right"}, - {RV3D_VIEW_BOTTOM, "BOTTOM", ICON_TRIA_DOWN, "Bottom", "View from the bottom"}, - {RV3D_VIEW_TOP, "TOP", ICON_TRIA_UP, "Top", "View from the top"}, - {RV3D_VIEW_FRONT, "FRONT", 0, "Front", "View from the front"}, - {RV3D_VIEW_BACK, "BACK", 0, "Back", "View from the back"}, - {0, NULL, 0, NULL, NULL}, -}; - -/* would like to make this a generic function - outside of transform */ - -/** - * \param align_to_quat: When not NULL, set the axis relative to this rotation. - */ -static void axis_set_view(bContext *C, - View3D *v3d, - ARegion *region, - const float quat_[4], - char view, - char view_axis_roll, - int perspo, - const float *align_to_quat, - const int smooth_viewtx) -{ - RegionView3D *rv3d = region->regiondata; /* no NULL check is needed, poll checks */ - float quat[4]; - const short orig_persp = rv3d->persp; - - normalize_qt_qt(quat, quat_); - - if (align_to_quat) { - mul_qt_qtqt(quat, quat, align_to_quat); - rv3d->view = view = RV3D_VIEW_USER; - rv3d->view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; - } - - if (align_to_quat == NULL) { - rv3d->view = view; - rv3d->view_axis_roll = view_axis_roll; - } - - if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) { - ED_region_tag_redraw(region); - return; - } - - if (U.uiflag & USER_AUTOPERSP) { - rv3d->persp = RV3D_VIEW_IS_AXIS(view) ? RV3D_ORTHO : perspo; - } - else if (rv3d->persp == RV3D_CAMOB) { - rv3d->persp = perspo; - } - - if (rv3d->persp == RV3D_CAMOB && v3d->camera) { - /* to camera */ - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .camera_old = v3d->camera, - .ofs = rv3d->ofs, - .quat = quat, - }); - } - else if (orig_persp == RV3D_CAMOB && v3d->camera) { - /* from camera */ - float ofs[3], dist; - - copy_v3_v3(ofs, rv3d->ofs); - dist = rv3d->dist; - - /* so we animate _from_ the camera location */ - Object *camera_eval = DEG_get_evaluated_object(CTX_data_ensure_evaluated_depsgraph(C), - v3d->camera); - ED_view3d_from_object(camera_eval, rv3d->ofs, NULL, &rv3d->dist, NULL); - - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .camera_old = camera_eval, - .ofs = ofs, - .quat = quat, - .dist = &dist, - }); - } - else { - /* rotate around selection */ - const float *dyn_ofs_pt = NULL; - float dyn_ofs[3]; - - if (U.uiflag & USER_ORBIT_SELECTION) { - if (view3d_orbit_calc_center(C, dyn_ofs)) { - negate_v3(dyn_ofs); - dyn_ofs_pt = dyn_ofs; - } - } - - /* no camera involved */ - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .quat = quat, - .dyn_ofs = dyn_ofs_pt, - }); - } -} - -static int view_axis_exec(bContext *C, wmOperator *op) -{ - View3D *v3d; - ARegion *region; - RegionView3D *rv3d; - static int perspo = RV3D_PERSP; - int viewnum; - int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - /* no NULL check is needed, poll checks */ - ED_view3d_context_user_region(C, &v3d, ®ion); - rv3d = region->regiondata; - - ED_view3d_smooth_view_force_finish(C, v3d, region); - - viewnum = RNA_enum_get(op->ptr, "type"); - - float align_quat_buf[4]; - float *align_quat = NULL; - - if (RNA_boolean_get(op->ptr, "align_active")) { - /* align to active object */ - Object *obact = CTX_data_active_object(C); - if (obact != NULL) { - float twmat[3][3]; - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *obedit = CTX_data_edit_object(C); - /* same as transform gizmo when normal is set */ - ED_getTransformOrientationMatrix(view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat); - align_quat = align_quat_buf; - mat3_to_quat(align_quat, twmat); - invert_qt_normalized(align_quat); - } - } - - if (RNA_boolean_get(op->ptr, "relative")) { - float quat_rotate[4]; - float quat_test[4]; - - if (viewnum == RV3D_VIEW_LEFT) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], -M_PI / 2.0f); - } - else if (viewnum == RV3D_VIEW_RIGHT) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], M_PI / 2.0f); - } - else if (viewnum == RV3D_VIEW_TOP) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], -M_PI / 2.0f); - } - else if (viewnum == RV3D_VIEW_BOTTOM) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI / 2.0f); - } - else if (viewnum == RV3D_VIEW_FRONT) { - unit_qt(quat_rotate); - } - else if (viewnum == RV3D_VIEW_BACK) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI); - } - else { - BLI_assert(0); - } - - mul_qt_qtqt(quat_test, rv3d->viewquat, quat_rotate); - - float angle_best = FLT_MAX; - int view_best = -1; - int view_axis_roll_best = -1; - for (int i = RV3D_VIEW_FRONT; i <= RV3D_VIEW_BOTTOM; i++) { - for (int j = RV3D_VIEW_AXIS_ROLL_0; j <= RV3D_VIEW_AXIS_ROLL_270; j++) { - float quat_axis[4]; - ED_view3d_quat_from_axis_view(i, j, quat_axis); - if (align_quat) { - mul_qt_qtqt(quat_axis, quat_axis, align_quat); - } - const float angle_test = fabsf(angle_signed_qtqt(quat_axis, quat_test)); - if (angle_best > angle_test) { - angle_best = angle_test; - view_best = i; - view_axis_roll_best = j; - } - } - } - if (view_best == -1) { - view_best = RV3D_VIEW_FRONT; - view_axis_roll_best = RV3D_VIEW_AXIS_ROLL_0; - } - - /* Disallow non-upright views in turn-table modes, - * it's too difficult to navigate out of them. */ - if ((U.flag & USER_TRACKBALL) == 0) { - if (!ELEM(view_best, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) { - view_axis_roll_best = RV3D_VIEW_AXIS_ROLL_0; - } - } - - viewnum = view_best; - view_axis_roll = view_axis_roll_best; - } - - /* Use this to test if we started out with a camera */ - const int nextperspo = (rv3d->persp == RV3D_CAMOB) ? rv3d->lpersp : perspo; - float quat[4]; - ED_view3d_quat_from_axis_view(viewnum, view_axis_roll, quat); - axis_set_view( - C, v3d, region, quat, viewnum, view_axis_roll, nextperspo, align_quat, smooth_viewtx); - - perspo = rv3d->persp; - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_view_axis(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "View Axis"; - ot->description = "Use a preset viewpoint"; - ot->idname = "VIEW3D_OT_view_axis"; - - /* api callbacks */ - ot->exec = view_axis_exec; - ot->poll = ED_operator_rv3d_user_region_poll; - - /* flags */ - ot->flag = 0; - - ot->prop = RNA_def_enum(ot->srna, "type", prop_view_items, 0, "View", "Preset viewpoint to use"); - RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean( - ot->srna, "align_active", 0, "Align Active", "Align to the active object's axis"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean( - ot->srna, "relative", 0, "Relative", "Rotate relative to the current orientation"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Camera Operator - * \{ */ - -static int view_camera_exec(bContext *C, wmOperator *op) -{ - View3D *v3d; - ARegion *region; - RegionView3D *rv3d; - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - /* no NULL check is needed, poll checks */ - ED_view3d_context_user_region(C, &v3d, ®ion); - rv3d = region->regiondata; - - ED_view3d_smooth_view_force_finish(C, v3d, region); - - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) == 0) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); - - if (rv3d->persp != RV3D_CAMOB) { - Object *ob = OBACT(view_layer); - - if (!rv3d->smooth_timer) { - /* store settings of current view before allowing overwriting with camera view - * only if we're not currently in a view transition */ - - ED_view3d_lastview_store(rv3d); - } - - /* first get the default camera for the view lock type */ - if (v3d->scenelock) { - /* sets the camera view if available */ - v3d->camera = scene->camera; - } - else { - /* use scene camera if one is not set (even though we're unlocked) */ - if (v3d->camera == NULL) { - v3d->camera = scene->camera; - } - } - - /* if the camera isn't found, check a number of options */ - if (v3d->camera == NULL && ob && ob->type == OB_CAMERA) { - v3d->camera = ob; - } - - if (v3d->camera == NULL) { - v3d->camera = BKE_view_layer_camera_find(view_layer); - } - - /* couldn't find any useful camera, bail out */ - if (v3d->camera == NULL) { - return OPERATOR_CANCELLED; - } - - /* important these don't get out of sync for locked scenes */ - if (v3d->scenelock && scene->camera != v3d->camera) { - scene->camera = v3d->camera; - DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); - } - - /* finally do snazzy view zooming */ - rv3d->persp = RV3D_CAMOB; - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .camera = v3d->camera, - .ofs = rv3d->ofs, - .quat = rv3d->viewquat, - .dist = &rv3d->dist, - .lens = &v3d->lens, - }); - } - else { - /* return to settings of last view */ - /* does view3d_smooth_view too */ - axis_set_view(C, - v3d, - region, - rv3d->lviewquat, - rv3d->lview, - rv3d->lview_axis_roll, - rv3d->lpersp, - NULL, - smooth_viewtx); - } - } - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_view_camera(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "View Camera"; - ot->description = "Toggle the camera view"; - ot->idname = "VIEW3D_OT_view_camera"; - - /* api callbacks */ - ot->exec = view_camera_exec; - ot->poll = ED_operator_rv3d_user_region_poll; - - /* flags */ - ot->flag = 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Orbit Operator - * - * Rotate (orbit) in incremental steps. For interactive orbit see #VIEW3D_OT_rotate. - * \{ */ - -enum { - V3D_VIEW_STEPLEFT = 1, - V3D_VIEW_STEPRIGHT, - V3D_VIEW_STEPDOWN, - V3D_VIEW_STEPUP, -}; - -static const EnumPropertyItem prop_view_orbit_items[] = { - {V3D_VIEW_STEPLEFT, "ORBITLEFT", 0, "Orbit Left", "Orbit the view around to the left"}, - {V3D_VIEW_STEPRIGHT, "ORBITRIGHT", 0, "Orbit Right", "Orbit the view around to the right"}, - {V3D_VIEW_STEPUP, "ORBITUP", 0, "Orbit Up", "Orbit the view up"}, - {V3D_VIEW_STEPDOWN, "ORBITDOWN", 0, "Orbit Down", "Orbit the view down"}, - {0, NULL, 0, NULL, NULL}, -}; - -static int vieworbit_exec(bContext *C, wmOperator *op) -{ - View3D *v3d; - ARegion *region; - RegionView3D *rv3d; - int orbitdir; - char view_opposite; - PropertyRNA *prop_angle = RNA_struct_find_property(op->ptr, "angle"); - float angle = RNA_property_is_set(op->ptr, prop_angle) ? - RNA_property_float_get(op->ptr, prop_angle) : - DEG2RADF(U.pad_rot_angle); - - /* no NULL check is needed, poll checks */ - v3d = CTX_wm_view3d(C); - region = CTX_wm_region(C); - rv3d = region->regiondata; - - /* support for switching to the opposite view (even when in locked views) */ - view_opposite = (fabsf(angle) == (float)M_PI) ? ED_view3d_axis_view_opposite(rv3d->view) : - RV3D_VIEW_USER; - orbitdir = RNA_enum_get(op->ptr, "type"); - - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) && (view_opposite == RV3D_VIEW_USER)) { - /* no NULL check is needed, poll checks */ - ED_view3d_context_user_region(C, &v3d, ®ion); - rv3d = region->regiondata; - } - - ED_view3d_smooth_view_force_finish(C, v3d, region); - - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0 || (view_opposite != RV3D_VIEW_USER)) { - if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { - int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - float quat_mul[4]; - float quat_new[4]; - - if (view_opposite == RV3D_VIEW_USER) { - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_persp_ensure(depsgraph, v3d, region); - } - - if (ELEM(orbitdir, V3D_VIEW_STEPLEFT, V3D_VIEW_STEPRIGHT)) { - if (orbitdir == V3D_VIEW_STEPRIGHT) { - angle = -angle; - } - - /* z-axis */ - axis_angle_to_quat_single(quat_mul, 'Z', angle); - } - else { - - if (orbitdir == V3D_VIEW_STEPDOWN) { - angle = -angle; - } - - /* horizontal axis */ - axis_angle_to_quat(quat_mul, rv3d->viewinv[0], angle); - } - - mul_qt_qtqt(quat_new, rv3d->viewquat, quat_mul); - - /* avoid precision loss over time */ - normalize_qt(quat_new); - - if (view_opposite != RV3D_VIEW_USER) { - rv3d->view = view_opposite; - /* avoid float in-precision, just get a new orientation */ - ED_view3d_quat_from_axis_view(view_opposite, rv3d->view_axis_roll, quat_new); - } - else { - rv3d->view = RV3D_VIEW_USER; - } - - float dyn_ofs[3], *dyn_ofs_pt = NULL; - - if (U.uiflag & USER_ORBIT_SELECTION) { - if (view3d_orbit_calc_center(C, dyn_ofs)) { - negate_v3(dyn_ofs); - dyn_ofs_pt = dyn_ofs; - } - } - - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .quat = quat_new, - .dyn_ofs = dyn_ofs_pt, - }); - - return OPERATOR_FINISHED; - } - } - - return OPERATOR_CANCELLED; -} - -void VIEW3D_OT_view_orbit(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "View Orbit"; - ot->description = "Orbit the view"; - ot->idname = "VIEW3D_OT_view_orbit"; - - /* api callbacks */ - ot->exec = vieworbit_exec; - ot->poll = ED_operator_rv3d_user_region_poll; - - /* flags */ - ot->flag = 0; - - /* properties */ - prop = RNA_def_float(ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - ot->prop = RNA_def_enum( - ot->srna, "type", prop_view_orbit_items, 0, "Orbit", "Direction of View Orbit"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Roll Operator - * \{ */ - -static void view_roll_angle( - ARegion *region, float quat[4], const float orig_quat[4], const float dvec[3], float angle) -{ - RegionView3D *rv3d = region->regiondata; - float quat_mul[4]; - - /* camera axis */ - axis_angle_normalized_to_quat(quat_mul, dvec, angle); - - mul_qt_qtqt(quat, orig_quat, quat_mul); - - /* avoid precision loss over time */ - normalize_qt(quat); - - rv3d->view = RV3D_VIEW_USER; -} - -static void viewroll_apply(ViewOpsData *vod, int x, int y) -{ - float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y}); - - if (angle != 0.0f) { - view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle); - } - - if (vod->use_dyn_ofs) { - view3d_orbit_apply_dyn_ofs( - vod->rv3d->ofs, vod->init.ofs, vod->init.quat, vod->rv3d->viewquat, vod->dyn_ofs); - } - - if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { - view3d_boxview_sync(vod->area, vod->region); - } - - ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); - - ED_region_tag_redraw(vod->region); -} - -static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod = op->customdata; - short event_code = VIEW_PASS; - bool use_autokey = false; - int ret = OPERATOR_RUNNING_MODAL; - - /* execute the events */ - if (event->type == MOUSEMOVE) { - event_code = VIEW_APPLY; - } - else if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case VIEW_MODAL_CONFIRM: - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_MOVE: - WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - case VIEWROT_MODAL_SWITCH_ROTATE: - WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); - event_code = VIEW_CONFIRM; - break; - } - } - else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) { - /* Note this does not remove auto-keys on locked cameras. */ - copy_qt_qt(vod->rv3d->viewquat, vod->init.quat); - ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); - viewops_data_free(C, op); - return OPERATOR_CANCELLED; - } - else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { - event_code = VIEW_CONFIRM; - } - - if (event_code == VIEW_APPLY) { - viewroll_apply(vod, event->xy[0], event->xy[1]); - if (ED_screen_animation_playing(CTX_wm_manager(C))) { - use_autokey = true; - } - } - else if (event_code == VIEW_CONFIRM) { - use_autokey = true; - ret = OPERATOR_FINISHED; - } - - if (use_autokey) { - ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, false); - } - - if (ret & OPERATOR_FINISHED) { - viewops_data_free(C, op); - } - - return ret; -} - -static const EnumPropertyItem prop_view_roll_items[] = { - {0, "ANGLE", 0, "Roll Angle", "Roll the view using an angle value"}, - {V3D_VIEW_STEPLEFT, "LEFT", 0, "Roll Left", "Roll the view around to the left"}, - {V3D_VIEW_STEPRIGHT, "RIGHT", 0, "Roll Right", "Roll the view around to the right"}, - {0, NULL, 0, NULL, NULL}, -}; - -static int viewroll_exec(bContext *C, wmOperator *op) -{ - View3D *v3d; - RegionView3D *rv3d; - ARegion *region; - - if (op->customdata) { - ViewOpsData *vod = op->customdata; - region = vod->region; - v3d = vod->v3d; - } - else { - ED_view3d_context_user_region(C, &v3d, ®ion); - } - - rv3d = region->regiondata; - if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { - - ED_view3d_smooth_view_force_finish(C, v3d, region); - - int type = RNA_enum_get(op->ptr, "type"); - float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle); - float mousevec[3]; - float quat_new[4]; - - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - if (type == V3D_VIEW_STEPLEFT) { - angle = -angle; - } - - normalize_v3_v3(mousevec, rv3d->viewinv[2]); - negate_v3(mousevec); - view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle); - - const float *dyn_ofs_pt = NULL; - float dyn_ofs[3]; - if (U.uiflag & USER_ORBIT_SELECTION) { - if (view3d_orbit_calc_center(C, dyn_ofs)) { - negate_v3(dyn_ofs); - dyn_ofs_pt = dyn_ofs; - } - } - - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .quat = quat_new, - .dyn_ofs = dyn_ofs_pt, - }); - - viewops_data_free(C, op); - return OPERATOR_FINISHED; - } - - viewops_data_free(C, op); - return OPERATOR_CANCELLED; -} - -static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ViewOpsData *vod; - - bool use_angle = RNA_enum_get(op->ptr, "type") != 0; - - if (use_angle || RNA_struct_property_is_set(op->ptr, "angle")) { - viewroll_exec(C, op); - } - else { - /* makes op->customdata */ - viewops_data_alloc(C, op); - viewops_data_create(C, op, event, viewops_flag_from_prefs()); - vod = op->customdata; - vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct), - BLI_rcti_cent_y(&vod->region->winrct)}, - FLT_EPSILON); - - ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); - - /* overwrite the mouse vector with the view direction */ - normalize_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]); - negate_v3(vod->init.mousevec); - - if (event->type == MOUSEROTATE) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; - viewroll_apply(vod, event->prev_xy[0], event->prev_xy[1]); - - viewops_data_free(C, op); - return OPERATOR_FINISHED; - } - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_FINISHED; -} - -static void viewroll_cancel(bContext *C, wmOperator *op) -{ - viewops_data_free(C, op); -} - -void VIEW3D_OT_view_roll(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "View Roll"; - ot->description = "Roll the view"; - ot->idname = "VIEW3D_OT_view_roll"; - - /* api callbacks */ - ot->invoke = viewroll_invoke; - ot->exec = viewroll_exec; - ot->modal = viewroll_modal; - ot->poll = ED_operator_rv3d_user_region_poll; - ot->cancel = viewroll_cancel; - - /* flags */ - ot->flag = 0; - - /* properties */ - ot->prop = prop = RNA_def_float( - ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_enum(ot->srna, - "type", - prop_view_roll_items, - 0, - "Roll Angle Source", - "How roll angle is calculated"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -enum { - V3D_VIEW_PANLEFT = 1, - V3D_VIEW_PANRIGHT, - V3D_VIEW_PANDOWN, - V3D_VIEW_PANUP, -}; - -static const EnumPropertyItem prop_view_pan_items[] = { - {V3D_VIEW_PANLEFT, "PANLEFT", 0, "Pan Left", "Pan the view to the left"}, - {V3D_VIEW_PANRIGHT, "PANRIGHT", 0, "Pan Right", "Pan the view to the right"}, - {V3D_VIEW_PANUP, "PANUP", 0, "Pan Up", "Pan the view up"}, - {V3D_VIEW_PANDOWN, "PANDOWN", 0, "Pan Down", "Pan the view down"}, - {0, NULL, 0, NULL, NULL}, -}; - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Pan Operator - * - * Move (pan) in incremental steps. For interactive pan see #VIEW3D_OT_move. - * \{ */ - -static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - int x = 0, y = 0; - int pandir = RNA_enum_get(op->ptr, "type"); - - if (pandir == V3D_VIEW_PANRIGHT) { - x = -32; - } - else if (pandir == V3D_VIEW_PANLEFT) { - x = 32; - } - else if (pandir == V3D_VIEW_PANUP) { - y = -25; - } - else if (pandir == V3D_VIEW_PANDOWN) { - y = 25; - } - - viewops_data_alloc(C, op); - viewops_data_create(C, op, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT)); - ViewOpsData *vod = op->customdata; - - viewmove_apply(vod, vod->prev.event_xy[0] + x, vod->prev.event_xy[1] + y); - - viewops_data_free(C, op); - - return OPERATOR_FINISHED; -} - -void VIEW3D_OT_view_pan(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Pan View Direction"; - ot->description = "Pan the view in a given direction"; - ot->idname = "VIEW3D_OT_view_pan"; - - /* api callbacks */ - ot->invoke = viewpan_invoke; - ot->poll = view3d_pan_poll; - - /* flags */ - ot->flag = 0; - - /* Properties */ - ot->prop = RNA_def_enum( - ot->srna, "type", prop_view_pan_items, 0, "Pan", "Direction of View Pan"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name View Toggle Perspective/Orthographic Operator * \{ */ diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 607ca110d0f..7f872c9b1af 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -52,8 +52,6 @@ #include "view3d_intern.h" -static void do_view3d_header_buttons(bContext *C, void *arg, int event); - #define B_SEL_VERT 110 #define B_SEL_EDGE 111 #define B_SEL_FACE 112 @@ -98,101 +96,45 @@ void VIEW3D_OT_toggle_matcap_flip(wmOperatorType *ot) /** \name UI Templates * \{ */ -static void do_view3d_header_buttons(bContext *C, void *UNUSED(arg), int event) -{ - wmWindow *win = CTX_wm_window(C); - const int ctrl = win->eventstate->ctrl, shift = win->eventstate->shift; - - /* watch it: if area->win does not exist, check that when calling direct drawing routines */ - - switch (event) { - case B_SEL_VERT: - if (EDBM_selectmode_toggle_multi(C, SCE_SELECT_VERTEX, -1, shift, ctrl)) { - ED_undo_push(C, "Selectmode Set: Vertex"); - } - break; - case B_SEL_EDGE: - if (EDBM_selectmode_toggle_multi(C, SCE_SELECT_EDGE, -1, shift, ctrl)) { - ED_undo_push(C, "Selectmode Set: Edge"); - } - break; - case B_SEL_FACE: - if (EDBM_selectmode_toggle_multi(C, SCE_SELECT_FACE, -1, shift, ctrl)) { - ED_undo_push(C, "Selectmode Set: Face"); - } - break; - default: - break; - } -} - void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C) { Object *obedit = CTX_data_edit_object(C); - uiBlock *block = uiLayoutGetBlock(layout); - - UI_block_func_handle_set(block, do_view3d_header_buttons, NULL); - - if (obedit && (obedit->type == OB_MESH)) { - BMEditMesh *em = BKE_editmesh_from_object(obedit); - uiLayout *row; - uiBut *but; - - row = uiLayoutRow(layout, true); - block = uiLayoutGetBlock(row); - but = uiDefIconButBitS( - block, - UI_BTYPE_TOGGLE, - SCE_SELECT_VERTEX, - B_SEL_VERT, - ICON_VERTEXSEL, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - &em->selectmode, - 1.0, - 0.0, - 0, - 0, - TIP_("Vertex select - Shift-Click for multiple modes, Ctrl-Click contracts selection")); - UI_but_flag_disable(but, UI_BUT_UNDO); - but = uiDefIconButBitS( - block, - UI_BTYPE_TOGGLE, - SCE_SELECT_EDGE, - B_SEL_EDGE, - ICON_EDGESEL, - 0, - 0, - ceilf(UI_UNIT_X - U.pixelsize), - UI_UNIT_Y, - &em->selectmode, - 1.0, - 0.0, - 0, - 0, - TIP_("Edge select - Shift-Click for multiple modes, " - "Ctrl-Click expands/contracts selection depending on the current mode")); - UI_but_flag_disable(but, UI_BUT_UNDO); - but = uiDefIconButBitS( - block, - UI_BTYPE_TOGGLE, - SCE_SELECT_FACE, - B_SEL_FACE, - ICON_FACESEL, - 0, - 0, - ceilf(UI_UNIT_X - U.pixelsize), - UI_UNIT_Y, - &em->selectmode, - 1.0, - 0.0, - 0, - 0, - TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection")); - UI_but_flag_disable(but, UI_BUT_UNDO); + if (!obedit || obedit->type != OB_MESH) { + return; } + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + uiLayout *row = uiLayoutRow(layout, true); + + PointerRNA op_ptr; + wmOperatorType *ot = WM_operatortype_find("MESH_OT_select_mode", true); + uiItemFullO_ptr(row, + ot, + "", + ICON_VERTEXSEL, + NULL, + WM_OP_INVOKE_DEFAULT, + (em->selectmode & SCE_SELECT_VERTEX) ? UI_ITEM_O_DEPRESS : 0, + &op_ptr); + RNA_enum_set(&op_ptr, "type", SCE_SELECT_VERTEX); + uiItemFullO_ptr(row, + ot, + "", + ICON_EDGESEL, + NULL, + WM_OP_INVOKE_DEFAULT, + (em->selectmode & SCE_SELECT_EDGE) ? UI_ITEM_O_DEPRESS : 0, + &op_ptr); + RNA_enum_set(&op_ptr, "type", SCE_SELECT_EDGE); + uiItemFullO_ptr(row, + ot, + "", + ICON_FACESEL, + NULL, + WM_OP_INVOKE_DEFAULT, + (em->selectmode & SCE_SELECT_FACE) ? UI_ITEM_O_DEPRESS : 0, + &op_ptr); + RNA_enum_set(&op_ptr, "type", SCE_SELECT_FACE); } static void uiTemplatePaintModeSelection(uiLayout *layout, struct bContext *C) diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 6a1a09df316..b443ebeed94 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -32,6 +32,8 @@ struct ARegionType; struct BoundBox; struct Depsgraph; struct Object; +struct Scene; +struct ViewContext; struct ViewLayer; struct bContext; struct wmGizmoGroupType; @@ -40,13 +42,6 @@ struct wmKeyConfig; struct wmOperatorType; struct wmWindowManager; -/* drawing flags: */ -enum { - DRAW_PICKING = (1 << 0), - DRAW_CONSTCOLOR = (1 << 1), - DRAW_SCENESET = (1 << 2), -}; - /* view3d_header.c */ void VIEW3D_OT_toggle_matcap_flip(struct wmOperatorType *ot); @@ -54,84 +49,24 @@ void VIEW3D_OT_toggle_matcap_flip(struct wmOperatorType *ot); void view3d_operatortypes(void); /* view3d_edit.c */ -void VIEW3D_OT_zoom(struct wmOperatorType *ot); -void VIEW3D_OT_dolly(struct wmOperatorType *ot); void VIEW3D_OT_zoom_camera_1_to_1(struct wmOperatorType *ot); -void VIEW3D_OT_move(struct wmOperatorType *ot); -void VIEW3D_OT_rotate(struct wmOperatorType *ot); -#ifdef WITH_INPUT_NDOF -void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot); -void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot); -void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot); -void VIEW3D_OT_ndof_all(struct wmOperatorType *ot); -#endif /* WITH_INPUT_NDOF */ -void VIEW3D_OT_view_all(struct wmOperatorType *ot); -void VIEW3D_OT_view_axis(struct wmOperatorType *ot); -void VIEW3D_OT_view_camera(struct wmOperatorType *ot); -void VIEW3D_OT_view_selected(struct wmOperatorType *ot); void VIEW3D_OT_view_lock_clear(struct wmOperatorType *ot); void VIEW3D_OT_view_lock_to_active(struct wmOperatorType *ot); -void VIEW3D_OT_view_center_cursor(struct wmOperatorType *ot); -void VIEW3D_OT_view_center_pick(struct wmOperatorType *ot); void VIEW3D_OT_view_center_camera(struct wmOperatorType *ot); void VIEW3D_OT_view_center_lock(struct wmOperatorType *ot); -void VIEW3D_OT_view_pan(struct wmOperatorType *ot); void VIEW3D_OT_view_persportho(struct wmOperatorType *ot); void VIEW3D_OT_navigate(struct wmOperatorType *ot); void VIEW3D_OT_background_image_add(struct wmOperatorType *ot); void VIEW3D_OT_background_image_remove(struct wmOperatorType *ot); void VIEW3D_OT_drop_world(struct wmOperatorType *ot); -void VIEW3D_OT_view_orbit(struct wmOperatorType *ot); -void VIEW3D_OT_view_roll(struct wmOperatorType *ot); void VIEW3D_OT_clip_border(struct wmOperatorType *ot); void VIEW3D_OT_cursor3d(struct wmOperatorType *ot); void VIEW3D_OT_render_border(struct wmOperatorType *ot); void VIEW3D_OT_clear_render_border(struct wmOperatorType *ot); -void VIEW3D_OT_zoom_border(struct wmOperatorType *ot); void VIEW3D_OT_toggle_shading(struct wmOperatorType *ot); void VIEW3D_OT_toggle_xray(struct wmOperatorType *ot); -/** - * For home, center etc. - */ -void view3d_boxview_copy(struct ScrArea *area, struct ARegion *region); -/** - * Sync center/zoom view of region to others, for view transforms. - */ -void view3d_boxview_sync(struct ScrArea *area, struct ARegion *region); - -void view3d_orbit_apply_dyn_ofs(float r_ofs[3], - const float ofs_old[3], - const float viewquat_old[4], - const float viewquat_new[4], - const float dyn_ofs[3]); - -#ifdef WITH_INPUT_NDOF -struct wmNDOFMotionData; - -/** - * Called from both fly mode and walk mode, - */ -void view3d_ndof_fly(const struct wmNDOFMotionData *ndof, - struct View3D *v3d, - struct RegionView3D *rv3d, - bool use_precision, - short protectflag, - bool *r_has_translate, - bool *r_has_rotate); -#endif /* WITH_INPUT_NDOF */ - -/* view3d_navigate_fly.c */ - -void view3d_keymap(struct wmKeyConfig *keyconf); -void VIEW3D_OT_fly(struct wmOperatorType *ot); - -/* view3d_navigate_walk.c */ - -void VIEW3D_OT_walk(struct wmOperatorType *ot); - /* view3d_draw.c */ - void view3d_main_region_draw(const struct bContext *C, struct ARegion *region); /** * Information drawn on top of the solid plates and composed data. @@ -141,16 +76,16 @@ void view3d_draw_region_info(const struct bContext *C, struct ARegion *region); /* view3d_draw_legacy.c */ void ED_view3d_draw_select_loop(struct Depsgraph *depsgraph, - ViewContext *vc, - Scene *scene, + struct ViewContext *vc, + struct Scene *scene, struct ViewLayer *view_layer, - View3D *v3d, + struct View3D *v3d, struct ARegion *region, bool use_obedit_skip, bool use_nearest); void ED_view3d_draw_depth_loop(struct Depsgraph *depsgraph, - Scene *scene, + struct Scene *scene, struct ARegion *region, View3D *v3d); @@ -168,57 +103,27 @@ void VIEW3D_OT_select_lasso(struct wmOperatorType *ot); void VIEW3D_OT_select_menu(struct wmOperatorType *ot); void VIEW3D_OT_bone_select_menu(struct wmOperatorType *ot); -/* view3d_view.c */ -void VIEW3D_OT_smoothview(struct wmOperatorType *ot); -void VIEW3D_OT_camera_to_view(struct wmOperatorType *ot); -void VIEW3D_OT_camera_to_view_selected(struct wmOperatorType *ot); -void VIEW3D_OT_object_as_camera(struct wmOperatorType *ot); -void VIEW3D_OT_localview(struct wmOperatorType *ot); -void VIEW3D_OT_localview_remove_from(struct wmOperatorType *ot); +/* view3d_utils.c */ +/** + * For home, center etc. + */ +void view3d_boxview_copy(struct ScrArea *area, struct ARegion *region); +/** + * Sync center/zoom view of region to others, for view transforms. + */ +void view3d_boxview_sync(struct ScrArea *area, struct ARegion *region); bool ED_view3d_boundbox_clip_ex(const RegionView3D *rv3d, const struct BoundBox *bb, float obmat[4][4]); bool ED_view3d_boundbox_clip(RegionView3D *rv3d, const struct BoundBox *bb); -/** - * Parameters for setting the new 3D Viewport state. - * - * Each of the struct members may be NULL to signify they aren't to be adjusted. - */ -typedef struct V3D_SmoothParams { - struct Object *camera_old, *camera; - const float *ofs, *quat, *dist, *lens; - - /** Alternate rotation center, when set `ofs` must be NULL. */ - const float *dyn_ofs; -} V3D_SmoothParams; - -/** - * The arguments are the desired situation. - */ -void ED_view3d_smooth_view_ex(const struct Depsgraph *depsgraph, - struct wmWindowManager *wm, - struct wmWindow *win, - struct ScrArea *area, - struct View3D *v3d, - struct ARegion *region, - int smooth_viewtx, - const V3D_SmoothParams *sview); - -void ED_view3d_smooth_view(struct bContext *C, - struct View3D *v3d, - struct ARegion *region, - int smooth_viewtx, - const V3D_SmoothParams *sview); - -/** - * Apply the smooth-view immediately, use when we need to start a new view operation. - * (so we don't end up half-applying a view operation when pressing keys quickly). - */ -void ED_view3d_smooth_view_force_finish(struct bContext *C, - struct View3D *v3d, - struct ARegion *region); +/* view3d_view.c */ +void VIEW3D_OT_camera_to_view(struct wmOperatorType *ot); +void VIEW3D_OT_camera_to_view_selected(struct wmOperatorType *ot); +void VIEW3D_OT_object_as_camera(struct wmOperatorType *ot); +void VIEW3D_OT_localview(struct wmOperatorType *ot); +void VIEW3D_OT_localview_remove_from(struct wmOperatorType *ot); /** * \param rect: optional for picking (can be NULL). @@ -247,12 +152,7 @@ void view3d_viewmatrix_set(struct Depsgraph *depsgraph, /* Called in transform_ops.c, on each regeneration of key-maps. */ -void fly_modal_keymap(struct wmKeyConfig *keyconf); -void walk_modal_keymap(struct wmKeyConfig *keyconf); -void viewrotate_modal_keymap(struct wmKeyConfig *keyconf); -void viewmove_modal_keymap(struct wmKeyConfig *keyconf); -void viewzoom_modal_keymap(struct wmKeyConfig *keyconf); -void viewdolly_modal_keymap(struct wmKeyConfig *keyconf); +/* view3d_placement.c */ void viewplace_modal_keymap(struct wmKeyConfig *keyconf); /* view3d_buttons.c */ @@ -267,7 +167,7 @@ void view3d_buttons_register(struct ARegionType *art); * the view for first-person style navigation. */ struct View3DCameraControl *ED_view3d_cameracontrol_acquire(struct Depsgraph *depsgraph, - Scene *scene, + struct Scene *scene, View3D *v3d, RegionView3D *rv3d); /** diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c new file mode 100644 index 00000000000..98eef94d5fb --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -0,0 +1,1593 @@ +/* + * 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 spview3d + */ + +#include "DNA_curve_types.h" +#include "DNA_gpencil_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "BKE_armature.h" +#include "BKE_context.h" +#include "BKE_gpencil_geom.h" +#include "BKE_layer.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_vfont.h" + +#include "DEG_depsgraph_query.h" + +#include "ED_mesh.h" +#include "ED_particle.h" +#include "ED_screen.h" +#include "ED_transform.h" + +#include "WM_api.h" +#include "WM_message.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_resources.h" + +#include "view3d_intern.h" + +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name Navigation Polls + * \{ */ + +static bool view3d_navigation_poll_impl(bContext *C, const char viewlock) +{ + if (!ED_operator_region_view3d_active(C)) { + return false; + } + + const RegionView3D *rv3d = CTX_wm_region_view3d(C); + return !(RV3D_LOCK_FLAGS(rv3d) & viewlock); +} + +bool view3d_location_poll(bContext *C) +{ + return view3d_navigation_poll_impl(C, RV3D_LOCK_LOCATION); +} + +bool view3d_rotation_poll(bContext *C) +{ + return view3d_navigation_poll_impl(C, RV3D_LOCK_ROTATION); +} + +bool view3d_zoom_or_dolly_poll(bContext *C) +{ + return view3d_navigation_poll_impl(C, RV3D_LOCK_ZOOM_AND_DOLLY); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generic View Operator Properties + * \{ */ + +void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPropFlag flag) +{ + if (flag & V3D_OP_PROP_MOUSE_CO) { + PropertyRNA *prop; + prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Region Position X", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Region Position Y", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + } + if (flag & V3D_OP_PROP_DELTA) { + RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX); + } + if (flag & V3D_OP_PROP_USE_ALL_REGIONS) { + PropertyRNA *prop; + prop = RNA_def_boolean( + ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } + if (flag & V3D_OP_PROP_USE_MOUSE_INIT) { + WM_operator_properties_use_cursor_init(ot); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generic View Operator Custom-Data + * \{ */ + +void calctrackballvec(const rcti *rect, const int event_xy[2], float r_dir[3]) +{ + const float radius = V3D_OP_TRACKBALLSIZE; + const float t = radius / (float)M_SQRT2; + const float size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)}; + /* Aspect correct so dragging in a non-square view doesn't squash the direction. + * So diagonal motion rotates the same direction the cursor is moving. */ + const float size_min = min_ff(size[0], size[1]); + const float aspect[2] = {size_min / size[0], size_min / size[1]}; + + /* Normalize x and y. */ + r_dir[0] = (event_xy[0] - BLI_rcti_cent_x(rect)) / ((size[0] * aspect[0]) / 2.0); + r_dir[1] = (event_xy[1] - BLI_rcti_cent_y(rect)) / ((size[1] * aspect[1]) / 2.0); + const float d = len_v2(r_dir); + if (d < t) { + /* Inside sphere. */ + r_dir[2] = sqrtf(square_f(radius) - square_f(d)); + } + else { + /* On hyperbola. */ + r_dir[2] = square_f(t) / d; + } +} + +void view3d_orbit_apply_dyn_ofs(float r_ofs[3], + const float ofs_old[3], + const float viewquat_old[4], + const float viewquat_new[4], + const float dyn_ofs[3]) +{ + float q[4]; + invert_qt_qt_normalized(q, viewquat_old); + mul_qt_qtqt(q, q, viewquat_new); + + invert_qt_normalized(q); + + sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs); + mul_qt_v3(q, r_ofs); + add_v3_v3(r_ofs, dyn_ofs); +} + +void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4]) +{ + if (vod->use_dyn_ofs) { + RegionView3D *rv3d = vod->rv3d; + view3d_orbit_apply_dyn_ofs( + rv3d->ofs, vod->init.ofs, vod->init.quat, viewquat_new, vod->dyn_ofs); + } +} + +bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3]) +{ + static float lastofs[3] = {0, 0, 0}; + bool is_set = false; + + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); + View3D *v3d = CTX_wm_view3d(C); + Object *ob_act_eval = OBACT(view_layer_eval); + Object *ob_act = DEG_get_original_object(ob_act_eval); + + if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) && + /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */ + ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0) { + /* in case of sculpting use last average stroke position as a rotation + * center, in other cases it's not clear what rotation center shall be + * so just rotate around object origin + */ + if (ob_act->mode & + (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { + float stroke[3]; + BKE_paint_stroke_get_average(scene, ob_act_eval, stroke); + copy_v3_v3(lastofs, stroke); + } + else { + copy_v3_v3(lastofs, ob_act_eval->obmat[3]); + } + is_set = true; + } + else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) { + Curve *cu = ob_act_eval->data; + EditFont *ef = cu->editfont; + + zero_v3(lastofs); + for (int i = 0; i < 4; i++) { + add_v2_v2(lastofs, ef->textcurs[i]); + } + mul_v2_fl(lastofs, 1.0f / 4.0f); + + mul_m4_v3(ob_act_eval->obmat, lastofs); + + is_set = true; + } + else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) { + /* object mode use boundbox centers */ + Base *base_eval; + uint tot = 0; + float select_center[3]; + + zero_v3(select_center); + for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) { + if (BASE_SELECTED(v3d, base_eval)) { + /* use the boundbox if we can */ + Object *ob_eval = base_eval->object; + + if (ob_eval->runtime.bb && !(ob_eval->runtime.bb->flag & BOUNDBOX_DIRTY)) { + float cent[3]; + + BKE_boundbox_calc_center_aabb(ob_eval->runtime.bb, cent); + + mul_m4_v3(ob_eval->obmat, cent); + add_v3_v3(select_center, cent); + } + else { + add_v3_v3(select_center, ob_eval->obmat[3]); + } + tot++; + } + } + if (tot) { + mul_v3_fl(select_center, 1.0f / (float)tot); + copy_v3_v3(lastofs, select_center); + is_set = true; + } + } + else { + /* If there's no selection, `lastofs` is unmodified and last value since static. */ + is_set = calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, lastofs, NULL); + } + + copy_v3_v3(r_dyn_ofs, lastofs); + + return is_set; +} + +static enum eViewOpsFlag viewops_flag_from_args(bool use_select, bool use_depth) +{ + enum eViewOpsFlag flag = 0; + if (use_select) { + flag |= VIEWOPS_FLAG_ORBIT_SELECT; + } + if (use_depth) { + flag |= VIEWOPS_FLAG_DEPTH_NAVIGATE; + } + + return flag; +} + +enum eViewOpsFlag viewops_flag_from_prefs(void) +{ + return viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0, + (U.uiflag & USER_DEPTH_NAVIGATE) != 0); +} + +ViewOpsData *viewops_data_create(bContext *C, const wmEvent *event, enum eViewOpsFlag viewops_flag) +{ + ViewOpsData *vod = MEM_callocN(sizeof(ViewOpsData), __func__); + + /* Store data. */ + vod->bmain = CTX_data_main(C); + vod->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + vod->scene = CTX_data_scene(C); + vod->area = CTX_wm_area(C); + vod->region = CTX_wm_region(C); + vod->v3d = vod->area->spacedata.first; + vod->rv3d = vod->region->regiondata; + + Depsgraph *depsgraph = vod->depsgraph; + RegionView3D *rv3d = vod->rv3d; + + /* Could do this more nicely. */ + if ((viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) == 0) { + viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE; + } + + /* we need the depth info before changing any viewport options */ + if (viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE) { + float fallback_depth_pt[3]; + + view3d_operator_needs_opengl(C); /* Needed for Z-buffer drawing. */ + + negate_v3_v3(fallback_depth_pt, rv3d->ofs); + + vod->use_dyn_ofs = ED_view3d_autodist( + depsgraph, vod->region, vod->v3d, event->mval, vod->dyn_ofs, true, fallback_depth_pt); + } + else { + vod->use_dyn_ofs = false; + } + + if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) { + if (ED_view3d_persp_ensure(depsgraph, vod->v3d, vod->region)) { + /* If we're switching from camera view to the perspective one, + * need to tag viewport update, so camera view and borders are properly updated. */ + ED_region_tag_redraw(vod->region); + } + } + + /* set the view from the camera, if view locking is enabled. + * we may want to make this optional but for now its needed always */ + ED_view3d_camera_lock_init(depsgraph, vod->v3d, vod->rv3d); + + vod->init.persp = rv3d->persp; + vod->init.dist = rv3d->dist; + vod->init.camzoom = rv3d->camzoom; + copy_qt_qt(vod->init.quat, rv3d->viewquat); + copy_v2_v2_int(vod->init.event_xy, event->xy); + copy_v2_v2_int(vod->prev.event_xy, event->xy); + + if (viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) { + zero_v2_int(vod->init.event_xy_offset); + } + else { + /* Simulate the event starting in the middle of the region. */ + vod->init.event_xy_offset[0] = BLI_rcti_cent_x(&vod->region->winrct) - event->xy[0]; + vod->init.event_xy_offset[1] = BLI_rcti_cent_y(&vod->region->winrct) - event->xy[1]; + } + + vod->init.event_type = event->type; + copy_v3_v3(vod->init.ofs, rv3d->ofs); + + copy_qt_qt(vod->curr.viewquat, rv3d->viewquat); + + if (viewops_flag & VIEWOPS_FLAG_ORBIT_SELECT) { + float ofs[3]; + if (view3d_orbit_calc_center(C, ofs) || (vod->use_dyn_ofs == false)) { + vod->use_dyn_ofs = true; + negate_v3_v3(vod->dyn_ofs, ofs); + viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE; + } + } + + if (viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE) { + if (vod->use_dyn_ofs) { + if (rv3d->is_persp) { + float my_origin[3]; /* Original #RegionView3D.ofs. */ + float my_pivot[3]; /* View pivot. */ + float dvec[3]; + + /* locals for dist correction */ + float mat[3][3]; + float upvec[3]; + + 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 won't go bad when dist was zero. */ + + /* remove dist value */ + upvec[0] = upvec[1] = 0; + upvec[2] = rv3d->dist; + copy_m3_m4(mat, rv3d->viewinv); + + mul_m3_v3(mat, upvec); + sub_v3_v3v3(my_pivot, rv3d->ofs, upvec); + negate_v3(my_pivot); /* ofs is flipped */ + + /* find a new ofs value that is along the view axis + * (rather than the mouse location) */ + closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin); + vod->init.dist = rv3d->dist = len_v3v3(my_pivot, dvec); + + negate_v3_v3(rv3d->ofs, dvec); + } + else { + const float mval_region_mid[2] = {(float)vod->region->winx / 2.0f, + (float)vod->region->winy / 2.0f}; + + ED_view3d_win_to_3d(vod->v3d, vod->region, vod->dyn_ofs, mval_region_mid, rv3d->ofs); + negate_v3(rv3d->ofs); + } + negate_v3(vod->dyn_ofs); + copy_v3_v3(vod->init.ofs, rv3d->ofs); + } + } + + /* For dolly */ + ED_view3d_win_to_vector(vod->region, (const float[2]){UNPACK2(event->mval)}, vod->init.mousevec); + + { + int event_xy_offset[2]; + add_v2_v2v2_int(event_xy_offset, event->xy, vod->init.event_xy_offset); + + /* For rotation with trackball rotation. */ + calctrackballvec(&vod->region->winrct, event_xy_offset, vod->init.trackvec); + } + + { + float tvec[3]; + negate_v3_v3(tvec, rv3d->ofs); + vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL); + } + + vod->reverse = 1.0f; + if (rv3d->persmat[2][1] < 0.0f) { + vod->reverse = -1.0f; + } + + rv3d->rflag |= RV3D_NAVIGATING; + + return vod; +} + +void viewops_data_free(bContext *C, ViewOpsData *vod) +{ + ARegion *region; + if (vod) { + region = vod->region; + vod->rv3d->rflag &= ~RV3D_NAVIGATING; + + if (vod->timer) { + WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer); + } + + if (vod->init.dial) { + MEM_freeN(vod->init.dial); + } + + MEM_freeN(vod); + } + else { + region = CTX_wm_region(C); + } + + /* Need to redraw because drawing code uses RV3D_NAVIGATING to draw + * faster while navigation operator runs. */ + ED_region_tag_redraw(region); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generic View Operator Utilities + * \{ */ + +/** + * \param align_to_quat: When not NULL, set the axis relative to this rotation. + */ +static void axis_set_view(bContext *C, + View3D *v3d, + ARegion *region, + const float quat_[4], + char view, + char view_axis_roll, + int perspo, + const float *align_to_quat, + const int smooth_viewtx) +{ + RegionView3D *rv3d = region->regiondata; /* no NULL check is needed, poll checks */ + float quat[4]; + const short orig_persp = rv3d->persp; + + normalize_qt_qt(quat, quat_); + + if (align_to_quat) { + mul_qt_qtqt(quat, quat, align_to_quat); + rv3d->view = view = RV3D_VIEW_USER; + rv3d->view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; + } + + if (align_to_quat == NULL) { + rv3d->view = view; + rv3d->view_axis_roll = view_axis_roll; + } + + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) { + ED_region_tag_redraw(region); + return; + } + + if (U.uiflag & USER_AUTOPERSP) { + rv3d->persp = RV3D_VIEW_IS_AXIS(view) ? RV3D_ORTHO : perspo; + } + else if (rv3d->persp == RV3D_CAMOB) { + rv3d->persp = perspo; + } + + if (rv3d->persp == RV3D_CAMOB && v3d->camera) { + /* to camera */ + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .camera_old = v3d->camera, + .ofs = rv3d->ofs, + .quat = quat, + }); + } + else if (orig_persp == RV3D_CAMOB && v3d->camera) { + /* from camera */ + float ofs[3], dist; + + copy_v3_v3(ofs, rv3d->ofs); + dist = rv3d->dist; + + /* so we animate _from_ the camera location */ + Object *camera_eval = DEG_get_evaluated_object(CTX_data_ensure_evaluated_depsgraph(C), + v3d->camera); + ED_view3d_from_object(camera_eval, rv3d->ofs, NULL, &rv3d->dist, NULL); + + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .camera_old = camera_eval, + .ofs = ofs, + .quat = quat, + .dist = &dist, + }); + } + else { + /* rotate around selection */ + const float *dyn_ofs_pt = NULL; + float dyn_ofs[3]; + + if (U.uiflag & USER_ORBIT_SELECTION) { + if (view3d_orbit_calc_center(C, dyn_ofs)) { + negate_v3(dyn_ofs); + dyn_ofs_pt = dyn_ofs; + } + } + + /* no camera involved */ + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .quat = quat, + .dyn_ofs = dyn_ofs_pt, + }); + } +} + +void viewmove_apply(ViewOpsData *vod, int x, int y) +{ + if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) { + vod->rv3d->ofs_lock[0] -= ((vod->prev.event_xy[0] - x) * 2.0f) / (float)vod->region->winx; + vod->rv3d->ofs_lock[1] -= ((vod->prev.event_xy[1] - y) * 2.0f) / (float)vod->region->winy; + } + else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) { + const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f; + vod->rv3d->camdx += (vod->prev.event_xy[0] - x) / (vod->region->winx * zoomfac); + vod->rv3d->camdy += (vod->prev.event_xy[1] - y) / (vod->region->winy * zoomfac); + CLAMP(vod->rv3d->camdx, -1.0f, 1.0f); + CLAMP(vod->rv3d->camdy, -1.0f, 1.0f); + } + else { + float dvec[3]; + float mval_f[2]; + + mval_f[0] = x - vod->prev.event_xy[0]; + mval_f[1] = y - vod->prev.event_xy[1]; + ED_view3d_win_to_delta(vod->region, mval_f, dvec, vod->init.zfac); + + add_v3_v3(vod->rv3d->ofs, dvec); + + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { + view3d_boxview_sync(vod->area, vod->region); + } + } + + vod->prev.event_xy[0] = x; + vod->prev.event_xy[1] = y; + + ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); + + ED_region_tag_redraw(vod->region); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View All Operator + * + * Move & Zoom the view to fit all of its contents. + * \{ */ + +static bool view3d_object_skip_minmax(const View3D *v3d, + const RegionView3D *rv3d, + const Object *ob, + const bool skip_camera, + bool *r_only_center) +{ + BLI_assert(ob->id.orig_id == NULL); + *r_only_center = false; + + if (skip_camera && (ob == v3d->camera)) { + return true; + } + + if ((ob->type == OB_EMPTY) && (ob->empty_drawtype == OB_EMPTY_IMAGE) && + !BKE_object_empty_image_frame_is_visible_in_view3d(ob, rv3d)) { + *r_only_center = true; + return false; + } + + return false; +} + +static void view3d_object_calc_minmax(Depsgraph *depsgraph, + Scene *scene, + Object *ob_eval, + const bool only_center, + float min[3], + float max[3]) +{ + /* Account for duplis. */ + if (BKE_object_minmax_dupli(depsgraph, scene, ob_eval, min, max, false) == 0) { + /* Use if duplis aren't found. */ + if (only_center) { + minmax_v3v3_v3(min, max, ob_eval->obmat[3]); + } + else { + BKE_object_minmax(ob_eval, min, max, false); + } + } +} + +static void view3d_from_minmax(bContext *C, + View3D *v3d, + ARegion *region, + const float min[3], + const float max[3], + bool ok_dist, + const int smooth_viewtx) +{ + RegionView3D *rv3d = region->regiondata; + float afm[3]; + float size; + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + /* SMOOTHVIEW */ + float new_ofs[3]; + float new_dist; + + sub_v3_v3v3(afm, max, min); + size = max_fff(afm[0], afm[1], afm[2]); + + if (ok_dist) { + char persp; + + if (rv3d->is_persp) { + if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) { + persp = RV3D_CAMOB; + } + else { + persp = RV3D_PERSP; + } + } + else { /* ortho */ + if (size < 0.0001f) { + /* bounding box was a single point so do not zoom */ + ok_dist = false; + } + else { + /* adjust zoom so it looks nicer */ + persp = RV3D_ORTHO; + } + } + + if (ok_dist) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + new_dist = ED_view3d_radius_to_dist( + v3d, region, depsgraph, persp, true, (size / 2) * VIEW3D_MARGIN); + if (rv3d->is_persp) { + /* don't zoom closer than the near clipping plane */ + new_dist = max_ff(new_dist, v3d->clip_start * 1.5f); + } + } + } + + mid_v3_v3v3(new_ofs, min, max); + negate_v3(new_ofs); + + if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) { + rv3d->persp = RV3D_PERSP; + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .camera_old = v3d->camera, + .ofs = new_ofs, + .dist = ok_dist ? &new_dist : NULL, + }); + } + else { + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .ofs = new_ofs, + .dist = ok_dist ? &new_dist : NULL, + }); + } + + /* Smooth-view does view-lock #RV3D_BOXVIEW copy. */ +} + +/** + * Same as #view3d_from_minmax but for all regions (except cameras). + */ +static void view3d_from_minmax_multi(bContext *C, + View3D *v3d, + const float min[3], + const float max[3], + const bool ok_dist, + const int smooth_viewtx) +{ + ScrArea *area = CTX_wm_area(C); + ARegion *region; + for (region = area->regionbase.first; region; region = region->next) { + if (region->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = region->regiondata; + /* when using all regions, don't jump out of camera view, + * but _do_ allow locked cameras to be moved */ + if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { + view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); + } + } + } +} + +static int view3d_all_exec(bContext *C, wmOperator *op) +{ + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); + Base *base_eval; + const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions"); + const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) || + /* any one of the regions may be locked */ + (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA)); + const bool center = RNA_boolean_get(op->ptr, "center"); + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + float min[3], max[3]; + bool changed = false; + + if (center) { + /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */ + View3DCursor *cursor = &scene->cursor; + zero_v3(min); + zero_v3(max); + zero_v3(cursor->location); + float mat3[3][3]; + unit_m3(mat3); + BKE_scene_cursor_mat3_to_rot(cursor, mat3, false); + } + else { + INIT_MINMAX(min, max); + } + + for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) { + if (BASE_VISIBLE(v3d, base_eval)) { + bool only_center = false; + Object *ob = DEG_get_original_object(base_eval->object); + if (view3d_object_skip_minmax(v3d, rv3d, ob, skip_camera, &only_center)) { + continue; + } + view3d_object_calc_minmax(depsgraph, scene, base_eval->object, only_center, min, max); + changed = true; + } + } + + if (center) { + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + WM_msg_publish_rna_prop(mbus, &scene->id, &scene->cursor, View3DCursor, location); + + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); + } + + if (!changed) { + ED_region_tag_redraw(region); + /* TODO: should this be cancel? + * I think no, because we always move the cursor, with or without + * object, but in this case there is no change in the scene, + * only the cursor so I choice a ED_region_tag like + * view3d_smooth_view do for the center_cursor. + * See bug T22640. + */ + return OPERATOR_FINISHED; + } + + if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { + /* This is an approximation, see function documentation for details. */ + ED_view3d_clipping_clamp_minmax(rv3d, min, max); + } + + if (use_all_regions) { + view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx); + } + else { + view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx); + } + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_view_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Frame All"; + ot->description = "View all objects in scene"; + ot->idname = "VIEW3D_OT_view_all"; + + /* api callbacks */ + ot->exec = view3d_all_exec; + ot->poll = ED_operator_region_view3d_active; + + /* flags */ + ot->flag = 0; + + /* properties */ + view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS); + RNA_def_boolean(ot->srna, "center", 0, "Center", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Frame Selected Operator + * + * Move & Zoom the view to fit selected contents. + * \{ */ + +static int viewselected_exec(bContext *C, wmOperator *op) +{ + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); + Object *ob_eval = OBACT(view_layer_eval); + Object *obedit = CTX_data_edit_object(C); + const bGPdata *gpd_eval = ob_eval && (ob_eval->type == OB_GPENCIL) ? ob_eval->data : NULL; + const bool is_gp_edit = gpd_eval ? GPENCIL_ANY_MODE(gpd_eval) : false; + const bool is_face_map = ((is_gp_edit == false) && region->gizmo_map && + WM_gizmomap_is_any_selected(region->gizmo_map)); + float min[3], max[3]; + bool ok = false, ok_dist = true; + const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions"); + const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) || + /* any one of the regions may be locked */ + (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA)); + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + INIT_MINMAX(min, max); + if (is_face_map) { + ob_eval = NULL; + } + + if (ob_eval && (ob_eval->mode & OB_MODE_WEIGHT_PAINT)) { + /* hard-coded exception, we look for the one selected armature */ + /* this is weak code this way, we should make a generic + * active/selection callback interface once... */ + Base *base_eval; + for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) { + if (BASE_SELECTED_EDITABLE(v3d, base_eval)) { + if (base_eval->object->type == OB_ARMATURE) { + if (base_eval->object->mode & OB_MODE_POSE) { + break; + } + } + } + } + if (base_eval) { + ob_eval = base_eval->object; + } + } + + if (is_gp_edit) { + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* we're only interested in selected points here... */ + if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) { + ok |= BKE_gpencil_stroke_minmax(gps, true, min, max); + } + if (gps->editcurve != NULL) { + for (int i = 0; i < gps->editcurve->tot_curve_points; i++) { + BezTriple *bezt = &gps->editcurve->curve_points[i].bezt; + if ((bezt->f1 & SELECT)) { + minmax_v3v3_v3(min, max, bezt->vec[0]); + ok = true; + } + if ((bezt->f2 & SELECT)) { + minmax_v3v3_v3(min, max, bezt->vec[1]); + ok = true; + } + if ((bezt->f3 & SELECT)) { + minmax_v3v3_v3(min, max, bezt->vec[2]); + ok = true; + } + } + } + } + CTX_DATA_END; + + if ((ob_eval) && (ok)) { + mul_m4_v3(ob_eval->obmat, min); + mul_m4_v3(ob_eval->obmat, max); + } + } + else if (is_face_map) { + ok = WM_gizmomap_minmax(region->gizmo_map, true, true, min, max); + } + else if (obedit) { + /* only selected */ + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer_eval, v3d, obedit->type, obedit->mode, ob_eval_iter) { + ok |= ED_view3d_minmax_verts(ob_eval_iter, min, max); + } + FOREACH_OBJECT_IN_MODE_END; + } + else if (ob_eval && (ob_eval->mode & OB_MODE_POSE)) { + FOREACH_OBJECT_IN_MODE_BEGIN ( + view_layer_eval, v3d, ob_eval->type, ob_eval->mode, ob_eval_iter) { + ok |= BKE_pose_minmax(ob_eval_iter, min, max, true, true); + } + FOREACH_OBJECT_IN_MODE_END; + } + else if (BKE_paint_select_face_test(ob_eval)) { + ok = paintface_minmax(ob_eval, min, max); + } + else if (ob_eval && (ob_eval->mode & OB_MODE_PARTICLE_EDIT)) { + ok = PE_minmax(depsgraph, scene, CTX_data_view_layer(C), min, max); + } + else if (ob_eval && (ob_eval->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | + OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) { + BKE_paint_stroke_get_average(scene, ob_eval, min); + copy_v3_v3(max, min); + ok = true; + ok_dist = 0; /* don't zoom */ + } + else { + Base *base_eval; + for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) { + if (BASE_SELECTED(v3d, base_eval)) { + bool only_center = false; + Object *ob = DEG_get_original_object(base_eval->object); + if (view3d_object_skip_minmax(v3d, rv3d, ob, skip_camera, &only_center)) { + continue; + } + view3d_object_calc_minmax(depsgraph, scene, base_eval->object, only_center, min, max); + ok = 1; + } + } + } + + if (ok == 0) { + return OPERATOR_FINISHED; + } + + if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { + /* This is an approximation, see function documentation for details. */ + ED_view3d_clipping_clamp_minmax(rv3d, min, max); + } + + if (use_all_regions) { + view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx); + } + else { + view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); + } + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_view_selected(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Frame Selected"; + ot->description = "Move the view to the selection center"; + ot->idname = "VIEW3D_OT_view_selected"; + + /* api callbacks */ + ot->exec = viewselected_exec; + ot->poll = view3d_zoom_or_dolly_poll; + + /* flags */ + ot->flag = 0; + + /* properties */ + view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Center Cursor Operator + * \{ */ + +static int viewcenter_cursor_exec(bContext *C, wmOperator *op) +{ + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + Scene *scene = CTX_data_scene(C); + + if (rv3d) { + ARegion *region = CTX_wm_region(C); + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + /* non camera center */ + float new_ofs[3]; + negate_v3_v3(new_ofs, scene->cursor.location); + ED_view3d_smooth_view( + C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs}); + + /* Smooth view does view-lock #RV3D_BOXVIEW copy. */ + } + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_view_center_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Center View to Cursor"; + ot->description = "Center the view so that the cursor is in the middle of the view"; + ot->idname = "VIEW3D_OT_view_center_cursor"; + + /* api callbacks */ + ot->exec = viewcenter_cursor_exec; + ot->poll = view3d_location_poll; + + /* flags */ + ot->flag = 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Center Pick Operator + * \{ */ + +static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + ARegion *region = CTX_wm_region(C); + + if (rv3d) { + struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + float new_ofs[3]; + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + view3d_operator_needs_opengl(C); + + if (ED_view3d_autodist(depsgraph, region, v3d, event->mval, new_ofs, false, NULL)) { + /* pass */ + } + else { + /* fallback to simple pan */ + negate_v3_v3(new_ofs, rv3d->ofs); + ED_view3d_win_to_3d_int(v3d, region, new_ofs, event->mval, new_ofs); + } + negate_v3(new_ofs); + ED_view3d_smooth_view( + C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs}); + } + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_view_center_pick(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Center View to Mouse"; + ot->description = "Center the view to the Z-depth position under the mouse cursor"; + ot->idname = "VIEW3D_OT_view_center_pick"; + + /* api callbacks */ + ot->invoke = viewcenter_pick_invoke; + ot->poll = view3d_location_poll; + + /* flags */ + ot->flag = 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Axis Operator + * \{ */ + +static const EnumPropertyItem prop_view_items[] = { + {RV3D_VIEW_LEFT, "LEFT", ICON_TRIA_LEFT, "Left", "View from the left"}, + {RV3D_VIEW_RIGHT, "RIGHT", ICON_TRIA_RIGHT, "Right", "View from the right"}, + {RV3D_VIEW_BOTTOM, "BOTTOM", ICON_TRIA_DOWN, "Bottom", "View from the bottom"}, + {RV3D_VIEW_TOP, "TOP", ICON_TRIA_UP, "Top", "View from the top"}, + {RV3D_VIEW_FRONT, "FRONT", 0, "Front", "View from the front"}, + {RV3D_VIEW_BACK, "BACK", 0, "Back", "View from the back"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int view_axis_exec(bContext *C, wmOperator *op) +{ + View3D *v3d; + ARegion *region; + RegionView3D *rv3d; + static int perspo = RV3D_PERSP; + int viewnum; + int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + /* no NULL check is needed, poll checks */ + ED_view3d_context_user_region(C, &v3d, ®ion); + rv3d = region->regiondata; + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + viewnum = RNA_enum_get(op->ptr, "type"); + + float align_quat_buf[4]; + float *align_quat = NULL; + + if (RNA_boolean_get(op->ptr, "align_active")) { + /* align to active object */ + Object *obact = CTX_data_active_object(C); + if (obact != NULL) { + float twmat[3][3]; + struct ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obedit = CTX_data_edit_object(C); + /* same as transform gizmo when normal is set */ + ED_getTransformOrientationMatrix(view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat); + align_quat = align_quat_buf; + mat3_to_quat(align_quat, twmat); + invert_qt_normalized(align_quat); + } + } + + if (RNA_boolean_get(op->ptr, "relative")) { + float quat_rotate[4]; + float quat_test[4]; + + if (viewnum == RV3D_VIEW_LEFT) { + axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], -M_PI / 2.0f); + } + else if (viewnum == RV3D_VIEW_RIGHT) { + axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], M_PI / 2.0f); + } + else if (viewnum == RV3D_VIEW_TOP) { + axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], -M_PI / 2.0f); + } + else if (viewnum == RV3D_VIEW_BOTTOM) { + axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI / 2.0f); + } + else if (viewnum == RV3D_VIEW_FRONT) { + unit_qt(quat_rotate); + } + else if (viewnum == RV3D_VIEW_BACK) { + axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI); + } + else { + BLI_assert(0); + } + + mul_qt_qtqt(quat_test, rv3d->viewquat, quat_rotate); + + float angle_best = FLT_MAX; + int view_best = -1; + int view_axis_roll_best = -1; + for (int i = RV3D_VIEW_FRONT; i <= RV3D_VIEW_BOTTOM; i++) { + for (int j = RV3D_VIEW_AXIS_ROLL_0; j <= RV3D_VIEW_AXIS_ROLL_270; j++) { + float quat_axis[4]; + ED_view3d_quat_from_axis_view(i, j, quat_axis); + if (align_quat) { + mul_qt_qtqt(quat_axis, quat_axis, align_quat); + } + const float angle_test = fabsf(angle_signed_qtqt(quat_axis, quat_test)); + if (angle_best > angle_test) { + angle_best = angle_test; + view_best = i; + view_axis_roll_best = j; + } + } + } + if (view_best == -1) { + view_best = RV3D_VIEW_FRONT; + view_axis_roll_best = RV3D_VIEW_AXIS_ROLL_0; + } + + /* Disallow non-upright views in turn-table modes, + * it's too difficult to navigate out of them. */ + if ((U.flag & USER_TRACKBALL) == 0) { + if (!ELEM(view_best, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) { + view_axis_roll_best = RV3D_VIEW_AXIS_ROLL_0; + } + } + + viewnum = view_best; + view_axis_roll = view_axis_roll_best; + } + + /* Use this to test if we started out with a camera */ + const int nextperspo = (rv3d->persp == RV3D_CAMOB) ? rv3d->lpersp : perspo; + float quat[4]; + ED_view3d_quat_from_axis_view(viewnum, view_axis_roll, quat); + axis_set_view( + C, v3d, region, quat, viewnum, view_axis_roll, nextperspo, align_quat, smooth_viewtx); + + perspo = rv3d->persp; + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_view_axis(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "View Axis"; + ot->description = "Use a preset viewpoint"; + ot->idname = "VIEW3D_OT_view_axis"; + + /* api callbacks */ + ot->exec = view_axis_exec; + ot->poll = ED_operator_rv3d_user_region_poll; + + /* flags */ + ot->flag = 0; + + ot->prop = RNA_def_enum(ot->srna, "type", prop_view_items, 0, "View", "Preset viewpoint to use"); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean( + ot->srna, "align_active", 0, "Align Active", "Align to the active object's axis"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean( + ot->srna, "relative", 0, "Relative", "Rotate relative to the current orientation"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Camera Operator + * \{ */ + +static int view_camera_exec(bContext *C, wmOperator *op) +{ + View3D *v3d; + ARegion *region; + RegionView3D *rv3d; + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + /* no NULL check is needed, poll checks */ + ED_view3d_context_user_region(C, &v3d, ®ion); + rv3d = region->regiondata; + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) == 0) { + ViewLayer *view_layer = CTX_data_view_layer(C); + Scene *scene = CTX_data_scene(C); + + if (rv3d->persp != RV3D_CAMOB) { + Object *ob = OBACT(view_layer); + + if (!rv3d->smooth_timer) { + /* store settings of current view before allowing overwriting with camera view + * only if we're not currently in a view transition */ + + ED_view3d_lastview_store(rv3d); + } + + /* first get the default camera for the view lock type */ + if (v3d->scenelock) { + /* sets the camera view if available */ + v3d->camera = scene->camera; + } + else { + /* use scene camera if one is not set (even though we're unlocked) */ + if (v3d->camera == NULL) { + v3d->camera = scene->camera; + } + } + + /* if the camera isn't found, check a number of options */ + if (v3d->camera == NULL && ob && ob->type == OB_CAMERA) { + v3d->camera = ob; + } + + if (v3d->camera == NULL) { + v3d->camera = BKE_view_layer_camera_find(view_layer); + } + + /* couldn't find any useful camera, bail out */ + if (v3d->camera == NULL) { + return OPERATOR_CANCELLED; + } + + /* important these don't get out of sync for locked scenes */ + if (v3d->scenelock && scene->camera != v3d->camera) { + scene->camera = v3d->camera; + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); + } + + /* finally do snazzy view zooming */ + rv3d->persp = RV3D_CAMOB; + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .camera = v3d->camera, + .ofs = rv3d->ofs, + .quat = rv3d->viewquat, + .dist = &rv3d->dist, + .lens = &v3d->lens, + }); + } + else { + /* return to settings of last view */ + /* does view3d_smooth_view too */ + axis_set_view(C, + v3d, + region, + rv3d->lviewquat, + rv3d->lview, + rv3d->lview_axis_roll, + rv3d->lpersp, + NULL, + smooth_viewtx); + } + } + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_view_camera(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Camera"; + ot->description = "Toggle the camera view"; + ot->idname = "VIEW3D_OT_view_camera"; + + /* api callbacks */ + ot->exec = view_camera_exec; + ot->poll = ED_operator_rv3d_user_region_poll; + + /* flags */ + ot->flag = 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Orbit Operator + * + * Rotate (orbit) in incremental steps. For interactive orbit see #VIEW3D_OT_rotate. + * \{ */ + +enum { + V3D_VIEW_STEPLEFT = 1, + V3D_VIEW_STEPRIGHT, + V3D_VIEW_STEPDOWN, + V3D_VIEW_STEPUP, +}; + +static const EnumPropertyItem prop_view_orbit_items[] = { + {V3D_VIEW_STEPLEFT, "ORBITLEFT", 0, "Orbit Left", "Orbit the view around to the left"}, + {V3D_VIEW_STEPRIGHT, "ORBITRIGHT", 0, "Orbit Right", "Orbit the view around to the right"}, + {V3D_VIEW_STEPUP, "ORBITUP", 0, "Orbit Up", "Orbit the view up"}, + {V3D_VIEW_STEPDOWN, "ORBITDOWN", 0, "Orbit Down", "Orbit the view down"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int vieworbit_exec(bContext *C, wmOperator *op) +{ + View3D *v3d; + ARegion *region; + RegionView3D *rv3d; + int orbitdir; + char view_opposite; + PropertyRNA *prop_angle = RNA_struct_find_property(op->ptr, "angle"); + float angle = RNA_property_is_set(op->ptr, prop_angle) ? + RNA_property_float_get(op->ptr, prop_angle) : + DEG2RADF(U.pad_rot_angle); + + /* no NULL check is needed, poll checks */ + v3d = CTX_wm_view3d(C); + region = CTX_wm_region(C); + rv3d = region->regiondata; + + /* support for switching to the opposite view (even when in locked views) */ + view_opposite = (fabsf(angle) == (float)M_PI) ? ED_view3d_axis_view_opposite(rv3d->view) : + RV3D_VIEW_USER; + orbitdir = RNA_enum_get(op->ptr, "type"); + + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) && (view_opposite == RV3D_VIEW_USER)) { + /* no NULL check is needed, poll checks */ + ED_view3d_context_user_region(C, &v3d, ®ion); + rv3d = region->regiondata; + } + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0 || (view_opposite != RV3D_VIEW_USER)) { + if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { + int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + float quat_mul[4]; + float quat_new[4]; + + if (view_opposite == RV3D_VIEW_USER) { + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_persp_ensure(depsgraph, v3d, region); + } + + if (ELEM(orbitdir, V3D_VIEW_STEPLEFT, V3D_VIEW_STEPRIGHT)) { + if (orbitdir == V3D_VIEW_STEPRIGHT) { + angle = -angle; + } + + /* z-axis */ + axis_angle_to_quat_single(quat_mul, 'Z', angle); + } + else { + + if (orbitdir == V3D_VIEW_STEPDOWN) { + angle = -angle; + } + + /* horizontal axis */ + axis_angle_to_quat(quat_mul, rv3d->viewinv[0], angle); + } + + mul_qt_qtqt(quat_new, rv3d->viewquat, quat_mul); + + /* avoid precision loss over time */ + normalize_qt(quat_new); + + if (view_opposite != RV3D_VIEW_USER) { + rv3d->view = view_opposite; + /* avoid float in-precision, just get a new orientation */ + ED_view3d_quat_from_axis_view(view_opposite, rv3d->view_axis_roll, quat_new); + } + else { + rv3d->view = RV3D_VIEW_USER; + } + + float dyn_ofs[3], *dyn_ofs_pt = NULL; + + if (U.uiflag & USER_ORBIT_SELECTION) { + if (view3d_orbit_calc_center(C, dyn_ofs)) { + negate_v3(dyn_ofs); + dyn_ofs_pt = dyn_ofs; + } + } + + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .quat = quat_new, + .dyn_ofs = dyn_ofs_pt, + }); + + return OPERATOR_FINISHED; + } + } + + return OPERATOR_CANCELLED; +} + +void VIEW3D_OT_view_orbit(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "View Orbit"; + ot->description = "Orbit the view"; + ot->idname = "VIEW3D_OT_view_orbit"; + + /* api callbacks */ + ot->exec = vieworbit_exec; + ot->poll = ED_operator_rv3d_user_region_poll; + + /* flags */ + ot->flag = 0; + + /* properties */ + prop = RNA_def_float(ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + ot->prop = RNA_def_enum( + ot->srna, "type", prop_view_orbit_items, 0, "Orbit", "Direction of View Orbit"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Pan Operator + * + * Move (pan) in incremental steps. For interactive pan see #VIEW3D_OT_move. + * \{ */ + +enum { + V3D_VIEW_PANLEFT = 1, + V3D_VIEW_PANRIGHT, + V3D_VIEW_PANDOWN, + V3D_VIEW_PANUP, +}; + +static const EnumPropertyItem prop_view_pan_items[] = { + {V3D_VIEW_PANLEFT, "PANLEFT", 0, "Pan Left", "Pan the view to the left"}, + {V3D_VIEW_PANRIGHT, "PANRIGHT", 0, "Pan Right", "Pan the view to the right"}, + {V3D_VIEW_PANUP, "PANUP", 0, "Pan Up", "Pan the view up"}, + {V3D_VIEW_PANDOWN, "PANDOWN", 0, "Pan Down", "Pan the view down"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int x = 0, y = 0; + int pandir = RNA_enum_get(op->ptr, "type"); + + if (pandir == V3D_VIEW_PANRIGHT) { + x = -32; + } + else if (pandir == V3D_VIEW_PANLEFT) { + x = 32; + } + else if (pandir == V3D_VIEW_PANUP) { + y = -25; + } + else if (pandir == V3D_VIEW_PANDOWN) { + y = 25; + } + + ViewOpsData *vod = viewops_data_create( + C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT)); + + viewmove_apply(vod, vod->prev.event_xy[0] + x, vod->prev.event_xy[1] + y); + + viewops_data_free(C, vod); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_view_pan(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Pan View Direction"; + ot->description = "Pan the view in a given direction"; + ot->idname = "VIEW3D_OT_view_pan"; + + /* api callbacks */ + ot->invoke = viewpan_invoke; + ot->poll = view3d_location_poll; + + /* flags */ + ot->flag = 0; + + /* Properties */ + ot->prop = RNA_def_enum( + ot->srna, "type", prop_view_pan_items, 0, "Pan", "Direction of View Pan"); +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h new file mode 100644 index 00000000000..792d2a83bc2 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate.h @@ -0,0 +1,282 @@ +/* + * 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) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spview3d + */ + +#pragma once + +/** + * Size of the sphere being dragged for trackball rotation within the view bounds. + * also affects speed (smaller is faster). + */ +#define V3D_OP_TRACKBALLSIZE (1.1f) + +struct ARegion; +struct bContext; +struct Depsgraph; +struct Dial; +struct Main; +struct rcti; +struct RegionView3D; +struct Scene; +struct ScrArea; +struct View3D; +struct wmEvent; +struct wmOperator; + +enum eV3D_OpPropFlag { + V3D_OP_PROP_MOUSE_CO = (1 << 0), + V3D_OP_PROP_DELTA = (1 << 1), + V3D_OP_PROP_USE_ALL_REGIONS = (1 << 2), + V3D_OP_PROP_USE_MOUSE_INIT = (1 << 3), +}; + +enum { + VIEW_PASS = 0, + VIEW_APPLY, + VIEW_CONFIRM, +}; + +/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */ +enum { + VIEW_MODAL_CONFIRM = 1, /* used for all view operations */ + VIEWROT_MODAL_AXIS_SNAP_ENABLE = 2, + VIEWROT_MODAL_AXIS_SNAP_DISABLE = 3, + VIEWROT_MODAL_SWITCH_ZOOM = 4, + VIEWROT_MODAL_SWITCH_MOVE = 5, + VIEWROT_MODAL_SWITCH_ROTATE = 6, +}; + +enum eViewOpsFlag { + /** When enabled, rotate around the selection. */ + VIEWOPS_FLAG_ORBIT_SELECT = (1 << 0), + /** When enabled, use the depth under the cursor for navigation. */ + VIEWOPS_FLAG_DEPTH_NAVIGATE = (1 << 1), + /** + * When enabled run #ED_view3d_persp_ensure this may switch out of camera view + * when orbiting or switch from orthographic to perspective when auto-perspective is enabled. + * Some operations don't require this (view zoom/pan or NDOF where subtle rotation is common + * so we don't want it to trigger auto-perspective). */ + VIEWOPS_FLAG_PERSP_ENSURE = (1 << 2), + /** When set, ignore any options that depend on initial cursor location. */ + VIEWOPS_FLAG_USE_MOUSE_INIT = (1 << 3), +}; + +/** Generic View Operator Custom-Data */ +typedef struct ViewOpsData { + /** Context pointers (assigned by #viewops_data_create). */ + struct Main *bmain; + struct Scene *scene; + struct ScrArea *area; + struct ARegion *region; + struct View3D *v3d; + struct RegionView3D *rv3d; + struct Depsgraph *depsgraph; + + /** Needed for continuous zoom. */ + struct wmTimer *timer; + + /** Viewport state on initialization, don't change afterwards. */ + struct { + float dist; + float camzoom; + float quat[4]; + /** #wmEvent.xy. */ + int event_xy[2]; + /** Offset to use when #VIEWOPS_FLAG_USE_MOUSE_INIT is not set. + * so we can simulate pressing in the middle of the screen. */ + int event_xy_offset[2]; + /** #wmEvent.type that triggered the operator. */ + int event_type; + float ofs[3]; + /** Initial distance to 'ofs'. */ + float zfac; + + /** Trackball rotation only. */ + float trackvec[3]; + /** Dolly only. */ + float mousevec[3]; + + /** + * #RegionView3D.persp set after auto-perspective is applied. + * If we want the value before running the operator, add a separate member. + */ + char persp; + + /** Used for roll */ + struct Dial *dial; + } init; + + /** Previous state (previous modal event handled). */ + struct { + int event_xy[2]; + /** For operators that use time-steps (continuous zoom). */ + double time; + } prev; + + /** Current state. */ + struct { + /** Working copy of #RegionView3D.viewquat, needed for rotation calculation + * so we can apply snap to the 3D Viewport while keeping the unsnapped rotation + * here to use when snap is disabled and for continued calculation. */ + float viewquat[4]; + } curr; + + float reverse; + bool axis_snap; /* view rotate only */ + + /** Use for orbit selection and auto-dist. */ + float dyn_ofs[3]; + bool use_dyn_ofs; +} ViewOpsData; + +/* view3d_navigate.c */ +bool view3d_location_poll(struct bContext *C); +bool view3d_rotation_poll(struct bContext *C); +bool view3d_zoom_or_dolly_poll(struct bContext *C); + +enum eViewOpsFlag viewops_flag_from_prefs(void); +void calctrackballvec(const struct rcti *rect, const int event_xy[2], float r_dir[3]); +void viewmove_apply(ViewOpsData *vod, int x, int y); +void view3d_orbit_apply_dyn_ofs(float r_ofs[3], + const float ofs_old[3], + const float viewquat_old[4], + const float viewquat_new[4], + const float dyn_ofs[3]); +void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4]); +bool view3d_orbit_calc_center(struct bContext *C, float r_dyn_ofs[3]); + +void view3d_operator_properties_common(struct wmOperatorType *ot, const enum eV3D_OpPropFlag flag); + +/** + * Allocate and fill in context pointers for #ViewOpsData + */ +void viewops_data_free(struct bContext *C, ViewOpsData *vod); + +/** + * Allocate, fill in context pointers and calculate the values for #ViewOpsData + */ +ViewOpsData *viewops_data_create(struct bContext *C, + const struct wmEvent *event, + enum eViewOpsFlag viewops_flag); + +void VIEW3D_OT_view_all(struct wmOperatorType *ot); +void VIEW3D_OT_view_selected(struct wmOperatorType *ot); +void VIEW3D_OT_view_center_cursor(struct wmOperatorType *ot); +void VIEW3D_OT_view_center_pick(struct wmOperatorType *ot); +void VIEW3D_OT_view_axis(struct wmOperatorType *ot); +void VIEW3D_OT_view_camera(struct wmOperatorType *ot); +void VIEW3D_OT_view_orbit(struct wmOperatorType *ot); +void VIEW3D_OT_view_pan(struct wmOperatorType *ot); + +/* view3d_navigate_dolly.c */ +void viewdolly_modal_keymap(struct wmKeyConfig *keyconf); +void VIEW3D_OT_dolly(struct wmOperatorType *ot); + +/* view3d_navigate_fly.c */ +void fly_modal_keymap(struct wmKeyConfig *keyconf); +void view3d_keymap(struct wmKeyConfig *keyconf); +void VIEW3D_OT_fly(struct wmOperatorType *ot); + +/* view3d_navigate_move.c */ +void viewmove_modal_keymap(struct wmKeyConfig *keyconf); +void VIEW3D_OT_move(struct wmOperatorType *ot); + +/* view3d_navigate_ndof.c */ +#ifdef WITH_INPUT_NDOF +struct wmNDOFMotionData; + +/** + * Called from both fly mode and walk mode, + */ +void view3d_ndof_fly(const struct wmNDOFMotionData *ndof, + struct View3D *v3d, + struct RegionView3D *rv3d, + bool use_precision, + short protectflag, + bool *r_has_translate, + bool *r_has_rotate); +void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot); +void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot); +void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot); +void VIEW3D_OT_ndof_all(struct wmOperatorType *ot); +#endif /* WITH_INPUT_NDOF */ + +/* view3d_navigate_roll.c */ +void VIEW3D_OT_view_roll(struct wmOperatorType *ot); + +/* view3d_navigate_rotate.c */ +void viewrotate_modal_keymap(struct wmKeyConfig *keyconf); +void VIEW3D_OT_rotate(struct wmOperatorType *ot); + +/* view3d_navigate_smoothview.c */ + +/** + * Parameters for setting the new 3D Viewport state. + * + * Each of the struct members may be NULL to signify they aren't to be adjusted. + */ +typedef struct V3D_SmoothParams { + struct Object *camera_old, *camera; + const float *ofs, *quat, *dist, *lens; + + /** Alternate rotation center, when set `ofs` must be NULL. */ + const float *dyn_ofs; +} V3D_SmoothParams; + +/** + * The arguments are the desired situation. + */ +void ED_view3d_smooth_view_ex(const struct Depsgraph *depsgraph, + struct wmWindowManager *wm, + struct wmWindow *win, + struct ScrArea *area, + struct View3D *v3d, + struct ARegion *region, + int smooth_viewtx, + const V3D_SmoothParams *sview); + +void ED_view3d_smooth_view(struct bContext *C, + struct View3D *v3d, + struct ARegion *region, + int smooth_viewtx, + const V3D_SmoothParams *sview); + +/** + * Apply the smooth-view immediately, use when we need to start a new view operation. + * (so we don't end up half-applying a view operation when pressing keys quickly). + */ +void ED_view3d_smooth_view_force_finish(struct bContext *C, + struct View3D *v3d, + struct ARegion *region); + +void VIEW3D_OT_smoothview(struct wmOperatorType *ot); + +/* view3d_navigate_walk.c */ +void walk_modal_keymap(struct wmKeyConfig *keyconf); +void VIEW3D_OT_walk(struct wmOperatorType *ot); + +/* view3d_navigate_zoom.c */ +void viewzoom_modal_keymap(struct wmKeyConfig *keyconf); +void VIEW3D_OT_zoom(struct wmOperatorType *ot); + +/* view3d_navigate_zoom_border.c */ +void VIEW3D_OT_zoom_border(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_view3d/view3d_navigate_dolly.c b/source/blender/editors/space_view3d/view3d_navigate_dolly.c new file mode 100644 index 00000000000..02783c2a244 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate_dolly.c @@ -0,0 +1,337 @@ +/* + * 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 spview3d + */ + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "view3d_intern.h" +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name View Dolly Operator + * + * Like zoom but translates the view offset along the view direction + * which avoids #RegionView3D.dist approaching zero. + * \{ */ + +/* This is an exact copy of #viewzoom_modal_keymap. */ +void viewdolly_modal_keymap(wmKeyConfig *keyconf) +{ + static const EnumPropertyItem modal_items[] = { + {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + + {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"}, + {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"}, + + {0, NULL, 0, NULL, NULL}, + }; + + wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Dolly Modal"); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) { + return; + } + + keymap = WM_modalkeymap_ensure(keyconf, "View3D Dolly Modal", modal_items); + + /* disabled mode switching for now, can re-implement better, later on */ +#if 0 + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); + WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); +#endif + + /* assign map to operators */ + WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly"); +} + +static bool viewdolly_offset_lock_check(bContext *C, wmOperator *op) +{ + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (ED_view3d_offset_lock_check(v3d, rv3d)) { + BKE_report(op->reports, RPT_WARNING, "Cannot dolly when the view offset is locked"); + return true; + } + return false; +} + +static void view_dolly_to_vector_3d(ARegion *region, + const float orig_ofs[3], + const float dvec[3], + float dfac) +{ + RegionView3D *rv3d = region->regiondata; + madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac)); +} + +static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert) +{ + float zfac = 1.0; + + { + float len1, len2; + + if (U.uiflag & USER_ZOOM_HORIZ) { + len1 = (vod->region->winrct.xmax - xy[0]) + 5; + len2 = (vod->region->winrct.xmax - vod->init.event_xy[0]) + 5; + } + else { + len1 = (vod->region->winrct.ymax - xy[1]) + 5; + len2 = (vod->region->winrct.ymax - vod->init.event_xy[1]) + 5; + } + if (zoom_invert) { + SWAP(float, len1, len2); + } + + zfac = 1.0f + ((len1 - len2) * 0.01f * vod->rv3d->dist); + } + + if (zfac != 1.0f) { + view_dolly_to_vector_3d(vod->region, vod->init.ofs, vod->init.mousevec, zfac); + } + + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { + view3d_boxview_sync(vod->area, vod->region); + } + + ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); + + ED_region_tag_redraw(vod->region); +} + +static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod = op->customdata; + short event_code = VIEW_PASS; + bool use_autokey = false; + int ret = OPERATOR_RUNNING_MODAL; + + /* execute the events */ + if (event->type == MOUSEMOVE) { + event_code = VIEW_APPLY; + } + else if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case VIEW_MODAL_CONFIRM: + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_MOVE: + WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_ROTATE: + WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + } + } + else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { + event_code = VIEW_CONFIRM; + } + + if (event_code == VIEW_APPLY) { + viewdolly_apply(vod, event->xy, (U.uiflag & USER_ZOOM_INVERT) != 0); + if (ED_screen_animation_playing(CTX_wm_manager(C))) { + use_autokey = true; + } + } + else if (event_code == VIEW_CONFIRM) { + use_autokey = true; + ret = OPERATOR_FINISHED; + } + + if (use_autokey) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); + } + + if (ret & OPERATOR_FINISHED) { + viewops_data_free(C, vod); + op->customdata = NULL; + } + + return ret; +} + +static int viewdolly_exec(bContext *C, wmOperator *op) +{ + View3D *v3d; + RegionView3D *rv3d; + ScrArea *area; + ARegion *region; + float mousevec[3]; + + const int delta = RNA_int_get(op->ptr, "delta"); + + if (op->customdata) { + ViewOpsData *vod = op->customdata; + + area = vod->area; + region = vod->region; + copy_v3_v3(mousevec, vod->init.mousevec); + } + else { + area = CTX_wm_area(C); + region = CTX_wm_region(C); + negate_v3_v3(mousevec, ((RegionView3D *)region->regiondata)->viewinv[2]); + normalize_v3(mousevec); + } + + v3d = area->spacedata.first; + rv3d = region->regiondata; + + const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); + + /* overwrite the mouse vector with the view direction (zoom into the center) */ + if ((use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) == 0) { + normalize_v3_v3(mousevec, rv3d->viewinv[2]); + negate_v3(mousevec); + } + + view_dolly_to_vector_3d(region, rv3d->ofs, mousevec, delta < 0 ? 1.8f : 0.2f); + + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_sync(area, region); + } + + ED_view3d_camera_lock_sync(CTX_data_ensure_evaluated_depsgraph(C), v3d, rv3d); + + ED_region_tag_redraw(region); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + + return OPERATOR_FINISHED; +} + +/* copied from viewzoom_invoke(), changes here may apply there */ +static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod; + + if (viewdolly_offset_lock_check(C, op)) { + return OPERATOR_CANCELLED; + } + + const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); + + vod = op->customdata = viewops_data_create( + C, + event, + (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) | + (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); + + /* needs to run before 'viewops_data_create' so the backup 'rv3d->ofs' is correct */ + /* switch from camera view when: */ + if (vod->rv3d->persp != RV3D_PERSP) { + if (vod->rv3d->persp == RV3D_CAMOB) { + /* ignore rv3d->lpersp because dolly only makes sense in perspective mode */ + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_persp_switch_from_camera(depsgraph, vod->v3d, vod->rv3d, RV3D_PERSP); + } + else { + vod->rv3d->persp = RV3D_PERSP; + } + ED_region_tag_redraw(vod->region); + } + + /* if one or the other zoom position aren't set, set from event */ + if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { + RNA_int_set(op->ptr, "mx", event->xy[0]); + RNA_int_set(op->ptr, "my", event->xy[1]); + } + + if (RNA_struct_property_is_set(op->ptr, "delta")) { + viewdolly_exec(C, op); + } + else { + /* overwrite the mouse vector with the view direction (zoom into the center) */ + if ((use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) == 0) { + negate_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]); + normalize_v3(vod->init.mousevec); + } + + if (event->type == MOUSEZOOM) { + /* Bypass Zoom invert flag for track pads (pass false always) */ + + if (U.uiflag & USER_ZOOM_HORIZ) { + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; + } + else { + /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */ + vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] - + event->prev_xy[0]; + } + viewdolly_apply(vod, event->prev_xy, (U.uiflag & USER_ZOOM_INVERT) == 0); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + return OPERATOR_FINISHED; + } + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_FINISHED; +} + +static void viewdolly_cancel(bContext *C, wmOperator *op) +{ + viewops_data_free(C, op->customdata); + op->customdata = NULL; +} + +void VIEW3D_OT_dolly(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Dolly View"; + ot->description = "Dolly in/out in the view"; + ot->idname = "VIEW3D_OT_dolly"; + + /* api callbacks */ + ot->invoke = viewdolly_invoke; + ot->exec = viewdolly_exec; + ot->modal = viewdolly_modal; + ot->poll = view3d_rotation_poll; + ot->cancel = viewdolly_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; + + /* properties */ + view3d_operator_properties_common( + ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT); +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_fly.c b/source/blender/editors/space_view3d/view3d_navigate_fly.c index 2e9cb419e2e..940616eec1f 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_fly.c +++ b/source/blender/editors/space_view3d/view3d_navigate_fly.c @@ -58,6 +58,7 @@ #include "DEG_depsgraph.h" #include "view3d_intern.h" /* own include */ +#include "view3d_navigate.h" /* -------------------------------------------------------------------- */ /** \name Modal Key-map diff --git a/source/blender/editors/space_view3d/view3d_navigate_move.c b/source/blender/editors/space_view3d/view3d_navigate_move.c new file mode 100644 index 00000000000..90acf20d24f --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate_move.c @@ -0,0 +1,188 @@ +/* + * 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 spview3d + */ + +#include "BKE_context.h" + +#include "WM_api.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_screen.h" + +#include "view3d_intern.h" +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name View Move (Pan) Operator + * \{ */ + +/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */ + +void viewmove_modal_keymap(wmKeyConfig *keyconf) +{ + static const EnumPropertyItem modal_items[] = { + {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + + {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"}, + {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"}, + + {0, NULL, 0, NULL, NULL}, + }; + + wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Move Modal"); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) { + return; + } + + keymap = WM_modalkeymap_ensure(keyconf, "View3D Move Modal", modal_items); + + /* items for modal map */ + WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM); + + /* disabled mode switching for now, can re-implement better, later on */ +#if 0 + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); + WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); + WM_modalkeymap_add_item( + keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); +#endif + + /* assign map to operators */ + WM_modalkeymap_assign(keymap, "VIEW3D_OT_move"); +} + +static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + + ViewOpsData *vod = op->customdata; + short event_code = VIEW_PASS; + bool use_autokey = false; + int ret = OPERATOR_RUNNING_MODAL; + + /* execute the events */ + if (event->type == MOUSEMOVE) { + event_code = VIEW_APPLY; + } + else if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case VIEW_MODAL_CONFIRM: + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_ZOOM: + WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_ROTATE: + WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + } + } + else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { + event_code = VIEW_CONFIRM; + } + + if (event_code == VIEW_APPLY) { + viewmove_apply(vod, event->xy[0], event->xy[1]); + if (ED_screen_animation_playing(CTX_wm_manager(C))) { + use_autokey = true; + } + } + else if (event_code == VIEW_CONFIRM) { + use_autokey = true; + ret = OPERATOR_FINISHED; + } + + if (use_autokey) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); + } + + if (ret & OPERATOR_FINISHED) { + viewops_data_free(C, op->customdata); + op->customdata = NULL; + } + + return ret; +} + +static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod; + + const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); + + vod = op->customdata = viewops_data_create( + C, + event, + (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) | + (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); + vod = op->customdata; + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); + + if (event->type == MOUSEPAN) { + /* invert it, trackpad scroll follows same principle as 2d windows this way */ + viewmove_apply( + vod, 2 * event->xy[0] - event->prev_xy[0], 2 * event->xy[1] - event->prev_xy[1]); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + + return OPERATOR_FINISHED; + } + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static void viewmove_cancel(bContext *C, wmOperator *op) +{ + viewops_data_free(C, op->customdata); + op->customdata = NULL; +} + +void VIEW3D_OT_move(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Pan View"; + ot->description = "Move the view"; + ot->idname = "VIEW3D_OT_move"; + + /* api callbacks */ + ot->invoke = viewmove_invoke; + ot->modal = viewmove_modal; + ot->poll = view3d_location_poll; + ot->cancel = viewmove_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; + + /* properties */ + view3d_operator_properties_common(ot, V3D_OP_PROP_USE_MOUSE_INIT); +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_ndof.c b/source/blender/editors/space_view3d/view3d_navigate_ndof.c new file mode 100644 index 00000000000..285d5c02db2 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate_ndof.c @@ -0,0 +1,661 @@ +/* + * 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 spview3d + */ + +#include "BLI_math.h" + +#include "BKE_context.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" + +#include "ED_screen.h" + +#include "view3d_intern.h" +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name NDOF Utility Functions + * \{ */ + +#ifdef WITH_INPUT_NDOF + +enum { + HAS_TRANSLATE = (1 << 0), + HAS_ROTATE = (1 << 0), +}; + +static bool ndof_has_translate(const wmNDOFMotionData *ndof, + const View3D *v3d, + const RegionView3D *rv3d) +{ + return !is_zero_v3(ndof->tvec) && (!ED_view3d_offset_lock_check(v3d, rv3d)); +} + +static bool ndof_has_rotate(const wmNDOFMotionData *ndof, const RegionView3D *rv3d) +{ + return !is_zero_v3(ndof->rvec) && ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0); +} + +/** + * \param depth_pt: A point to calculate the depth (in perspective mode) + */ +static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth_pt[3]) +{ + float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND; + + if (rv3d->is_persp) { + speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL); + } + + return speed; +} + +static float view3d_ndof_pan_speed_calc_from_dist(RegionView3D *rv3d, const float dist) +{ + float viewinv[4]; + float tvec[3]; + + BLI_assert(dist >= 0.0f); + + copy_v3_fl3(tvec, 0.0f, 0.0f, dist); + /* rv3d->viewinv isn't always valid */ +# if 0 + mul_mat3_m4_v3(rv3d->viewinv, tvec); +# else + invert_qt_qt_normalized(viewinv, rv3d->viewquat); + mul_qt_v3(viewinv, tvec); +# endif + + return view3d_ndof_pan_speed_calc_ex(rv3d, tvec); +} + +static float view3d_ndof_pan_speed_calc(RegionView3D *rv3d) +{ + float tvec[3]; + negate_v3_v3(tvec, rv3d->ofs); + + return view3d_ndof_pan_speed_calc_ex(rv3d, tvec); +} + +/** + * Zoom and pan in the same function since sometimes zoom is interpreted as dolly (pan forward). + * + * \param has_zoom: zoom, otherwise dolly, + * often `!rv3d->is_persp` since it doesn't make sense to dolly in ortho. + */ +static void view3d_ndof_pan_zoom(const struct wmNDOFMotionData *ndof, + ScrArea *area, + ARegion *region, + const bool has_translate, + const bool has_zoom) +{ + RegionView3D *rv3d = region->regiondata; + float view_inv[4]; + float pan_vec[3]; + + if (has_translate == false && has_zoom == false) { + return; + } + + WM_event_ndof_pan_get(ndof, pan_vec, false); + + if (has_zoom) { + /* zoom with Z */ + + /* Zoom! + * velocity should be proportional to the linear velocity attained by rotational motion + * of same strength [got that?] proportional to `arclength = radius * angle`. + */ + + pan_vec[2] = 0.0f; + + /* "zoom in" or "translate"? depends on zoom mode in user settings? */ + if (ndof->tvec[2]) { + float zoom_distance = rv3d->dist * ndof->dt * ndof->tvec[2]; + + if (U.ndof_flag & NDOF_ZOOM_INVERT) { + zoom_distance = -zoom_distance; + } + + rv3d->dist += zoom_distance; + } + } + else { + /* dolly with Z */ + + /* all callers must check */ + if (has_translate) { + BLI_assert(ED_view3d_offset_lock_check((View3D *)area->spacedata.first, rv3d) == false); + } + } + + if (has_translate) { + const float speed = view3d_ndof_pan_speed_calc(rv3d); + + mul_v3_fl(pan_vec, speed * ndof->dt); + + /* transform motion from view to world coordinates */ + invert_qt_qt_normalized(view_inv, rv3d->viewquat); + mul_qt_v3(view_inv, pan_vec); + + /* move center of view opposite of hand motion (this is camera mode, not object mode) */ + sub_v3_v3(rv3d->ofs, pan_vec); + + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_sync(area, region); + } + } +} + +static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof, + ScrArea *area, + ARegion *region, + ViewOpsData *vod, + const bool apply_dyn_ofs) +{ + View3D *v3d = area->spacedata.first; + RegionView3D *rv3d = region->regiondata; + + float view_inv[4]; + + BLI_assert((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0); + + ED_view3d_persp_ensure(vod->depsgraph, v3d, region); + + rv3d->view = RV3D_VIEW_USER; + + invert_qt_qt_normalized(view_inv, rv3d->viewquat); + + if (U.ndof_flag & NDOF_TURNTABLE) { + float rot[3]; + + /* Turntable view code adapted for 3D mouse use. */ + float angle, quat[4]; + float xvec[3] = {1, 0, 0}; + + /* only use XY, ignore Z */ + WM_event_ndof_rotate_get(ndof, rot); + + /* Determine the direction of the x vector (for rotating up and down) */ + mul_qt_v3(view_inv, xvec); + + /* Perform the up/down rotation */ + angle = ndof->dt * rot[0]; + axis_angle_to_quat(quat, xvec, angle); + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat); + + /* Perform the orbital rotation */ + angle = ndof->dt * rot[1]; + + /* Update the onscreen axis-angle indicator. */ + rv3d->rot_angle = angle; + rv3d->rot_axis[0] = 0; + rv3d->rot_axis[1] = 0; + rv3d->rot_axis[2] = 1; + + axis_angle_to_quat_single(quat, 'Z', angle); + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat); + } + else { + float quat[4]; + float axis[3]; + float angle = WM_event_ndof_to_axis_angle(ndof, axis); + + /* transform rotation axis from view to world coordinates */ + mul_qt_v3(view_inv, axis); + + /* Update the onscreen axis-angle indicator. */ + rv3d->rot_angle = angle; + copy_v3_v3(rv3d->rot_axis, axis); + + axis_angle_to_quat(quat, axis, angle); + + /* apply rotation */ + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat); + } + + if (apply_dyn_ofs) { + viewrotate_apply_dyn_ofs(vod, rv3d->viewquat); + } +} + +void view3d_ndof_fly(const wmNDOFMotionData *ndof, + View3D *v3d, + RegionView3D *rv3d, + const bool use_precision, + const short protectflag, + bool *r_has_translate, + bool *r_has_rotate) +{ + bool has_translate = ndof_has_translate(ndof, v3d, rv3d); + bool has_rotate = ndof_has_rotate(ndof, rv3d); + + float view_inv[4]; + invert_qt_qt_normalized(view_inv, rv3d->viewquat); + + rv3d->rot_angle = 0.0f; /* Disable onscreen rotation indicator. */ + + if (has_translate) { + /* ignore real 'dist' since fly has its own speed settings, + * also its overwritten at this point. */ + float speed = view3d_ndof_pan_speed_calc_from_dist(rv3d, 1.0f); + float trans[3], trans_orig_y; + + if (use_precision) { + speed *= 0.2f; + } + + WM_event_ndof_pan_get(ndof, trans, false); + mul_v3_fl(trans, speed * ndof->dt); + trans_orig_y = trans[1]; + + if (U.ndof_flag & NDOF_FLY_HELICOPTER) { + trans[1] = 0.0f; + } + + /* transform motion from view to world coordinates */ + mul_qt_v3(view_inv, trans); + + if (U.ndof_flag & NDOF_FLY_HELICOPTER) { + /* replace world z component with device y (yes it makes sense) */ + trans[2] = trans_orig_y; + } + + if (rv3d->persp == RV3D_CAMOB) { + /* respect camera position locks */ + if (protectflag & OB_LOCK_LOCX) { + trans[0] = 0.0f; + } + if (protectflag & OB_LOCK_LOCY) { + trans[1] = 0.0f; + } + if (protectflag & OB_LOCK_LOCZ) { + trans[2] = 0.0f; + } + } + + if (!is_zero_v3(trans)) { + /* move center of view opposite of hand motion + * (this is camera mode, not object mode) */ + sub_v3_v3(rv3d->ofs, trans); + has_translate = true; + } + else { + has_translate = false; + } + } + + if (has_rotate) { + const float turn_sensitivity = 1.0f; + + float rotation[4]; + float axis[3]; + float angle = turn_sensitivity * WM_event_ndof_to_axis_angle(ndof, axis); + + if (fabsf(angle) > 0.0001f) { + has_rotate = true; + + if (use_precision) { + angle *= 0.2f; + } + + /* transform rotation axis from view to world coordinates */ + mul_qt_v3(view_inv, axis); + + /* apply rotation to view */ + axis_angle_to_quat(rotation, axis, angle); + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation); + + if (U.ndof_flag & NDOF_LOCK_HORIZON) { + /* force an upright viewpoint + * TODO: make this less... sudden */ + float view_horizon[3] = {1.0f, 0.0f, 0.0f}; /* view +x */ + float view_direction[3] = {0.0f, 0.0f, -1.0f}; /* view -z (into screen) */ + + /* find new inverse since viewquat has changed */ + invert_qt_qt_normalized(view_inv, rv3d->viewquat); + /* could apply reverse rotation to existing view_inv to save a few cycles */ + + /* transform view vectors to world coordinates */ + mul_qt_v3(view_inv, view_horizon); + mul_qt_v3(view_inv, view_direction); + + /* find difference between view & world horizons + * true horizon lives in world xy plane, so look only at difference in z */ + angle = -asinf(view_horizon[2]); + + /* rotate view so view horizon = world horizon */ + axis_angle_to_quat(rotation, view_direction, angle); + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation); + } + + rv3d->view = RV3D_VIEW_USER; + } + else { + has_rotate = false; + } + } + + *r_has_translate = has_translate; + *r_has_rotate = has_rotate; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name NDOF Orbit/Translate Operator + * \{ */ + +static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (event->type != NDOF_MOTION) { + return OPERATOR_CANCELLED; + } + + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewOpsData *vod; + View3D *v3d; + RegionView3D *rv3d; + char xform_flag = 0; + + const wmNDOFMotionData *ndof = event->customdata; + + vod = op->customdata = viewops_data_create( + C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_DEPTH_NAVIGATE)); + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); + + v3d = vod->v3d; + rv3d = vod->rv3d; + + /* off by default, until changed later this function */ + rv3d->rot_angle = 0.0f; + + ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false); + + if (ndof->progress != P_FINISHING) { + const bool has_rotation = ndof_has_rotate(ndof, rv3d); + /* if we can't rotate, fallback to translate (locked axis views) */ + const bool has_translate = ndof_has_translate(ndof, v3d, rv3d) && + (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION); + const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp; + + if (has_translate || has_zoom) { + view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, has_zoom); + xform_flag |= HAS_TRANSLATE; + } + + if (has_rotation) { + view3d_ndof_orbit(ndof, vod->area, vod->region, vod, true); + xform_flag |= HAS_ROTATE; + } + } + + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + if (xform_flag) { + ED_view3d_camera_lock_autokey( + v3d, rv3d, C, xform_flag & HAS_ROTATE, xform_flag & HAS_TRANSLATE); + } + + ED_region_tag_redraw(vod->region); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "NDOF Orbit View"; + ot->description = "Orbit the view using the 3D mouse"; + ot->idname = "VIEW3D_OT_ndof_orbit"; + + /* api callbacks */ + ot->invoke = ndof_orbit_invoke; + ot->poll = ED_operator_view3d_active; + + /* flags */ + ot->flag = 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name NDOF Orbit/Zoom Operator + * \{ */ + +static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (event->type != NDOF_MOTION) { + return OPERATOR_CANCELLED; + } + + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewOpsData *vod; + View3D *v3d; + RegionView3D *rv3d; + char xform_flag = 0; + + const wmNDOFMotionData *ndof = event->customdata; + + vod = op->customdata = viewops_data_create( + C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_DEPTH_NAVIGATE)); + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); + + v3d = vod->v3d; + rv3d = vod->rv3d; + + /* off by default, until changed later this function */ + rv3d->rot_angle = 0.0f; + + ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false); + + if (ndof->progress == P_FINISHING) { + /* pass */ + } + else if ((rv3d->persp == RV3D_ORTHO) && RV3D_VIEW_IS_AXIS(rv3d->view)) { + /* if we can't rotate, fallback to translate (locked axis views) */ + const bool has_translate = ndof_has_translate(ndof, v3d, rv3d); + const bool has_zoom = (ndof->tvec[2] != 0.0f) && ED_view3d_offset_lock_check(v3d, rv3d); + + if (has_translate || has_zoom) { + view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, true); + xform_flag |= HAS_TRANSLATE; + } + } + else { + /* NOTE: based on feedback from T67579, users want to have pan and orbit enabled at once. + * It's arguable that orbit shouldn't pan (since we have a pan only operator), + * so if there are users who like to separate orbit/pan operations - it can be a preference. */ + const bool is_orbit_around_pivot = (U.ndof_flag & NDOF_MODE_ORBIT) || + ED_view3d_offset_lock_check(v3d, rv3d); + const bool has_rotation = ndof_has_rotate(ndof, rv3d); + bool has_translate, has_zoom; + + if (is_orbit_around_pivot) { + /* Orbit preference or forced lock (Z zooms). */ + has_translate = !is_zero_v2(ndof->tvec) && ndof_has_translate(ndof, v3d, rv3d); + has_zoom = (ndof->tvec[2] != 0.0f); + } + else { + /* Free preference (Z translates). */ + has_translate = ndof_has_translate(ndof, v3d, rv3d); + has_zoom = false; + } + + /* Rotation first because dynamic offset resets offset otherwise (and disables panning). */ + if (has_rotation) { + const float dist_backup = rv3d->dist; + if (!is_orbit_around_pivot) { + ED_view3d_distance_set(rv3d, 0.0f); + } + view3d_ndof_orbit(ndof, vod->area, vod->region, vod, is_orbit_around_pivot); + xform_flag |= HAS_ROTATE; + if (!is_orbit_around_pivot) { + ED_view3d_distance_set(rv3d, dist_backup); + } + } + + if (has_translate || has_zoom) { + view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, has_zoom); + xform_flag |= HAS_TRANSLATE; + } + } + + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + if (xform_flag) { + ED_view3d_camera_lock_autokey( + v3d, rv3d, C, xform_flag & HAS_ROTATE, xform_flag & HAS_TRANSLATE); + } + + ED_region_tag_redraw(vod->region); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "NDOF Orbit View with Zoom"; + ot->description = "Orbit and zoom the view using the 3D mouse"; + ot->idname = "VIEW3D_OT_ndof_orbit_zoom"; + + /* api callbacks */ + ot->invoke = ndof_orbit_zoom_invoke; + ot->poll = ED_operator_view3d_active; + + /* flags */ + ot->flag = 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name NDOF Pan/Zoom Operator + * \{ */ + +static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + if (event->type != NDOF_MOTION) { + return OPERATOR_CANCELLED; + } + + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + const wmNDOFMotionData *ndof = event->customdata; + char xform_flag = 0; + + const bool has_translate = ndof_has_translate(ndof, v3d, rv3d); + const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp; + + /* we're panning here! so erase any leftover rotation from other operators */ + rv3d->rot_angle = 0.0f; + + if (!(has_translate || has_zoom)) { + return OPERATOR_CANCELLED; + } + + ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false); + + if (ndof->progress != P_FINISHING) { + ScrArea *area = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + + if (has_translate || has_zoom) { + view3d_ndof_pan_zoom(ndof, area, region, has_translate, has_zoom); + xform_flag |= HAS_TRANSLATE; + } + } + + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + if (xform_flag) { + ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, xform_flag & HAS_TRANSLATE); + } + + ED_region_tag_redraw(CTX_wm_region(C)); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "NDOF Pan View"; + ot->description = "Pan the view with the 3D mouse"; + ot->idname = "VIEW3D_OT_ndof_pan"; + + /* api callbacks */ + ot->invoke = ndof_pan_invoke; + ot->poll = ED_operator_view3d_active; + + /* flags */ + ot->flag = 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name NDOF Transform All Operator + * \{ */ + +/** + * wraps #ndof_orbit_zoom but never restrict to orbit. + */ +static int ndof_all_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + /* weak!, but it works */ + const int ndof_flag = U.ndof_flag; + int ret; + + U.ndof_flag &= ~NDOF_MODE_ORBIT; + + ret = ndof_orbit_zoom_invoke(C, op, event); + + U.ndof_flag = ndof_flag; + + return ret; +} + +void VIEW3D_OT_ndof_all(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "NDOF Transform View"; + ot->description = "Pan and rotate the view with the 3D mouse"; + ot->idname = "VIEW3D_OT_ndof_all"; + + /* api callbacks */ + ot->invoke = ndof_all_invoke; + ot->poll = ED_operator_view3d_active; + + /* flags */ + ot->flag = 0; +} + +#endif /* WITH_INPUT_NDOF */ + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c new file mode 100644 index 00000000000..c9bfdc4412a --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c @@ -0,0 +1,292 @@ +/* + * 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 spview3d + */ + +#include "BLI_blenlib.h" +#include "BLI_dial_2d.h" +#include "BLI_math.h" + +#include "BKE_context.h" + +#include "WM_api.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_screen.h" + +#include "view3d_intern.h" +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name View Roll Operator + * \{ */ + +static void view_roll_angle( + ARegion *region, float quat[4], const float orig_quat[4], const float dvec[3], float angle) +{ + RegionView3D *rv3d = region->regiondata; + float quat_mul[4]; + + /* camera axis */ + axis_angle_normalized_to_quat(quat_mul, dvec, angle); + + mul_qt_qtqt(quat, orig_quat, quat_mul); + + /* avoid precision loss over time */ + normalize_qt(quat); + + rv3d->view = RV3D_VIEW_USER; +} + +static void viewroll_apply(ViewOpsData *vod, int x, int y) +{ + float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y}); + + if (angle != 0.0f) { + view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle); + } + + if (vod->use_dyn_ofs) { + view3d_orbit_apply_dyn_ofs( + vod->rv3d->ofs, vod->init.ofs, vod->init.quat, vod->rv3d->viewquat, vod->dyn_ofs); + } + + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { + view3d_boxview_sync(vod->area, vod->region); + } + + ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); + + ED_region_tag_redraw(vod->region); +} + +static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod = op->customdata; + short event_code = VIEW_PASS; + bool use_autokey = false; + int ret = OPERATOR_RUNNING_MODAL; + + /* execute the events */ + if (event->type == MOUSEMOVE) { + event_code = VIEW_APPLY; + } + else if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case VIEW_MODAL_CONFIRM: + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_MOVE: + WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_ROTATE: + WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + } + } + else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) { + /* Note this does not remove auto-keys on locked cameras. */ + copy_qt_qt(vod->rv3d->viewquat, vod->init.quat); + ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); + viewops_data_free(C, op->customdata); + op->customdata = NULL; + return OPERATOR_CANCELLED; + } + else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { + event_code = VIEW_CONFIRM; + } + + if (event_code == VIEW_APPLY) { + viewroll_apply(vod, event->xy[0], event->xy[1]); + if (ED_screen_animation_playing(CTX_wm_manager(C))) { + use_autokey = true; + } + } + else if (event_code == VIEW_CONFIRM) { + use_autokey = true; + ret = OPERATOR_FINISHED; + } + + if (use_autokey) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, false); + } + + if (ret & OPERATOR_FINISHED) { + viewops_data_free(C, op->customdata); + op->customdata = NULL; + } + + return ret; +} + +enum { + V3D_VIEW_STEPLEFT = 1, + V3D_VIEW_STEPRIGHT, +}; + +static const EnumPropertyItem prop_view_roll_items[] = { + {0, "ANGLE", 0, "Roll Angle", "Roll the view using an angle value"}, + {V3D_VIEW_STEPLEFT, "LEFT", 0, "Roll Left", "Roll the view around to the left"}, + {V3D_VIEW_STEPRIGHT, "RIGHT", 0, "Roll Right", "Roll the view around to the right"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int viewroll_exec(bContext *C, wmOperator *op) +{ + View3D *v3d; + RegionView3D *rv3d; + ARegion *region; + + if (op->customdata) { + ViewOpsData *vod = op->customdata; + region = vod->region; + v3d = vod->v3d; + } + else { + ED_view3d_context_user_region(C, &v3d, ®ion); + } + + rv3d = region->regiondata; + if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + int type = RNA_enum_get(op->ptr, "type"); + float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle); + float mousevec[3]; + float quat_new[4]; + + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + if (type == V3D_VIEW_STEPLEFT) { + angle = -angle; + } + + normalize_v3_v3(mousevec, rv3d->viewinv[2]); + negate_v3(mousevec); + view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle); + + const float *dyn_ofs_pt = NULL; + float dyn_ofs[3]; + if (U.uiflag & USER_ORBIT_SELECTION) { + if (view3d_orbit_calc_center(C, dyn_ofs)) { + negate_v3(dyn_ofs); + dyn_ofs_pt = dyn_ofs; + } + } + + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .quat = quat_new, + .dyn_ofs = dyn_ofs_pt, + }); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + return OPERATOR_FINISHED; + } + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + return OPERATOR_CANCELLED; +} + +static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod; + + bool use_angle = RNA_enum_get(op->ptr, "type") != 0; + + if (use_angle || RNA_struct_property_is_set(op->ptr, "angle")) { + viewroll_exec(C, op); + } + else { + /* makes op->customdata */ + vod = op->customdata = viewops_data_create(C, event, viewops_flag_from_prefs()); + vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct), + BLI_rcti_cent_y(&vod->region->winrct)}, + FLT_EPSILON); + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); + + /* overwrite the mouse vector with the view direction */ + normalize_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]); + negate_v3(vod->init.mousevec); + + if (event->type == MOUSEROTATE) { + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; + viewroll_apply(vod, event->prev_xy[0], event->prev_xy[1]); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + return OPERATOR_FINISHED; + } + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_FINISHED; +} + +static void viewroll_cancel(bContext *C, wmOperator *op) +{ + viewops_data_free(C, op->customdata); + op->customdata = NULL; +} + +void VIEW3D_OT_view_roll(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "View Roll"; + ot->description = "Roll the view"; + ot->idname = "VIEW3D_OT_view_roll"; + + /* api callbacks */ + ot->invoke = viewroll_invoke; + ot->exec = viewroll_exec; + ot->modal = viewroll_modal; + ot->poll = ED_operator_rv3d_user_region_poll; + ot->cancel = viewroll_cancel; + + /* flags */ + ot->flag = 0; + + /* properties */ + ot->prop = prop = RNA_def_float( + ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, + "type", + prop_view_roll_items, + 0, + "Roll Angle Source", + "How roll angle is calculated"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c new file mode 100644 index 00000000000..c3730b3b3b1 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c @@ -0,0 +1,452 @@ +/* + * 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 spview3d + */ + +#include "BLI_math.h" + +#include "BKE_context.h" + +#include "WM_api.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "view3d_intern.h" +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name View Rotate Operator + * \{ */ + +void viewrotate_modal_keymap(wmKeyConfig *keyconf) +{ + static const EnumPropertyItem modal_items[] = { + {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + + {VIEWROT_MODAL_AXIS_SNAP_ENABLE, "AXIS_SNAP_ENABLE", 0, "Axis Snap", ""}, + {VIEWROT_MODAL_AXIS_SNAP_DISABLE, "AXIS_SNAP_DISABLE", 0, "Axis Snap (Off)", ""}, + + {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"}, + {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"}, + + {0, NULL, 0, NULL, NULL}, + }; + + wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Rotate Modal"); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) { + return; + } + + keymap = WM_modalkeymap_ensure(keyconf, "View3D Rotate Modal", modal_items); + + /* disabled mode switching for now, can re-implement better, later on */ +#if 0 + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); + WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); +#endif + + /* assign map to operators */ + WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate"); +} + +static void viewrotate_apply_snap(ViewOpsData *vod) +{ + const float axis_limit = DEG2RADF(45 / 3); + + RegionView3D *rv3d = vod->rv3d; + + float viewquat_inv[4]; + float zaxis[3] = {0, 0, 1}; + float zaxis_best[3]; + int x, y, z; + bool found = false; + + invert_qt_qt_normalized(viewquat_inv, vod->curr.viewquat); + + mul_qt_v3(viewquat_inv, zaxis); + normalize_v3(zaxis); + + for (x = -1; x < 2; x++) { + for (y = -1; y < 2; y++) { + for (z = -1; z < 2; z++) { + if (x || y || z) { + float zaxis_test[3] = {x, y, z}; + + normalize_v3(zaxis_test); + + if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) { + copy_v3_v3(zaxis_best, zaxis_test); + found = true; + } + } + } + } + } + + if (found) { + + /* find the best roll */ + float quat_roll[4], quat_final[4], quat_best[4], quat_snap[4]; + float viewquat_align[4]; /* viewquat aligned to zaxis_best */ + float viewquat_align_inv[4]; /* viewquat aligned to zaxis_best */ + float best_angle = axis_limit; + int j; + + /* viewquat_align is the original viewquat aligned to the snapped axis + * for testing roll */ + rotation_between_vecs_to_quat(viewquat_align, zaxis_best, zaxis); + normalize_qt(viewquat_align); + mul_qt_qtqt(viewquat_align, vod->curr.viewquat, viewquat_align); + normalize_qt(viewquat_align); + invert_qt_qt_normalized(viewquat_align_inv, viewquat_align); + + vec_to_quat(quat_snap, zaxis_best, OB_NEGZ, OB_POSY); + normalize_qt(quat_snap); + invert_qt_normalized(quat_snap); + + /* check if we can find the roll */ + found = false; + + /* find best roll */ + for (j = 0; j < 8; j++) { + float angle; + float xaxis1[3] = {1, 0, 0}; + float xaxis2[3] = {1, 0, 0}; + float quat_final_inv[4]; + + axis_angle_to_quat(quat_roll, zaxis_best, (float)j * DEG2RADF(45.0f)); + normalize_qt(quat_roll); + + mul_qt_qtqt(quat_final, quat_snap, quat_roll); + normalize_qt(quat_final); + + /* compare 2 vector angles to find the least roll */ + invert_qt_qt_normalized(quat_final_inv, quat_final); + mul_qt_v3(viewquat_align_inv, xaxis1); + mul_qt_v3(quat_final_inv, xaxis2); + angle = angle_v3v3(xaxis1, xaxis2); + + if (angle <= best_angle) { + found = true; + best_angle = angle; + copy_qt_qt(quat_best, quat_final); + } + } + + if (found) { + /* lock 'quat_best' to an axis view if we can */ + ED_view3d_quat_to_axis_view(quat_best, 0.01f, &rv3d->view, &rv3d->view_axis_roll); + if (rv3d->view != RV3D_VIEW_USER) { + ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_best); + } + } + else { + copy_qt_qt(quat_best, viewquat_align); + } + + copy_qt_qt(rv3d->viewquat, quat_best); + + viewrotate_apply_dyn_ofs(vod, rv3d->viewquat); + + if (U.uiflag & USER_AUTOPERSP) { + if (RV3D_VIEW_IS_AXIS(rv3d->view)) { + if (rv3d->persp == RV3D_PERSP) { + rv3d->persp = RV3D_ORTHO; + } + } + } + } + else if (U.uiflag & USER_AUTOPERSP) { + rv3d->persp = vod->init.persp; + } +} + +static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2]) +{ + RegionView3D *rv3d = vod->rv3d; + + rv3d->view = RV3D_VIEW_USER; /* need to reset every time because of view snapping */ + + if (U.flag & USER_TRACKBALL) { + float axis[3], q1[4], dvec[3], newvec[3]; + float angle; + + { + const int event_xy_offset[2] = { + event_xy[0] + vod->init.event_xy_offset[0], + event_xy[1] + vod->init.event_xy_offset[1], + }; + calctrackballvec(&vod->region->winrct, event_xy_offset, newvec); + } + + sub_v3_v3v3(dvec, newvec, vod->init.trackvec); + + angle = (len_v3(dvec) / (2.0f * V3D_OP_TRACKBALLSIZE)) * (float)M_PI; + + /* Before applying the sensitivity this is rotating 1:1, + * where the cursor would match the surface of a sphere in the view. */ + angle *= U.view_rotate_sensitivity_trackball; + + /* Allow for rotation beyond the interval [-pi, pi] */ + angle = angle_wrap_rad(angle); + + /* This relation is used instead of the actual angle between vectors + * so that the angle of rotation is linearly proportional to + * the distance that the mouse is dragged. */ + + cross_v3_v3v3(axis, vod->init.trackvec, newvec); + axis_angle_to_quat(q1, axis, angle); + + mul_qt_qtqt(vod->curr.viewquat, q1, vod->init.quat); + + viewrotate_apply_dyn_ofs(vod, vod->curr.viewquat); + } + else { + float quat_local_x[4], quat_global_z[4]; + float m[3][3]; + float m_inv[3][3]; + const float zvec_global[3] = {0.0f, 0.0f, 1.0f}; + float xaxis[3]; + + /* Radians per-pixel. */ + const float sensitivity = U.view_rotate_sensitivity_turntable / U.dpi_fac; + + /* Get the 3x3 matrix and its inverse from the quaternion */ + quat_to_mat3(m, vod->curr.viewquat); + invert_m3_m3(m_inv, m); + + /* Avoid Gimbal Lock + * + * Even though turn-table mode is in use, this can occur when the user exits the camera view + * or when aligning the view to a rotated object. + * + * We have gimbal lock when the user's view is rotated +/- 90 degrees along the view axis. + * In this case the vertical rotation is the same as the sideways turntable motion. + * Making it impossible to get out of the gimbal locked state without resetting the view. + * + * The logic below lets the user exit out of this state without any abrupt 'fix' + * which would be disorienting. + * + * This works by blending two horizons: + * - Rotated-horizon: `cross_v3_v3v3(xaxis, zvec_global, m_inv[2])` + * When only this is used, this turntable rotation works - but it's side-ways + * (as if the entire turn-table has been placed on its side) + * While there is no gimbal lock, it's also awkward to use. + * - Un-rotated-horizon: `m_inv[0]` + * When only this is used, the turntable rotation can have gimbal lock. + * + * The solution used here is to blend between these two values, + * so the severity of the gimbal lock is used to blend the rotated horizon. + * Blending isn't essential, it just makes the transition smoother. + * + * This allows sideways turn-table rotation on a Z axis that isn't world-space Z, + * While up-down turntable rotation eventually corrects gimbal lock. */ +#if 1 + if (len_squared_v3v3(zvec_global, m_inv[2]) > 0.001f) { + float fac; + cross_v3_v3v3(xaxis, zvec_global, m_inv[2]); + if (dot_v3v3(xaxis, m_inv[0]) < 0) { + negate_v3(xaxis); + } + fac = angle_normalized_v3v3(zvec_global, m_inv[2]) / (float)M_PI; + fac = fabsf(fac - 0.5f) * 2; + fac = fac * fac; + interp_v3_v3v3(xaxis, xaxis, m_inv[0], fac); + } + else { + copy_v3_v3(xaxis, m_inv[0]); + } +#else + copy_v3_v3(xaxis, m_inv[0]); +#endif + + /* Determine the direction of the x vector (for rotating up and down) */ + /* This can likely be computed directly from the quaternion. */ + + /* Perform the up/down rotation */ + axis_angle_to_quat(quat_local_x, xaxis, sensitivity * -(event_xy[1] - vod->prev.event_xy[1])); + mul_qt_qtqt(quat_local_x, vod->curr.viewquat, quat_local_x); + + /* Perform the orbital rotation */ + axis_angle_to_quat_single( + quat_global_z, 'Z', sensitivity * vod->reverse * (event_xy[0] - vod->prev.event_xy[0])); + mul_qt_qtqt(vod->curr.viewquat, quat_local_x, quat_global_z); + + viewrotate_apply_dyn_ofs(vod, vod->curr.viewquat); + } + + /* avoid precision loss over time */ + normalize_qt(vod->curr.viewquat); + + /* use a working copy so view rotation locking doesn't overwrite the locked + * rotation back into the view we calculate with */ + copy_qt_qt(rv3d->viewquat, vod->curr.viewquat); + + /* Check for view snap, + * NOTE: don't apply snap to `vod->viewquat` so the view won't jam up. */ + if (vod->axis_snap) { + viewrotate_apply_snap(vod); + } + vod->prev.event_xy[0] = event_xy[0]; + vod->prev.event_xy[1] = event_xy[1]; + + ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, rv3d); + + ED_region_tag_redraw(vod->region); +} + +static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod = op->customdata; + short event_code = VIEW_PASS; + bool use_autokey = false; + int ret = OPERATOR_RUNNING_MODAL; + + /* execute the events */ + if (event->type == MOUSEMOVE) { + event_code = VIEW_APPLY; + } + else if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case VIEW_MODAL_CONFIRM: + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_AXIS_SNAP_ENABLE: + vod->axis_snap = true; + event_code = VIEW_APPLY; + break; + case VIEWROT_MODAL_AXIS_SNAP_DISABLE: + vod->rv3d->persp = vod->init.persp; + vod->axis_snap = false; + event_code = VIEW_APPLY; + break; + case VIEWROT_MODAL_SWITCH_ZOOM: + WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_MOVE: + WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + } + } + else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { + event_code = VIEW_CONFIRM; + } + + if (event_code == VIEW_APPLY) { + viewrotate_apply(vod, event->xy); + if (ED_screen_animation_playing(CTX_wm_manager(C))) { + use_autokey = true; + } + } + else if (event_code == VIEW_CONFIRM) { + use_autokey = true; + ret = OPERATOR_FINISHED; + } + + if (use_autokey) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, true); + } + + if (ret & OPERATOR_FINISHED) { + viewops_data_free(C, op->customdata); + op->customdata = NULL; + } + + return ret; +} + +static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod; + + const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); + + /* makes op->customdata */ + vod = op->customdata = viewops_data_create( + C, + event, + viewops_flag_from_prefs() | VIEWOPS_FLAG_PERSP_ENSURE | + (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); + + if (ELEM(event->type, MOUSEPAN, MOUSEROTATE)) { + /* Rotate direction we keep always same */ + int event_xy[2]; + + if (event->type == MOUSEPAN) { + if (event->is_direction_inverted) { + event_xy[0] = 2 * event->xy[0] - event->prev_xy[0]; + event_xy[1] = 2 * event->xy[1] - event->prev_xy[1]; + } + else { + copy_v2_v2_int(event_xy, event->prev_xy); + } + } + else { + /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */ + copy_v2_v2_int(event_xy, event->prev_xy); + } + + viewrotate_apply(vod, event_xy); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + + return OPERATOR_FINISHED; + } + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static void viewrotate_cancel(bContext *C, wmOperator *op) +{ + viewops_data_free(C, op->customdata); + op->customdata = NULL; +} + +void VIEW3D_OT_rotate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Rotate View"; + ot->description = "Rotate the view"; + ot->idname = "VIEW3D_OT_rotate"; + + /* api callbacks */ + ot->invoke = viewrotate_invoke; + ot->modal = viewrotate_modal; + ot->poll = view3d_rotation_poll; + ot->cancel = viewrotate_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; + + view3d_operator_properties_common(ot, V3D_OP_PROP_USE_MOUSE_INIT); +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c new file mode 100644 index 00000000000..aeffb520b0a --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c @@ -0,0 +1,406 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup spview3d + */ + +#include "DNA_camera_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "BKE_context.h" + +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" + +#include "ED_screen.h" + +#include "view3d_intern.h" +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name Smooth View Operator & Utilities + * + * Use for view transitions to have smooth (animated) transitions. + * \{ */ + +/* This operator is one of the 'timer refresh' ones like animation playback */ + +struct SmoothView3DState { + float dist; + float lens; + float quat[4]; + float ofs[3]; +}; + +struct SmoothView3DStore { + /* Source. */ + struct SmoothView3DState src; /* source */ + struct SmoothView3DState dst; /* destination */ + struct SmoothView3DState org; /* original */ + + bool to_camera; + + bool use_dyn_ofs; + float dyn_ofs[3]; + + /* When smooth-view is enabled, store the 'rv3d->view' here, + * assign back when the view motion is completed. */ + char org_view; + + double time_allowed; +}; + +static void view3d_smooth_view_state_backup(struct SmoothView3DState *sms_state, + const View3D *v3d, + const RegionView3D *rv3d) +{ + copy_v3_v3(sms_state->ofs, rv3d->ofs); + copy_qt_qt(sms_state->quat, rv3d->viewquat); + sms_state->dist = rv3d->dist; + sms_state->lens = v3d->lens; +} + +static void view3d_smooth_view_state_restore(const struct SmoothView3DState *sms_state, + View3D *v3d, + RegionView3D *rv3d) +{ + copy_v3_v3(rv3d->ofs, sms_state->ofs); + copy_qt_qt(rv3d->viewquat, sms_state->quat); + rv3d->dist = sms_state->dist; + v3d->lens = sms_state->lens; +} + +/* will start timer if appropriate */ +void ED_view3d_smooth_view_ex( + /* avoid passing in the context */ + const Depsgraph *depsgraph, + wmWindowManager *wm, + wmWindow *win, + ScrArea *area, + View3D *v3d, + ARegion *region, + const int smooth_viewtx, + const V3D_SmoothParams *sview) +{ + RegionView3D *rv3d = region->regiondata; + struct SmoothView3DStore sms = {{0}}; + + /* initialize sms */ + view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d); + view3d_smooth_view_state_backup(&sms.src, v3d, rv3d); + /* If smooth-view runs multiple times. */ + if (rv3d->sms == NULL) { + view3d_smooth_view_state_backup(&sms.org, v3d, rv3d); + } + else { + sms.org = rv3d->sms->org; + } + sms.org_view = rv3d->view; + + /* sms.to_camera = false; */ /* initialized to zero anyway */ + + /* note on camera locking, this is a little confusing but works ok. + * we may be changing the view 'as if' there is no active camera, but in fact + * there is an active camera which is locked to the view. + * + * In the case where smooth view is moving _to_ a camera we don't want that + * camera to be moved or changed, so only when the camera is not being set should + * we allow camera option locking to initialize the view settings from the camera. + */ + if (sview->camera == NULL && sview->camera_old == NULL) { + ED_view3d_camera_lock_init(depsgraph, v3d, rv3d); + } + + /* store the options we want to end with */ + if (sview->ofs) { + copy_v3_v3(sms.dst.ofs, sview->ofs); + } + if (sview->quat) { + copy_qt_qt(sms.dst.quat, sview->quat); + } + if (sview->dist) { + sms.dst.dist = *sview->dist; + } + if (sview->lens) { + sms.dst.lens = *sview->lens; + } + + if (sview->dyn_ofs) { + BLI_assert(sview->ofs == NULL); + BLI_assert(sview->quat != NULL); + + copy_v3_v3(sms.dyn_ofs, sview->dyn_ofs); + sms.use_dyn_ofs = true; + + /* calculate the final destination offset */ + view3d_orbit_apply_dyn_ofs(sms.dst.ofs, sms.src.ofs, sms.src.quat, sms.dst.quat, sms.dyn_ofs); + } + + if (sview->camera) { + Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera); + if (sview->ofs != NULL) { + sms.dst.dist = ED_view3d_offset_distance( + ob_camera_eval->obmat, sview->ofs, VIEW3D_DIST_FALLBACK); + } + ED_view3d_from_object(ob_camera_eval, sms.dst.ofs, sms.dst.quat, &sms.dst.dist, &sms.dst.lens); + sms.to_camera = true; /* restore view3d values in end */ + } + + if ((sview->camera_old == sview->camera) && /* Camera. */ + (sms.dst.dist == rv3d->dist) && /* Distance. */ + (sms.dst.lens == v3d->lens) && /* Lens. */ + equals_v3v3(sms.dst.ofs, rv3d->ofs) && /* Offset. */ + equals_v4v4(sms.dst.quat, rv3d->viewquat) /* Rotation. */ + ) { + /* Early return if nothing changed. */ + return; + } + + /* Skip smooth viewing for external render engine draw. */ + if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { + + /* original values */ + if (sview->camera_old) { + Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old); + if (sview->ofs != NULL) { + sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f); + } + ED_view3d_from_object( + ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens); + } + /* grid draw as floor */ + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { + /* use existing if exists, means multiple calls to smooth view + * won't lose the original 'view' setting */ + rv3d->view = RV3D_VIEW_USER; + } + + sms.time_allowed = (double)smooth_viewtx / 1000.0; + + /* If this is view rotation only we can decrease the time allowed by the angle between quats + * this means small rotations won't lag. */ + if (sview->quat && !sview->ofs && !sview->dist) { + /* scale the time allowed by the rotation */ + /* 180deg == 1.0 */ + sms.time_allowed *= (double)fabsf(angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) / + M_PI; + } + + /* ensure it shows correct */ + if (sms.to_camera) { + /* use ortho if we move from an ortho view to an ortho camera */ + Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera); + rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) && + (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ? + RV3D_ORTHO : + RV3D_PERSP); + } + + rv3d->rflag |= RV3D_NAVIGATING; + + /* not essential but in some cases the caller will tag the area for redraw, and in that + * case we can get a flicker of the 'org' user view but we want to see 'src' */ + view3d_smooth_view_state_restore(&sms.src, v3d, rv3d); + + /* keep track of running timer! */ + if (rv3d->sms == NULL) { + rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d"); + } + *rv3d->sms = sms; + if (rv3d->smooth_timer) { + WM_event_remove_timer(wm, win, rv3d->smooth_timer); + } + /* #TIMER1 is hard-coded in key-map. */ + rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); + } + else { + /* Animation is disabled, apply immediately. */ + if (sms.to_camera == false) { + copy_v3_v3(rv3d->ofs, sms.dst.ofs); + copy_qt_qt(rv3d->viewquat, sms.dst.quat); + rv3d->dist = sms.dst.dist; + v3d->lens = sms.dst.lens; + + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + } + + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_copy(area, region); + } + + ED_region_tag_redraw(region); + + WM_event_add_mousemove(win); + } +} + +void ED_view3d_smooth_view(bContext *C, + View3D *v3d, + ARegion *region, + const int smooth_viewtx, + const struct V3D_SmoothParams *sview) +{ + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + ScrArea *area = CTX_wm_area(C); + + ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview); +} + +/* only meant for timer usage */ +static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview) +{ + wmWindowManager *wm = CTX_wm_manager(C); + RegionView3D *rv3d = region->regiondata; + struct SmoothView3DStore *sms = rv3d->sms; + float step, step_inv; + + if (sms->time_allowed != 0.0) { + step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed); + } + else { + step = 1.0f; + } + + /* end timer */ + if (step >= 1.0f) { + wmWindow *win = CTX_wm_window(C); + + /* if we went to camera, store the original */ + if (sms->to_camera) { + rv3d->persp = RV3D_CAMOB; + view3d_smooth_view_state_restore(&sms->org, v3d, rv3d); + } + else { + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d); + + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); + } + + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { + rv3d->view = sms->org_view; + } + + MEM_freeN(rv3d->sms); + rv3d->sms = NULL; + + WM_event_remove_timer(wm, win, rv3d->smooth_timer); + rv3d->smooth_timer = NULL; + rv3d->rflag &= ~RV3D_NAVIGATING; + + /* Event handling won't know if a UI item has been moved under the pointer. */ + WM_event_add_mousemove(win); + } + else { + /* ease in/out */ + step = (3.0f * step * step - 2.0f * step * step * step); + + step_inv = 1.0f - step; + + interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step); + + if (sms->use_dyn_ofs) { + view3d_orbit_apply_dyn_ofs( + rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs); + } + else { + interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step); + } + + rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv; + v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv; + + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + if (ED_screen_animation_playing(wm)) { + ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); + } + } + + if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) { + view3d_boxview_copy(CTX_wm_area(C), region); + } + + /* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636, + * when switching camera in quad-view the other ortho views would zoom & reset. + * + * For now only redraw all regions when smooth-view finishes. + */ + if (step >= 1.0f) { + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); + } + else { + ED_region_tag_redraw(region); + } +} + +static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + View3D *v3d = CTX_wm_view3d(C); + ARegion *region = CTX_wm_region(C); + RegionView3D *rv3d = region->regiondata; + + /* escape if not our timer */ + if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) { + return OPERATOR_PASS_THROUGH; + } + + view3d_smoothview_apply(C, v3d, region, true); + + return OPERATOR_FINISHED; +} + +void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region) +{ + RegionView3D *rv3d = region->regiondata; + + if (rv3d && rv3d->sms) { + rv3d->sms->time_allowed = 0.0; /* force finishing */ + view3d_smoothview_apply(C, v3d, region, false); + + /* force update of view matrix so tools that run immediately after + * can use them without redrawing first */ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false); + } +} + +void VIEW3D_OT_smoothview(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Smooth View"; + ot->idname = "VIEW3D_OT_smoothview"; + + /* api callbacks */ + ot->invoke = view3d_smoothview_invoke; + + /* flags */ + ot->flag = OPTYPE_INTERNAL; + + ot->poll = ED_operator_view3d_active; +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index ed76b10c95a..d72fa3cb90f 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -58,6 +58,7 @@ #include "DEG_depsgraph.h" #include "view3d_intern.h" /* own include */ +#include "view3d_navigate.h" #ifdef WITH_INPUT_NDOF //# define NDOF_WALK_DEBUG diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c new file mode 100644 index 00000000000..a6c7d06c079 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c @@ -0,0 +1,598 @@ +/* + * 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 spview3d + */ + +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "BKE_context.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "PIL_time.h" + +#include "view3d_intern.h" +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name View Zoom Operator + * \{ */ + +/* #viewdolly_modal_keymap has an exact copy of this, apply fixes to both. */ +void viewzoom_modal_keymap(wmKeyConfig *keyconf) +{ + static const EnumPropertyItem modal_items[] = { + {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + + {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"}, + {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"}, + + {0, NULL, 0, NULL, NULL}, + }; + + wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Zoom Modal"); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) { + return; + } + + keymap = WM_modalkeymap_ensure(keyconf, "View3D Zoom Modal", modal_items); + + /* disabled mode switching for now, can re-implement better, later on */ +#if 0 + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); + WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); +#endif + + /* assign map to operators */ + WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom"); +} + +/** + * \param zoom_xy: Optionally zoom to window location + * (coords compatible w/ #wmEvent.xy). Use when not NULL. + */ +static void view_zoom_to_window_xy_camera(Scene *scene, + Depsgraph *depsgraph, + View3D *v3d, + ARegion *region, + float dfac, + const int zoom_xy[2]) +{ + RegionView3D *rv3d = region->regiondata; + const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom); + const float zoomfac_new = clamp_f( + zoomfac * (1.0f / dfac), RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR); + const float camzoom_new = BKE_screen_view3d_zoom_from_fac(zoomfac_new); + + if (zoom_xy != NULL) { + float zoomfac_px; + rctf camera_frame_old; + rctf camera_frame_new; + + const float pt_src[2] = {zoom_xy[0], zoom_xy[1]}; + float pt_dst[2]; + float delta_px[2]; + + ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_old, false); + BLI_rctf_translate(&camera_frame_old, region->winrct.xmin, region->winrct.ymin); + + rv3d->camzoom = camzoom_new; + CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); + + ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_new, false); + BLI_rctf_translate(&camera_frame_new, region->winrct.xmin, region->winrct.ymin); + + BLI_rctf_transform_pt_v(&camera_frame_new, &camera_frame_old, pt_dst, pt_src); + sub_v2_v2v2(delta_px, pt_dst, pt_src); + + /* translate the camera offset using pixel space delta + * mapped back to the camera (same logic as panning in camera view) */ + zoomfac_px = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f; + + rv3d->camdx += delta_px[0] / (region->winx * zoomfac_px); + rv3d->camdy += delta_px[1] / (region->winy * zoomfac_px); + CLAMP(rv3d->camdx, -1.0f, 1.0f); + CLAMP(rv3d->camdy, -1.0f, 1.0f); + } + else { + rv3d->camzoom = camzoom_new; + CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); + } +} + +/** + * \param zoom_xy: Optionally zoom to window location + * (coords compatible w/ #wmEvent.xy). Use when not NULL. + */ +static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoom_xy[2]) +{ + RegionView3D *rv3d = region->regiondata; + const float dist_new = rv3d->dist * dfac; + + if (zoom_xy != NULL) { + float dvec[3]; + float tvec[3]; + float tpos[3]; + float mval_f[2]; + + float zfac; + + negate_v3_v3(tpos, rv3d->ofs); + + mval_f[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f; + mval_f[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f; + + /* Project cursor position into 3D space */ + zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL); + ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + + /* Calculate view target position for dolly */ + add_v3_v3v3(tvec, tpos, dvec); + negate_v3(tvec); + + /* Offset to target position and dolly */ + copy_v3_v3(rv3d->ofs, tvec); + rv3d->dist = dist_new; + + /* Calculate final offset */ + madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac); + } + else { + rv3d->dist = dist_new; + } +} + +static float viewzoom_scale_value(const rcti *winrct, + const eViewZoom_Style viewzoom, + const bool zoom_invert, + const bool zoom_invert_force, + const int xy_curr[2], + const int xy_init[2], + const float val, + const float val_orig, + double *r_timer_lastdraw) +{ + float zfac; + + if (viewzoom == USER_ZOOM_CONTINUE) { + double time = PIL_check_seconds_timer(); + float time_step = (float)(time - *r_timer_lastdraw); + float fac; + + if (U.uiflag & USER_ZOOM_HORIZ) { + fac = (float)(xy_init[0] - xy_curr[0]); + } + else { + fac = (float)(xy_init[1] - xy_curr[1]); + } + + fac /= U.dpi_fac; + + if (zoom_invert != zoom_invert_force) { + fac = -fac; + } + + zfac = 1.0f + ((fac / 20.0f) * time_step); + *r_timer_lastdraw = time; + } + else if (viewzoom == USER_ZOOM_SCALE) { + /* method which zooms based on how far you move the mouse */ + + const int ctr[2] = { + BLI_rcti_cent_x(winrct), + BLI_rcti_cent_y(winrct), + }; + float len_new = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_curr) / U.dpi_fac); + float len_old = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_init) / U.dpi_fac); + + /* intentionally ignore 'zoom_invert' for scale */ + if (zoom_invert_force) { + SWAP(float, len_new, len_old); + } + + zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val; + } + else { /* USER_ZOOM_DOLLY */ + float len_new = 5 * U.dpi_fac; + float len_old = 5 * U.dpi_fac; + + if (U.uiflag & USER_ZOOM_HORIZ) { + len_new += (winrct->xmax - (xy_curr[0])) / U.dpi_fac; + len_old += (winrct->xmax - (xy_init[0])) / U.dpi_fac; + } + else { + len_new += (winrct->ymax - (xy_curr[1])) / U.dpi_fac; + len_old += (winrct->ymax - (xy_init[1])) / U.dpi_fac; + } + + if (zoom_invert != zoom_invert_force) { + SWAP(float, len_new, len_old); + } + + zfac = val_orig * (2.0f * ((len_new / max_ff(len_old, 1.0f)) - 1.0f) + 1.0f) / val; + } + + return zfac; +} + +static float viewzoom_scale_value_offset(const rcti *winrct, + const eViewZoom_Style viewzoom, + const bool zoom_invert, + const bool zoom_invert_force, + const int xy_curr[2], + const int xy_init[2], + const int xy_offset[2], + const float val, + const float val_orig, + double *r_timer_lastdraw) +{ + const int xy_curr_offset[2] = { + xy_curr[0] + xy_offset[0], + xy_curr[1] + xy_offset[1], + }; + const int xy_init_offset[2] = { + xy_init[0] + xy_offset[0], + xy_init[1] + xy_offset[1], + }; + return viewzoom_scale_value(winrct, + viewzoom, + zoom_invert, + zoom_invert_force, + xy_curr_offset, + xy_init_offset, + val, + val_orig, + r_timer_lastdraw); +} + +static void viewzoom_apply_camera(ViewOpsData *vod, + const int xy[2], + const eViewZoom_Style viewzoom, + const bool zoom_invert, + const bool zoom_to_pos) +{ + float zfac; + float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->init.camzoom) * 2.0f; + float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f; + + zfac = viewzoom_scale_value_offset(&vod->region->winrct, + viewzoom, + zoom_invert, + true, + xy, + vod->init.event_xy, + vod->init.event_xy_offset, + zoomfac, + zoomfac_prev, + &vod->prev.time); + + if (!ELEM(zfac, 1.0f, 0.0f)) { + /* calculate inverted, then invert again (needed because of camera zoom scaling) */ + zfac = 1.0f / zfac; + view_zoom_to_window_xy_camera(vod->scene, + vod->depsgraph, + vod->v3d, + vod->region, + zfac, + zoom_to_pos ? vod->prev.event_xy : NULL); + } + + ED_region_tag_redraw(vod->region); +} + +static void viewzoom_apply_3d(ViewOpsData *vod, + const int xy[2], + const eViewZoom_Style viewzoom, + const bool zoom_invert, + const bool zoom_to_pos) +{ + float zfac; + float dist_range[2]; + + ED_view3d_dist_range_get(vod->v3d, dist_range); + + zfac = viewzoom_scale_value_offset(&vod->region->winrct, + viewzoom, + zoom_invert, + false, + xy, + vod->init.event_xy, + vod->init.event_xy_offset, + vod->rv3d->dist, + vod->init.dist, + &vod->prev.time); + + if (zfac != 1.0f) { + const float zfac_min = dist_range[0] / vod->rv3d->dist; + const float zfac_max = dist_range[1] / vod->rv3d->dist; + CLAMP(zfac, zfac_min, zfac_max); + + view_zoom_to_window_xy_3d(vod->region, zfac, zoom_to_pos ? vod->prev.event_xy : NULL); + } + + /* these limits were in old code too */ + CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]); + + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { + view3d_boxview_sync(vod->area, vod->region); + } + + ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); + + ED_region_tag_redraw(vod->region); +} + +static void viewzoom_apply(ViewOpsData *vod, + const int xy[2], + const eViewZoom_Style viewzoom, + const bool zoom_invert, + const bool zoom_to_pos) +{ + if ((vod->rv3d->persp == RV3D_CAMOB) && + (vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0) { + viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert, zoom_to_pos); + } + else { + viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert, zoom_to_pos); + } +} + +static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod = op->customdata; + short event_code = VIEW_PASS; + bool use_autokey = false; + int ret = OPERATOR_RUNNING_MODAL; + + /* execute the events */ + if (event->type == TIMER && event->customdata == vod->timer) { + /* continuous zoom */ + event_code = VIEW_APPLY; + } + else if (event->type == MOUSEMOVE) { + event_code = VIEW_APPLY; + } + else if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case VIEW_MODAL_CONFIRM: + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_MOVE: + WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + case VIEWROT_MODAL_SWITCH_ROTATE: + WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); + event_code = VIEW_CONFIRM; + break; + } + } + else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { + event_code = VIEW_CONFIRM; + } + + if (event_code == VIEW_APPLY) { + const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); + viewzoom_apply(vod, + event->xy, + (eViewZoom_Style)U.viewzoom, + (U.uiflag & USER_ZOOM_INVERT) != 0, + (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); + if (ED_screen_animation_playing(CTX_wm_manager(C))) { + use_autokey = true; + } + } + else if (event_code == VIEW_CONFIRM) { + use_autokey = true; + ret = OPERATOR_FINISHED; + } + + if (use_autokey) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); + } + + if (ret & OPERATOR_FINISHED) { + viewops_data_free(C, op->customdata); + op->customdata = NULL; + } + + return ret; +} + +static int viewzoom_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + View3D *v3d; + RegionView3D *rv3d; + ScrArea *area; + ARegion *region; + bool use_cam_zoom; + float dist_range[2]; + + const int delta = RNA_int_get(op->ptr, "delta"); + const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); + + if (op->customdata) { + ViewOpsData *vod = op->customdata; + + area = vod->area; + region = vod->region; + } + else { + area = CTX_wm_area(C); + region = CTX_wm_region(C); + } + + v3d = area->spacedata.first; + rv3d = region->regiondata; + + use_cam_zoom = (rv3d->persp == RV3D_CAMOB) && + !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d)); + + int zoom_xy_buf[2]; + const int *zoom_xy = NULL; + if (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) { + zoom_xy_buf[0] = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") : + region->winx / 2; + zoom_xy_buf[1] = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") : + region->winy / 2; + zoom_xy = zoom_xy_buf; + } + + ED_view3d_dist_range_get(v3d, dist_range); + + if (delta < 0) { + const float step = 1.2f; + if (use_cam_zoom) { + view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); + } + else { + if (rv3d->dist < dist_range[1]) { + view_zoom_to_window_xy_3d(region, step, zoom_xy); + } + } + } + else { + const float step = 1.0f / 1.2f; + if (use_cam_zoom) { + view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); + } + else { + if (rv3d->dist > dist_range[0]) { + view_zoom_to_window_xy_3d(region, step, zoom_xy); + } + } + } + + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_sync(area, region); + } + + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true); + + ED_region_tag_redraw(region); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + + return OPERATOR_FINISHED; +} + +/* viewdolly_invoke() copied this function, changes here may apply there */ +static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ViewOpsData *vod; + + const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); + + vod = op->customdata = viewops_data_create( + C, + event, + (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) | + (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); + + /* if one or the other zoom position aren't set, set from event */ + if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { + RNA_int_set(op->ptr, "mx", event->xy[0]); + RNA_int_set(op->ptr, "my", event->xy[1]); + } + + if (RNA_struct_property_is_set(op->ptr, "delta")) { + viewzoom_exec(C, op); + } + else { + if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { + + if (U.uiflag & USER_ZOOM_HORIZ) { + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; + } + else { + /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */ + vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] - + event->prev_xy[0]; + } + viewzoom_apply(vod, + event->prev_xy, + USER_ZOOM_DOLLY, + (U.uiflag & USER_ZOOM_INVERT) != 0, + (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); + + viewops_data_free(C, op->customdata); + op->customdata = NULL; + return OPERATOR_FINISHED; + } + + if (U.viewzoom == USER_ZOOM_CONTINUE) { + /* needs a timer to continue redrawing */ + vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f); + vod->prev.time = PIL_check_seconds_timer(); + } + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_FINISHED; +} + +static void viewzoom_cancel(bContext *C, wmOperator *op) +{ + viewops_data_free(C, op->customdata); + op->customdata = NULL; +} + +void VIEW3D_OT_zoom(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Zoom View"; + ot->description = "Zoom in/out in the view"; + ot->idname = "VIEW3D_OT_zoom"; + + /* api callbacks */ + ot->invoke = viewzoom_invoke; + ot->exec = viewzoom_exec; + ot->modal = viewzoom_modal; + ot->poll = view3d_zoom_or_dolly_poll; + ot->cancel = viewzoom_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; + + /* properties */ + view3d_operator_properties_common( + ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT); +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c new file mode 100644 index 00000000000..38c3e37bac6 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c @@ -0,0 +1,221 @@ +/* + * 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 spview3d + */ + +#include "DNA_camera_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "BKE_context.h" +#include "BKE_report.h" + +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" + +#include "RNA_access.h" + +#include "view3d_intern.h" +#include "view3d_navigate.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name Border Zoom Operator + * \{ */ + +static int view3d_zoom_border_exec(bContext *C, wmOperator *op) +{ + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + /* Zooms in on a border drawn by the user */ + rcti rect; + float dvec[3], vb[2], xscale, yscale; + float dist_range[2]; + + /* SMOOTHVIEW */ + float new_dist; + float new_ofs[3]; + + /* ZBuffer depth vars */ + float depth_close = FLT_MAX; + float cent[2], p[3]; + + /* NOTE: otherwise opengl won't work. */ + view3d_operator_needs_opengl(C); + + /* get box select values using rna */ + WM_operator_properties_border_to_rcti(op, &rect); + + /* check if zooming in/out view */ + const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out"); + + 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, NULL); + { + /* avoid allocating the whole depth buffer */ + ViewDepths depth_temp = {0}; + + /* avoid view3d_update_depths() for speed. */ + view3d_depths_rect_create(region, &rect, &depth_temp); + + /* find the closest Z pixel */ + depth_close = view3d_depth_near(&depth_temp); + + MEM_SAFE_FREE(depth_temp.depths); + } + + /* Resize border to the same ratio as the window. */ + { + const float region_aspect = (float)region->winx / (float)region->winy; + if (((float)BLI_rcti_size_x(&rect) / (float)BLI_rcti_size_y(&rect)) < region_aspect) { + BLI_rcti_resize_x(&rect, (int)(BLI_rcti_size_y(&rect) * region_aspect)); + } + else { + BLI_rcti_resize_y(&rect, (int)(BLI_rcti_size_x(&rect) / region_aspect)); + } + } + + cent[0] = (((float)rect.xmin) + ((float)rect.xmax)) / 2; + cent[1] = (((float)rect.ymin) + ((float)rect.ymax)) / 2; + + if (rv3d->is_persp) { + float p_corner[3]; + + /* 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; + } + /* convert border to 3d coordinates */ + if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) || + (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) { + return OPERATOR_CANCELLED; + } + + sub_v3_v3v3(dvec, p, p_corner); + negate_v3_v3(new_ofs, p); + + new_dist = len_v3(dvec); + + /* Account for the lens, without this a narrow lens zooms in too close. */ + new_dist *= (v3d->lens / DEFAULT_SENSOR_WIDTH); + + /* ignore dist_range min */ + dist_range[0] = v3d->clip_start * 1.5f; + } + else { /* orthographic */ + /* find the current window width and height */ + vb[0] = region->winx; + vb[1] = region->winy; + + new_dist = rv3d->dist; + + /* convert the drawn rectangle into 3d space */ + if (depth_close != FLT_MAX && + ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) { + negate_v3_v3(new_ofs, p); + } + else { + float mval_f[2]; + float zfac; + + /* We can't use the depth, fallback to the old way that doesn't set the center depth */ + copy_v3_v3(new_ofs, rv3d->ofs); + + { + float tvec[3]; + negate_v3_v3(tvec, new_ofs); + zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL); + } + + mval_f[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f; + mval_f[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f; + ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + /* center the view to the center of the rectangle */ + sub_v3_v3(new_ofs, dvec); + } + + /* work out the ratios, so that everything selected fits when we zoom */ + xscale = (BLI_rcti_size_x(&rect) / vb[0]); + yscale = (BLI_rcti_size_y(&rect) / vb[1]); + new_dist *= max_ff(xscale, yscale); + } + + if (!zoom_in) { + sub_v3_v3v3(dvec, new_ofs, rv3d->ofs); + new_dist = rv3d->dist * (rv3d->dist / new_dist); + add_v3_v3v3(new_ofs, rv3d->ofs, dvec); + } + + /* clamp after because we may have been zooming out */ + CLAMP(new_dist, dist_range[0], dist_range[1]); + + /* TODO(campbell): 'is_camera_lock' not currently working well. */ + const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d); + if ((rv3d->persp == RV3D_CAMOB) && (is_camera_lock == false)) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP); + } + + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .ofs = new_ofs, + .dist = &new_dist, + }); + + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_sync(CTX_wm_area(C), region); + } + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_zoom_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Zoom to Border"; + ot->description = "Zoom in the view to the nearest object contained in the border"; + ot->idname = "VIEW3D_OT_zoom_border"; + + /* api callbacks */ + ot->invoke = WM_gesture_box_invoke; + ot->exec = view3d_zoom_border_exec; + ot->modal = WM_gesture_box_modal; + ot->cancel = WM_gesture_box_cancel; + + ot->poll = view3d_zoom_or_dolly_poll; + + /* flags */ + ot->flag = 0; + + /* properties */ + WM_operator_properties_gesture_box_zoom(ot); +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 823aa3b6643..52db8526937 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -50,6 +50,7 @@ #include "ED_transform.h" #include "view3d_intern.h" +#include "view3d_navigate.h" #ifdef WIN32 # include "BLI_math_base.h" /* M_PI */ diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 45899880b41..34aa24a1eef 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -99,6 +99,7 @@ #include "UI_resources.h" #include "GPU_matrix.h" +#include "GPU_select.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -1566,8 +1567,8 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot) static Base *object_mouse_select_menu(bContext *C, ViewContext *vc, - const uint *buffer, - int hits, + const GPUSelectResult *buffer, + const int hits, const int mval[2], bool extend, bool deselect, @@ -1585,7 +1586,7 @@ static Base *object_mouse_select_menu(bContext *C, if (buffer) { for (int a = 0; a < hits; a++) { /* index was converted */ - if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & ~0xFFFF0000)) { + if (base->object->runtime.select_id == (buffer[a].id & ~0xFFFF0000)) { ok = true; break; } @@ -1742,7 +1743,7 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } static bool bone_mouse_select_menu(bContext *C, - const uint *buffer, + const GPUSelectResult *buffer, const int hits, const bool is_editmode, const bool extend, @@ -1760,7 +1761,7 @@ static bool bone_mouse_select_menu(bContext *C, for (int a = 0; a < hits; a++) { void *bone_ptr = NULL; Base *bone_base = NULL; - uint hitresult = buffer[3 + (a * 4)]; + uint hitresult = buffer[a].id; if (!(hitresult & BONESEL_ANY)) { /* To avoid including objects in selection. */ @@ -1874,10 +1875,10 @@ static bool bone_mouse_select_menu(bContext *C, return true; } -static bool selectbuffer_has_bones(const uint *buffer, const uint hits) +static bool selectbuffer_has_bones(const GPUSelectResult *buffer, const uint hits) { for (uint i = 0; i < hits; i++) { - if (buffer[(4 * i) + 3] & 0xFFFF0000) { + if (buffer[i].id & 0xFFFF0000) { return true; } } @@ -1885,25 +1886,25 @@ static bool selectbuffer_has_bones(const uint *buffer, const uint hits) } /* utility function for mixed_bones_object_selectbuffer */ -static int selectbuffer_ret_hits_15(uint *UNUSED(buffer), const int hits15) +static int selectbuffer_ret_hits_15(GPUSelectResult *UNUSED(buffer), const int hits15) { return hits15; } -static int selectbuffer_ret_hits_9(uint *buffer, const int hits15, const int hits9) +static int selectbuffer_ret_hits_9(GPUSelectResult *buffer, const int hits15, const int hits9) { - const int ofs = 4 * hits15; - memcpy(buffer, buffer + ofs, 4 * hits9 * sizeof(uint)); + const int ofs = hits15; + memcpy(buffer, buffer + ofs, hits9 * sizeof(GPUSelectResult)); return hits9; } -static int selectbuffer_ret_hits_5(uint *buffer, +static int selectbuffer_ret_hits_5(GPUSelectResult *buffer, const int hits15, const int hits9, const int hits5) { - const int ofs = 4 * hits15 + 4 * hits9; - memcpy(buffer, buffer + ofs, 4 * hits5 * sizeof(uint)); + const int ofs = hits15 + hits9; + memcpy(buffer, buffer + ofs, hits5 * sizeof(GPUSelectResult)); return hits5; } @@ -1916,7 +1917,8 @@ static int selectbuffer_ret_hits_5(uint *buffer, * Needed so we can step to the next, non-active object when it's already selected, see: T76445. */ static int mixed_bones_object_selectbuffer(ViewContext *vc, - uint *buffer, + GPUSelectResult *buffer, + const int buffer_len, const int mval[2], eV3DSelectObjectFilter select_filter, bool do_nearest, @@ -1941,7 +1943,7 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc, BLI_rcti_init_pt_radius(&rect, mval, 14); hits15 = view3d_opengl_select_ex( - vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter, do_material_slot_selection); + vc, buffer, buffer_len, &rect, select_mode, select_filter, do_material_slot_selection); if (hits15 == 1) { hits = selectbuffer_ret_hits_15(buffer, hits15); goto finally; @@ -1950,10 +1952,10 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc, int ofs; has_bones15 = selectbuffer_has_bones(buffer, hits15); - ofs = 4 * hits15; + ofs = hits15; BLI_rcti_init_pt_radius(&rect, mval, 9); hits9 = view3d_opengl_select( - vc, buffer + ofs, MAXPICKBUF - ofs, &rect, select_mode, select_filter); + vc, buffer + ofs, buffer_len - ofs, &rect, select_mode, select_filter); if (hits9 == 1) { hits = selectbuffer_ret_hits_9(buffer, hits15, hits9); goto finally; @@ -1961,10 +1963,10 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc, else if (hits9 > 0) { has_bones9 = selectbuffer_has_bones(buffer + ofs, hits9); - ofs += 4 * hits9; + ofs += hits9; BLI_rcti_init_pt_radius(&rect, mval, 5); hits5 = view3d_opengl_select( - vc, buffer + ofs, MAXPICKBUF - ofs, &rect, select_mode, select_filter); + vc, buffer + ofs, buffer_len - ofs, &rect, select_mode, select_filter); if (hits5 == 1) { hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5); goto finally; @@ -2007,7 +2009,8 @@ finally: } static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, - uint *buffer, + GPUSelectResult *buffer, + const int buffer_len, const int mval[2], eV3DSelectObjectFilter select_filter, bool use_cycle, @@ -2038,7 +2041,7 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, do_nearest = do_nearest && !enumerate; int hits = mixed_bones_object_selectbuffer( - vc, buffer, mval, select_filter, do_nearest, true, false); + vc, buffer, buffer_len, mval, select_filter, do_nearest, true, false); return hits; } @@ -2051,7 +2054,7 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, * \return the active base or NULL. */ static Base *mouse_select_eval_buffer(ViewContext *vc, - const uint *buffer, + const GPUSelectResult *buffer, int hits, Base *startbase, bool has_bones, @@ -2071,10 +2074,10 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, if (has_bones) { /* we skip non-bone hits */ for (a = 0; a < hits; a++) { - if (min > buffer[4 * a + 1] && (buffer[4 * a + 3] & 0xFFFF0000)) { - min = buffer[4 * a + 1]; - selcol = buffer[4 * a + 3] & 0xFFFF; - sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16; + if (min > buffer[a].depth && (buffer[a].id & 0xFFFF0000)) { + min = buffer[a].depth; + selcol = buffer[a].id & 0xFFFF; + sub_selection_id = (buffer[a].id & 0xFFFF0000) >> 16; } } } @@ -2085,10 +2088,10 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, } for (a = 0; a < hits; a++) { - if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) { - min = buffer[4 * a + 1]; - selcol = buffer[4 * a + 3] & 0xFFFF; - sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16; + if (min > buffer[a].depth && notcol != (buffer[a].id & 0xFFFF)) { + min = buffer[a].depth; + selcol = buffer[a].id & 0xFFFF; + sub_selection_id = (buffer[a].id & 0xFFFF0000) >> 16; } } } @@ -2127,14 +2130,14 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, for (a = 0; a < hits; a++) { if (has_bones) { /* skip non-bone objects */ - if (buffer[4 * a + 3] & 0xFFFF0000) { - if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) { + if (buffer[a].id & 0xFFFF0000) { + if (base->object->runtime.select_id == (buffer[a].id & 0xFFFF)) { basact = base; } } } else { - if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) { + if (base->object->runtime.select_id == (buffer[a].id & 0xFFFF)) { basact = base; } } @@ -2169,7 +2172,7 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C, Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; Base *basact = NULL; - uint buffer[MAXPICKBUF]; + GPUSelectResult buffer[MAXPICKELEMS]; /* setup view context for argument to callbacks */ view3d_operator_needs_opengl(C); @@ -2179,8 +2182,14 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C, const bool do_nearest = !XRAY_ACTIVE(vc.v3d); const bool do_material_slot_selection = r_material_slot != NULL; - const int hits = mixed_bones_object_selectbuffer( - &vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false, do_material_slot_selection); + const int hits = mixed_bones_object_selectbuffer(&vc, + buffer, + ARRAY_SIZE(buffer), + mval, + VIEW3D_SELECT_FILTER_NOP, + do_nearest, + false, + do_material_slot_selection); if (hits > 0) { const bool has_bones = (r_material_slot == NULL) && selectbuffer_has_bones(buffer, hits); @@ -2342,7 +2351,7 @@ static bool ed_object_select_pick(bContext *C, } } else { - uint buffer[MAXPICKBUF]; + GPUSelectResult buffer[MAXPICKELEMS]; bool do_nearest; // TIMEIT_START(select_time); @@ -2353,7 +2362,7 @@ static bool ed_object_select_pick(bContext *C, vc.obact) : VIEW3D_SELECT_FILTER_NOP); hits = mixed_bones_object_selectbuffer_extended( - &vc, buffer, mval, select_filter, true, enumerate, &do_nearest); + &vc, buffer, ARRAY_SIZE(buffer), mval, select_filter, true, enumerate, &do_nearest); // TIMEIT_END(select_time); @@ -2383,7 +2392,7 @@ static bool ed_object_select_pick(bContext *C, bool changed = false; for (int i = 0; i < hits; i++) { - int hitresult = buffer[3 + (i * 4)]; + const int hitresult = buffer[i].id; /* if there's bundles in buffer select bundles first, * so non-camera elements should be ignored in buffer */ @@ -2394,7 +2403,7 @@ static bool ed_object_select_pick(bContext *C, /* index of bundle is 1<<16-based. if there's no "bone" index * in height word, this buffer value belongs to camera. not to bundle */ - if (buffer[4 * i + 3] & 0xFFFF0000) { + if (hitresult & 0xFFFF0000) { MovieTracking *tracking = &clip->tracking; ListBase *tracksbase; MovieTrackingTrack *track; @@ -2674,9 +2683,15 @@ static int view3d_select_exec(bContext *C, wmOperator *op) ViewContext vc; ED_view3d_viewcontext_init(C, &vc, depsgraph); - uint buffer[MAXPICKBUF]; - const int hits = mixed_bones_object_selectbuffer( - &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true, false); + GPUSelectResult buffer[MAXPICKELEMS]; + const int hits = mixed_bones_object_selectbuffer(&vc, + buffer, + ARRAY_SIZE(buffer), + location, + VIEW3D_SELECT_FILTER_NOP, + false, + true, + false); retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle); } if (!retval) { @@ -3256,11 +3271,11 @@ static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectO int a; bool changed = false; - uint buffer[MAXPICKBUF]; + GPUSelectResult buffer[MAXPICKELEMS]; int hits; hits = view3d_opengl_select( - vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP); + vc, buffer, MAXPICKELEMS, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { changed |= BKE_mball_deselect_all(mb); @@ -3272,7 +3287,7 @@ static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectO bool is_inside_stiff = false; for (a = 0; a < hits; a++) { - int hitresult = buffer[(4 * a) + 3]; + const int hitresult = buffer[a].id; if (hitresult == -1) { continue; @@ -3323,11 +3338,11 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel bool changed = false; int a; - uint buffer[MAXPICKBUF]; + GPUSelectResult buffer[MAXPICKELEMS]; int hits; hits = view3d_opengl_select( - vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP); + vc, buffer, MAXPICKELEMS, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( @@ -3347,7 +3362,7 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel /* first we only check points inside the border */ for (a = 0; a < hits; a++) { - int select_id = buffer[(4 * a) + 3]; + const int select_id = buffer[a].id; if (select_id != -1) { if ((select_id & 0xFFFF0000) == 0) { continue; @@ -3375,14 +3390,13 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel } /** - * Compare result of 'GPU_select': 'uint[4]', + * Compare result of 'GPU_select': 'GPUSelectResult', * needed for when we need to align with object draw-order. */ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p) { - /* 4th element is select id */ - uint sel_a = ((uint *)sel_a_p)[3]; - uint sel_b = ((uint *)sel_b_p)[3]; + uint sel_a = ((GPUSelectResult *)sel_a_p)->id; + uint sel_b = ((GPUSelectResult *)sel_b_p)->id; #ifdef __BIG_ENDIAN__ BLI_endian_switch_uint32(&sel_a); @@ -3401,14 +3415,15 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_ static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op) { View3D *v3d = vc->v3d; - int totobj = MAXPICKBUF; /* XXX solve later */ + int totobj = MAXPICKELEMS; /* XXX solve later */ - /* selection buffer now has bones potentially too, so we add MAXPICKBUF */ - uint *vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer"); + /* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */ + GPUSelectResult *buffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), + "selection buffer"); const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, vc->obact); const int hits = view3d_opengl_select( - vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); LISTBASE_FOREACH (Base *, base, &vc->view_layer->object_bases) { base->object->id.tag &= ~LIB_TAG_DOIT; @@ -3435,12 +3450,13 @@ static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const } /* The draw order doesn't always match the order we populate the engine, see: T51695. */ - qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp); + qsort(buffer, hits, sizeof(GPUSelectResult), opengl_bone_select_buffer_cmp); - for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) { + for (const GPUSelectResult *buf_iter = buffer, *buf_end = buf_iter + hits; buf_iter < buf_end; + buf_iter++) { bPoseChannel *pchan_dummy; Base *base = ED_armature_base_and_pchan_from_select_buffer( - bases, BLI_array_len(bases), *col, &pchan_dummy); + bases, BLI_array_len(bases), buf_iter->id, &pchan_dummy); if (base != NULL) { base->object->id.tag |= LIB_TAG_DOIT; } @@ -3463,7 +3479,7 @@ finally: MEM_freeN(bases); } - MEM_freeN(vbuffer); + MEM_freeN(buffer); if (changed) { DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); @@ -3477,14 +3493,15 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e uint bases_len; Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len); - int totobj = MAXPICKBUF; /* XXX solve later */ + int totobj = MAXPICKELEMS; /* XXX solve later */ - /* selection buffer now has bones potentially too, so we add MAXPICKBUF */ - uint *vbuffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer"); + /* Selection buffer has bones potentially too, so add #MAXPICKELEMS. */ + GPUSelectResult *buffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), + "selection buffer"); const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, vc->obact); const int hits = view3d_opengl_select( - vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); /* * LOGIC NOTES (theeth): * The buffer and ListBase have the same relative order, which makes the selection @@ -3498,18 +3515,20 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e /* no need to loop if there's no hit */ /* The draw order doesn't always match the order we populate the engine, see: T51695. */ - qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp); + qsort(buffer, hits, sizeof(GPUSelectResult), opengl_bone_select_buffer_cmp); - for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) { + for (const GPUSelectResult *buf_iter = buffer, *buf_end = buf_iter + hits; buf_iter < buf_end; + buf_iter++) { Bone *bone; - Base *base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, *col, &bone); + Base *base = ED_armature_base_and_bone_from_select_buffer( + bases, bases_len, buf_iter->id, &bone); if (base == NULL) { continue; } /* Loop over contiguous bone hits for 'base'. */ - for (; col != col_end; col += 4) { + for (; buf_iter != buf_end; buf_iter++) { /* should never fail */ if (bone != NULL) { base->object->id.tag |= LIB_TAG_DOIT; @@ -3517,12 +3536,13 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e } /* Select the next bone if we're not switching bases. */ - if (col + 4 != col_end) { - if ((base->object->runtime.select_id & 0x0000FFFF) != (col[4] & 0x0000FFFF)) { + if (buf_iter + 1 != buf_end) { + const GPUSelectResult *col_next = buf_iter + 1; + if ((base->object->runtime.select_id & 0x0000FFFF) != (col_next->id & 0x0000FFFF)) { break; } if (base->object->pose != NULL) { - const uint hit_bone = (col[4] & ~BONESEL_ANY) >> 16; + const uint hit_bone = (col_next->id & ~BONESEL_ANY) >> 16; bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone); bone = pchan ? pchan->bone : NULL; } @@ -3543,7 +3563,7 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e if (bases != NULL) { MEM_freeN(bases); } - MEM_freeN(vbuffer); + MEM_freeN(buffer); return changed_multi; } diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 53bd181f544..4334ede0a06 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -1033,7 +1033,7 @@ bool ED_view3d_minmax_verts(Object *obedit, float r_min[3], float r_max[3]) } if (ED_transverts_check_obedit(obedit)) { - ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS); + ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS | TM_CALC_MAPLOC); } if (tvs.transverts_tot == 0) { diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 165f931394d..ddd5cc640bb 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -21,21 +21,14 @@ * \ingroup spview3d */ -#include "DNA_camera_types.h" -#include "DNA_gpencil_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - #include "MEM_guardedalloc.h" #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" -#include "BLI_utildefines.h" #include "BKE_action.h" -#include "BKE_camera.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_gpencil_modifier.h" @@ -47,7 +40,6 @@ #include "BKE_report.h" #include "BKE_scene.h" -#include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" #include "UI_resources.h" @@ -57,7 +49,6 @@ #include "GPU_state.h" #include "WM_api.h" -#include "WM_types.h" #include "ED_object.h" #include "ED_screen.h" @@ -68,376 +59,7 @@ #include "RNA_define.h" #include "view3d_intern.h" /* own include */ - -/* -------------------------------------------------------------------- */ -/** \name Smooth View Operator & Utilities - * - * Use for view transitions to have smooth (animated) transitions. - * \{ */ - -/* This operator is one of the 'timer refresh' ones like animation playback */ - -struct SmoothView3DState { - float dist; - float lens; - float quat[4]; - float ofs[3]; -}; - -struct SmoothView3DStore { - /* Source. */ - struct SmoothView3DState src; /* source */ - struct SmoothView3DState dst; /* destination */ - struct SmoothView3DState org; /* original */ - - bool to_camera; - - bool use_dyn_ofs; - float dyn_ofs[3]; - - /* When smooth-view is enabled, store the 'rv3d->view' here, - * assign back when the view motion is completed. */ - char org_view; - - double time_allowed; -}; - -static void view3d_smooth_view_state_backup(struct SmoothView3DState *sms_state, - const View3D *v3d, - const RegionView3D *rv3d) -{ - copy_v3_v3(sms_state->ofs, rv3d->ofs); - copy_qt_qt(sms_state->quat, rv3d->viewquat); - sms_state->dist = rv3d->dist; - sms_state->lens = v3d->lens; -} - -static void view3d_smooth_view_state_restore(const struct SmoothView3DState *sms_state, - View3D *v3d, - RegionView3D *rv3d) -{ - copy_v3_v3(rv3d->ofs, sms_state->ofs); - copy_qt_qt(rv3d->viewquat, sms_state->quat); - rv3d->dist = sms_state->dist; - v3d->lens = sms_state->lens; -} - -/* will start timer if appropriate */ -void ED_view3d_smooth_view_ex( - /* avoid passing in the context */ - const Depsgraph *depsgraph, - wmWindowManager *wm, - wmWindow *win, - ScrArea *area, - View3D *v3d, - ARegion *region, - const int smooth_viewtx, - const V3D_SmoothParams *sview) -{ - RegionView3D *rv3d = region->regiondata; - struct SmoothView3DStore sms = {{0}}; - - /* initialize sms */ - view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d); - view3d_smooth_view_state_backup(&sms.src, v3d, rv3d); - /* If smooth-view runs multiple times. */ - if (rv3d->sms == NULL) { - view3d_smooth_view_state_backup(&sms.org, v3d, rv3d); - } - else { - sms.org = rv3d->sms->org; - } - sms.org_view = rv3d->view; - - /* sms.to_camera = false; */ /* initialized to zero anyway */ - - /* note on camera locking, this is a little confusing but works ok. - * we may be changing the view 'as if' there is no active camera, but in fact - * there is an active camera which is locked to the view. - * - * In the case where smooth view is moving _to_ a camera we don't want that - * camera to be moved or changed, so only when the camera is not being set should - * we allow camera option locking to initialize the view settings from the camera. - */ - if (sview->camera == NULL && sview->camera_old == NULL) { - ED_view3d_camera_lock_init(depsgraph, v3d, rv3d); - } - - /* store the options we want to end with */ - if (sview->ofs) { - copy_v3_v3(sms.dst.ofs, sview->ofs); - } - if (sview->quat) { - copy_qt_qt(sms.dst.quat, sview->quat); - } - if (sview->dist) { - sms.dst.dist = *sview->dist; - } - if (sview->lens) { - sms.dst.lens = *sview->lens; - } - - if (sview->dyn_ofs) { - BLI_assert(sview->ofs == NULL); - BLI_assert(sview->quat != NULL); - - copy_v3_v3(sms.dyn_ofs, sview->dyn_ofs); - sms.use_dyn_ofs = true; - - /* calculate the final destination offset */ - view3d_orbit_apply_dyn_ofs(sms.dst.ofs, sms.src.ofs, sms.src.quat, sms.dst.quat, sms.dyn_ofs); - } - - if (sview->camera) { - Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera); - if (sview->ofs != NULL) { - sms.dst.dist = ED_view3d_offset_distance( - ob_camera_eval->obmat, sview->ofs, VIEW3D_DIST_FALLBACK); - } - ED_view3d_from_object(ob_camera_eval, sms.dst.ofs, sms.dst.quat, &sms.dst.dist, &sms.dst.lens); - sms.to_camera = true; /* restore view3d values in end */ - } - - if ((sview->camera_old == sview->camera) && /* Camera. */ - (sms.dst.dist == rv3d->dist) && /* Distance. */ - (sms.dst.lens == v3d->lens) && /* Lens. */ - equals_v3v3(sms.dst.ofs, rv3d->ofs) && /* Offset. */ - equals_v4v4(sms.dst.quat, rv3d->viewquat) /* Rotation. */ - ) { - /* Early return if nothing changed. */ - return; - } - - /* Skip smooth viewing for external render engine draw. */ - if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { - - /* original values */ - if (sview->camera_old) { - Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old); - if (sview->ofs != NULL) { - sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f); - } - ED_view3d_from_object( - ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens); - } - /* grid draw as floor */ - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { - /* use existing if exists, means multiple calls to smooth view - * won't lose the original 'view' setting */ - rv3d->view = RV3D_VIEW_USER; - } - - sms.time_allowed = (double)smooth_viewtx / 1000.0; - - /* If this is view rotation only we can decrease the time allowed by the angle between quats - * this means small rotations won't lag. */ - if (sview->quat && !sview->ofs && !sview->dist) { - /* scale the time allowed by the rotation */ - /* 180deg == 1.0 */ - sms.time_allowed *= (double)fabsf(angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) / - M_PI; - } - - /* ensure it shows correct */ - if (sms.to_camera) { - /* use ortho if we move from an ortho view to an ortho camera */ - Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera); - rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) && - (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ? - RV3D_ORTHO : - RV3D_PERSP); - } - - rv3d->rflag |= RV3D_NAVIGATING; - - /* not essential but in some cases the caller will tag the area for redraw, and in that - * case we can get a flicker of the 'org' user view but we want to see 'src' */ - view3d_smooth_view_state_restore(&sms.src, v3d, rv3d); - - /* keep track of running timer! */ - if (rv3d->sms == NULL) { - rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d"); - } - *rv3d->sms = sms; - if (rv3d->smooth_timer) { - WM_event_remove_timer(wm, win, rv3d->smooth_timer); - } - /* #TIMER1 is hard-coded in key-map. */ - rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); - } - else { - /* Animation is disabled, apply immediately. */ - if (sms.to_camera == false) { - copy_v3_v3(rv3d->ofs, sms.dst.ofs); - copy_qt_qt(rv3d->viewquat, sms.dst.quat); - rv3d->dist = sms.dst.dist; - v3d->lens = sms.dst.lens; - - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - } - - if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { - view3d_boxview_copy(area, region); - } - - ED_region_tag_redraw(region); - - WM_event_add_mousemove(win); - } -} - -void ED_view3d_smooth_view(bContext *C, - View3D *v3d, - ARegion *region, - const int smooth_viewtx, - const struct V3D_SmoothParams *sview) -{ - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - ScrArea *area = CTX_wm_area(C); - - ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview); -} - -/* only meant for timer usage */ -static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview) -{ - wmWindowManager *wm = CTX_wm_manager(C); - RegionView3D *rv3d = region->regiondata; - struct SmoothView3DStore *sms = rv3d->sms; - float step, step_inv; - - if (sms->time_allowed != 0.0) { - step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed); - } - else { - step = 1.0f; - } - - /* end timer */ - if (step >= 1.0f) { - wmWindow *win = CTX_wm_window(C); - - /* if we went to camera, store the original */ - if (sms->to_camera) { - rv3d->persp = RV3D_CAMOB; - view3d_smooth_view_state_restore(&sms->org, v3d, rv3d); - } - else { - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - - view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d); - - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); - } - - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { - rv3d->view = sms->org_view; - } - - MEM_freeN(rv3d->sms); - rv3d->sms = NULL; - - WM_event_remove_timer(wm, win, rv3d->smooth_timer); - rv3d->smooth_timer = NULL; - rv3d->rflag &= ~RV3D_NAVIGATING; - - /* Event handling won't know if a UI item has been moved under the pointer. */ - WM_event_add_mousemove(win); - } - else { - /* ease in/out */ - step = (3.0f * step * step - 2.0f * step * step * step); - - step_inv = 1.0f - step; - - interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step); - - if (sms->use_dyn_ofs) { - view3d_orbit_apply_dyn_ofs( - rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs); - } - else { - interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step); - } - - rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv; - v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv; - - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - if (ED_screen_animation_playing(wm)) { - ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); - } - } - - if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) { - view3d_boxview_copy(CTX_wm_area(C), region); - } - - /* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636, - * when switching camera in quad-view the other ortho views would zoom & reset. - * - * For now only redraw all regions when smooth-view finishes. - */ - if (step >= 1.0f) { - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); - } - else { - ED_region_tag_redraw(region); - } -} - -static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) -{ - View3D *v3d = CTX_wm_view3d(C); - ARegion *region = CTX_wm_region(C); - RegionView3D *rv3d = region->regiondata; - - /* escape if not our timer */ - if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) { - return OPERATOR_PASS_THROUGH; - } - - view3d_smoothview_apply(C, v3d, region, true); - - return OPERATOR_FINISHED; -} - -void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region) -{ - RegionView3D *rv3d = region->regiondata; - - if (rv3d && rv3d->sms) { - rv3d->sms->time_allowed = 0.0; /* force finishing */ - view3d_smoothview_apply(C, v3d, region, false); - - /* force update of view matrix so tools that run immediately after - * can use them without redrawing first */ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false); - } -} - -void VIEW3D_OT_smoothview(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Smooth View"; - ot->idname = "VIEW3D_OT_smoothview"; - - /* api callbacks */ - ot->invoke = view3d_smoothview_invoke; - - /* flags */ - ot->flag = OPTYPE_INTERNAL; - - ot->poll = ED_operator_view3d_active; -} - -/** \} */ +#include "view3d_navigate.h" /* -------------------------------------------------------------------- */ /** \name Camera to View Operator @@ -863,10 +485,10 @@ void view3d_opengl_select_cache_end(void) struct DrawSelectLoopUserData { uint pass; uint hits; - uint *buffer; + GPUSelectResult *buffer; uint buffer_len; const rcti *rect; - char gpu_select_mode; + eGPUSelectMode gpu_select_mode; }; static bool drw_select_loop_pass(eDRWSelectStage stage, void *user_data) @@ -927,8 +549,8 @@ static bool drw_select_filter_object_mode_lock_for_weight_paint(Object *ob, void } int view3d_opengl_select_ex(ViewContext *vc, - uint *buffer, - uint bufsize, + GPUSelectResult *buffer, + uint buffer_len, const rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter, @@ -950,7 +572,7 @@ int view3d_opengl_select_ex(ViewContext *vc, const bool use_nearest = (is_pick_select && select_mode == VIEW3D_SELECT_PICK_NEAREST); bool draw_surface = true; - char gpu_select_mode; + eGPUSelectMode gpu_select_mode; /* case not a box select */ if (input->xmin == input->xmax) { @@ -981,6 +603,15 @@ int view3d_opengl_select_ex(ViewContext *vc, } } + /* Re-use cache (rect must be smaller than the cached) + * other context is assumed to be unchanged */ + if (GPU_select_is_cached()) { + GPU_select_begin(buffer, buffer_len, &rect, gpu_select_mode, 0); + GPU_select_cache_load_id(); + hits = GPU_select_end(); + goto finally; + } + /* Important to use 'vc->obact', not 'OBACT(vc->view_layer)' below, * so it will be NULL when hidden. */ struct { @@ -1040,15 +671,6 @@ int view3d_opengl_select_ex(ViewContext *vc, UI_Theme_Store(&theme_state); UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW); - /* Re-use cache (rect must be smaller than the cached) - * other context is assumed to be unchanged */ - if (GPU_select_is_cached()) { - GPU_select_begin(buffer, bufsize, &rect, gpu_select_mode, 0); - GPU_select_cache_load_id(); - hits = GPU_select_end(); - goto finally; - } - /* All of the queries need to be perform on the drawing context. */ DRW_opengl_context_enable(); @@ -1071,7 +693,7 @@ int view3d_opengl_select_ex(ViewContext *vc, .pass = 0, .hits = 0, .buffer = buffer, - .buffer_len = bufsize, + .buffer_len = buffer_len, .rect = &rect, .gpu_select_mode = gpu_select_mode, }; @@ -1101,7 +723,7 @@ int view3d_opengl_select_ex(ViewContext *vc, .pass = 0, .hits = 0, .buffer = buffer, - .buffer_len = bufsize, + .buffer_len = buffer_len, .rect = &rect, .gpu_select_mode = gpu_select_mode, }; @@ -1132,36 +754,36 @@ int view3d_opengl_select_ex(ViewContext *vc, DRW_opengl_context_disable(); + UI_Theme_Restore(&theme_state); + finally: if (hits < 0) { printf("Too many objects in select buffer\n"); /* XXX make error message */ } - UI_Theme_Restore(&theme_state); - return hits; } int view3d_opengl_select(ViewContext *vc, - uint *buffer, - uint bufsize, + GPUSelectResult *buffer, + uint buffer_len, const rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter) { - return view3d_opengl_select_ex(vc, buffer, bufsize, input, select_mode, select_filter, false); + return view3d_opengl_select_ex(vc, buffer, buffer_len, input, select_mode, select_filter, false); } int view3d_opengl_select_with_id_filter(ViewContext *vc, - uint *buffer, - uint bufsize, + GPUSelectResult *buffer, + const uint buffer_len, const rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter, uint select_id) { - int hits = view3d_opengl_select(vc, buffer, bufsize, input, select_mode, select_filter); + int hits = view3d_opengl_select(vc, buffer, buffer_len, input, select_mode, select_filter); /* Selection sometimes uses -1 for an invalid selection ID, remove these as they * interfere with detection of actual number of hits in the selection. */ diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 64a720322c1..09c53d7196c 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -125,9 +125,5 @@ set(LIB bf_gpu ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_transform "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 642de550812..8d91f90ea29 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -580,11 +580,11 @@ typedef struct TransInfo { /** Mouse side of the current frame, 'L', 'R' or 'B' */ char frame_side; - /** copy from G.vd, prevents feedback. */ + /** copy from #RegionView3D, prevents feedback. */ float viewmat[4][4]; /** and to make sure we don't have to. */ float viewinv[4][4]; - /** access G.vd from other space types. */ + /** Access #RegionView3D from other space types. */ float persmat[4][4]; float persinv[4][4]; short persp; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index c40f3c28a79..90f78d4abf1 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -124,10 +124,8 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t); * Sets transform flags in the bones. * Returns total number of bones with #BONE_TRANSFORM. */ -int transform_convert_pose_transflags_update(Object *ob, - int mode, - short around, - bool has_translate_rotate[2]); +void transform_convert_pose_transflags_update(Object *ob, int mode, short around); + /** * When objects array is NULL, use 't->data_container' as is. */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 5d0a3bd9dd1..04a8d462924 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -739,9 +739,43 @@ void createTransPose(TransInfo *t) const bool mirror = ((pose->flag & POSE_MIRROR_EDIT) != 0); - /* set flags and count total */ - tc->data_len = transform_convert_pose_transflags_update( - ob, t->mode, t->around, has_translate_rotate); + /* Set flags. */ + transform_convert_pose_transflags_update(ob, t->mode, t->around); + + /* Now count, and check if we have autoIK or have to switch from translate to rotate. */ + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + Bone *bone = pchan->bone; + if (!(bone->flag & BONE_TRANSFORM)) { + continue; + } + + tc->data_len++; + + if (has_translate_rotate != NULL) { + if (has_translate_rotate[0] && has_translate_rotate[1]) { + continue; + } + + if (has_targetless_ik(pchan) == NULL) { + if (pchan->parent && (bone->flag & BONE_CONNECTED)) { + if (bone->flag & BONE_HINGE_CHILD_TRANSFORM) { + has_translate_rotate[0] = true; + } + } + else { + if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) { + has_translate_rotate[0] = true; + } + } + if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) { + has_translate_rotate[1] = true; + } + } + else { + has_translate_rotate[0] = true; + } + } + } if (tc->data_len == 0) { continue; @@ -1499,15 +1533,11 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb) } } -int transform_convert_pose_transflags_update(Object *ob, - const int mode, - const short around, - bool has_translate_rotate[2]) +void transform_convert_pose_transflags_update(Object *ob, const int mode, const short around) { bArmature *arm = ob->data; bPoseChannel *pchan; Bone *bone; - int total = 0; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; @@ -1537,36 +1567,6 @@ int transform_convert_pose_transflags_update(Object *ob, } } } - /* now count, and check if we have autoIK or have to switch from translate to rotate */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - bone = pchan->bone; - if (bone->flag & BONE_TRANSFORM) { - total++; - - if (has_translate_rotate != NULL) { - if (has_targetless_ik(pchan) == NULL) { - if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) { - if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) { - has_translate_rotate[0] = true; - } - } - else { - if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) { - has_translate_rotate[0] = true; - } - } - if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) { - has_translate_rotate[1] = true; - } - } - else { - has_translate_rotate[0] = true; - } - } - } - } - - return total; } static short apply_targetless_ik(Object *ob) @@ -1733,7 +1733,7 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t) /* Set BONE_TRANSFORM flags for auto-key, gizmo draw might have changed them. */ if (!canceled && (t->mode != TFM_DUMMY)) { - transform_convert_pose_transflags_update(ob, t->mode, t->around, NULL); + transform_convert_pose_transflags_update(ob, t->mode, t->around); } /* if target-less IK grabbing, we calculate the pchan transforms and clear flag */ diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 9bd55d78039..c0572478481 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -953,32 +953,25 @@ int ED_transform_calc_gizmo_stats(const bContext *C, for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob_iter = objects[ob_index]; - const bool use_mat_local = (ob_iter != ob); - bPoseChannel *pchan; - + const bool use_mat_local = params->use_local_axis && (ob_iter != ob); /* mislead counting bones... bah. We don't know the gizmo mode, could be mixed */ const int mode = TFM_ROTATION; - const int totsel_iter = transform_convert_pose_transflags_update( - ob_iter, mode, V3D_AROUND_CENTER_BOUNDS, NULL); + transform_convert_pose_transflags_update(ob_iter, mode, V3D_AROUND_CENTER_BOUNDS); - if (totsel_iter) { - float mat_local[4][4]; - if (params->use_local_axis) { - if (use_mat_local) { - mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat); - } - } + float mat_local[4][4]; + if (use_mat_local) { + mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat); + } - /* use channels to get stats */ - for (pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { - Bone *bone = pchan->bone; - if (bone && (bone->flag & BONE_TRANSFORM)) { - calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local); - protectflag_to_drawflags_pchan(rv3d, pchan, orient_index); - } + /* Use channels to get stats. */ + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + if (!(pchan->bone->flag & BONE_TRANSFORM)) { + continue; } - totsel += totsel_iter; + calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local); + protectflag_to_drawflags_pchan(rv3d, pchan, orient_index); + totsel++; } } MEM_freeN(objects); diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index e96c43e0d02..9dc8b6cf69d 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -153,19 +153,21 @@ static Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool * return NULL; } - BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval); - if ((edit_mode_type == SNAP_GEOM_FINAL) && em_eval->mesh_eval_final) { - if (em_eval->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval); + Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval); + + if ((edit_mode_type == SNAP_GEOM_FINAL) && editmesh_eval_final) { + if (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { return NULL; } - me_eval = em_eval->mesh_eval_final; + me_eval = editmesh_eval_final; use_hide = true; } - else if ((edit_mode_type == SNAP_GEOM_CAGE) && em_eval->mesh_eval_cage) { - if (em_eval->mesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + else if ((edit_mode_type == SNAP_GEOM_CAGE) && editmesh_eval_cage) { + if (editmesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { return NULL; } - me_eval = em_eval->mesh_eval_cage; + me_eval = editmesh_eval_cage; use_hide = true; } } @@ -345,12 +347,14 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob_eval) { - BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval); - if (em_eval->mesh_eval_final) { - return &em_eval->mesh_eval_final->runtime; + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval); + if (editmesh_eval_final) { + return &editmesh_eval_final->runtime; } - if (em_eval->mesh_eval_cage) { - return &em_eval->mesh_eval_cage->runtime; + + Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval); + if (editmesh_eval_cage) { + return &editmesh_eval_cage->runtime; } return &((Mesh *)ob_eval->data)->runtime; diff --git a/source/blender/editors/undo/CMakeLists.txt b/source/blender/editors/undo/CMakeLists.txt index 0f4152c9128..6f659e383fe 100644 --- a/source/blender/editors/undo/CMakeLists.txt +++ b/source/blender/editors/undo/CMakeLists.txt @@ -46,8 +46,4 @@ set(LIB bf_editor_physics ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_undo "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 90a09c87cc6..66cda0fc3f8 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -115,10 +115,6 @@ set(LIB ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) list(APPEND INC diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c index 705dfff7260..b9e90670a4d 100644 --- a/source/blender/editors/util/ed_transverts.c +++ b/source/blender/editors/util/ed_transverts.c @@ -41,6 +41,7 @@ #include "BKE_editmesh.h" #include "BKE_lattice.h" #include "BKE_mesh_iterators.h" +#include "BKE_object.h" #include "DEG_depsgraph.h" @@ -194,12 +195,12 @@ static void set_mapped_co(void *vuserdata, int index, const float co[3], const f } } -bool ED_transverts_check_obedit(Object *obedit) +bool ED_transverts_check_obedit(const Object *obedit) { return (ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL)); } -void ED_transverts_create_from_obedit(TransVertStore *tvs, Object *obedit, const int mode) +void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, const int mode) { Nurb *nu; BezTriple *bezt; @@ -213,7 +214,7 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, Object *obedit, const tvs->transverts_tot = 0; if (obedit->type == OB_MESH) { - BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMEditMesh *em = BKE_editmesh_from_object((Object *)obedit); BMesh *bm = em->bm; BMIter iter; void *userdata[2] = {em, NULL}; @@ -311,9 +312,13 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, Object *obedit, const userdata[1] = tvs->transverts; } - if (tvs->transverts && em->mesh_eval_cage) { - BM_mesh_elem_table_ensure(bm, BM_VERT); - BKE_mesh_foreach_mapped_vert(em->mesh_eval_cage, set_mapped_co, userdata, MESH_FOREACH_NOP); + if (mode & TM_CALC_MAPLOC) { + struct Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(obedit); + if (tvs->transverts && editmesh_eval_cage) { + BM_mesh_elem_table_ensure(bm, BM_VERT); + BKE_mesh_foreach_mapped_vert( + editmesh_eval_cage, set_mapped_co, userdata, MESH_FOREACH_NOP); + } } } else if (obedit->type == OB_ARMATURE) { diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 882f140c063..e86392e47ab 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -35,6 +35,7 @@ #include "BKE_collection.h" #include "BKE_global.h" +#include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_multires.h" @@ -46,6 +47,8 @@ #include "DEG_depsgraph.h" +#include "DNA_gpencil_types.h" + #include "ED_armature.h" #include "ED_asset.h" #include "ED_image.h" @@ -116,6 +119,10 @@ void ED_editors_init(bContext *C) /* For multi-edit mode we may already have mode data (grease pencil does not need it). * However we may have a non-active object stuck in a grease-pencil edit mode. */ if (ob != obact) { + bGPdata *gpd = (bGPdata *)ob->data; + gpd->flag &= ~(GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE | + GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE | + GP_DATA_STROKE_VERTEXMODE); ob->mode = OB_MODE_OBJECT; DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } @@ -434,11 +441,27 @@ void unpack_menu(bContext *C, UI_popup_menu_end(C, pup); } -void ED_spacedata_id_remap(struct ScrArea *area, struct SpaceLink *sl, ID *old_id, ID *new_id) +void ED_spacedata_id_remap(struct ScrArea *area, + struct SpaceLink *sl, + const struct IDRemapper *mappings) +{ + SpaceType *st = BKE_spacetype_from_id(sl->spacetype); + if (st && st->id_remap) { + st->id_remap(area, sl, mappings); + } +} + +void ED_spacedata_id_remap_single(struct ScrArea *area, + struct SpaceLink *sl, + ID *old_id, + ID *new_id) { SpaceType *st = BKE_spacetype_from_id(sl->spacetype); if (st && st->id_remap) { - st->id_remap(area, sl, old_id, new_id); + struct IDRemapper *mappings = BKE_id_remapper_create(); + BKE_id_remapper_add(mappings, old_id, new_id); + st->id_remap(area, sl, mappings); + BKE_id_remapper_free(mappings); } } diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc index a1b17d799bc..ae37dab7bb4 100644 --- a/source/blender/editors/util/ed_util_ops.cc +++ b/source/blender/editors/util/ed_util_ops.cc @@ -121,6 +121,22 @@ static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot) FILE_SORT_DEFAULT); } +static bool lib_id_generate_preview_poll(bContext *C) +{ + if (!lib_id_preview_editing_poll(C)) { + return false; + } + + const PointerRNA idptr = CTX_data_pointer_get(C, "id"); + const ID *id = (ID *)idptr.data; + if (GS(id->name) == ID_NT) { + CTX_wm_operator_poll_msg_set(C, TIP_("Can't generate automatic preview for node group")); + return false; + } + + return true; +} + static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op)) { PointerRNA idptr = CTX_data_pointer_get(C, "id"); @@ -148,13 +164,57 @@ static void ED_OT_lib_id_generate_preview(wmOperatorType *ot) ot->idname = "ED_OT_lib_id_generate_preview"; /* api callbacks */ - ot->poll = lib_id_preview_editing_poll; + ot->poll = lib_id_generate_preview_poll; ot->exec = lib_id_generate_preview_exec; /* flags */ ot->flag = OPTYPE_INTERNAL | OPTYPE_REGISTER | OPTYPE_UNDO; } +static bool lib_id_generate_preview_from_object_poll(bContext *C) +{ + if (!lib_id_preview_editing_poll(C)) { + return false; + } + if (CTX_data_active_object(C) == nullptr) { + return false; + } + return true; +} + +static int lib_id_generate_preview_from_object_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PointerRNA idptr = CTX_data_pointer_get(C, "id"); + ID *id = (ID *)idptr.data; + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + Object *object_to_render = CTX_data_active_object(C); + + BKE_previewimg_id_free(id); + PreviewImage *preview_image = BKE_previewimg_id_ensure(id); + UI_icon_render_id_ex(C, nullptr, &object_to_render->id, ICON_SIZE_PREVIEW, true, preview_image); + + WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); + ED_assetlist_storage_tag_main_data_dirty(); + + return OPERATOR_FINISHED; +} + +static void ED_OT_lib_id_generate_preview_from_object(wmOperatorType *ot) +{ + ot->name = "Generate Preview from Object"; + ot->description = "Create a preview for this asset by rendering the active object"; + ot->idname = "ED_OT_lib_id_generate_preview_from_object"; + + /* api callbacks */ + ot->poll = lib_id_generate_preview_from_object_poll; + ot->exec = lib_id_generate_preview_from_object_exec; + + /* flags */ + ot->flag = OPTYPE_INTERNAL | OPTYPE_REGISTER | OPTYPE_UNDO; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -280,6 +340,7 @@ void ED_operatortypes_edutils() { WM_operatortype_append(ED_OT_lib_id_load_custom_preview); WM_operatortype_append(ED_OT_lib_id_generate_preview); + WM_operatortype_append(ED_OT_lib_id_generate_preview_from_object); WM_operatortype_append(ED_OT_lib_id_fake_user_toggle); WM_operatortype_append(ED_OT_lib_id_unlink); diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt index 1c8a56e0608..a3b29f29354 100644 --- a/source/blender/editors/uvedit/CMakeLists.txt +++ b/source/blender/editors/uvedit/CMakeLists.txt @@ -52,9 +52,5 @@ set(LIB bf_bmesh ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_editor_uvedit "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/freestyle/CMakeLists.txt b/source/blender/freestyle/CMakeLists.txt index d16787714c9..948e68e52da 100644 --- a/source/blender/freestyle/CMakeLists.txt +++ b/source/blender/freestyle/CMakeLists.txt @@ -583,10 +583,6 @@ if(WITH_PYTHON_SAFETY) add_definitions(-DWITH_PYTHON_SAFETY) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WIN32) list(APPEND INC_SYS ${PTHREADS_INC} diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index e869927c33b..e42feac1644 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -87,8 +87,7 @@ class FieldNode { public: FieldNode(FieldNodeType node_type); - - virtual ~FieldNode() = default; + virtual ~FieldNode(); virtual const CPPType &output_cpp_type(int output_index) const = 0; @@ -230,6 +229,7 @@ class FieldOperation : public FieldNode { public: FieldOperation(std::shared_ptr<const MultiFunction> function, Vector<GField> inputs = {}); FieldOperation(const MultiFunction &function, Vector<GField> inputs = {}); + ~FieldOperation(); Span<GField> inputs() const; const MultiFunction &multi_function() const; @@ -259,6 +259,7 @@ class FieldInput : public FieldNode { public: FieldInput(const CPPType &type, std::string debug_name = ""); + ~FieldInput(); /** * Get the value of this specific input based on the given context. The returned virtual array, diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 0bbfbc8cb10..7f1eb8bc1a7 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -31,6 +31,7 @@ MAKE_CPP_TYPE(float3, blender::float3, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(float4x4, blender::float4x4, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(int32, int32_t, CPPTypeFlags::BasicType) +MAKE_CPP_TYPE(int8, int8_t, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(uint32, uint32_t, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(uint8, uint8_t, CPPTypeFlags::BasicType) @@ -44,6 +45,7 @@ MAKE_FIELD_CPP_TYPE(Float2Field, float2); MAKE_FIELD_CPP_TYPE(Float3Field, float3); MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f); MAKE_FIELD_CPP_TYPE(BoolField, bool); +MAKE_FIELD_CPP_TYPE(Int8Field, int8_t); MAKE_FIELD_CPP_TYPE(Int32Field, int32_t); MAKE_FIELD_CPP_TYPE(StringField, std::string); diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index d6b83c42294..fe3041b8602 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -571,6 +571,13 @@ bool IndexFieldInput::is_equal_to(const fn::FieldNode &other) const } /* -------------------------------------------------------------------- + * FieldNode. + */ + +/* Avoid generating the destructor in every translation unit. */ +FieldNode::~FieldNode() = default; + +/* -------------------------------------------------------------------- * FieldOperation. */ @@ -581,6 +588,9 @@ FieldOperation::FieldOperation(std::shared_ptr<const MultiFunction> function, owned_function_ = std::move(function); } +/* Avoid generating the destructor in every translation unit. */ +FieldOperation::~FieldOperation() = default; + /** * Returns the field inputs used by all the provided fields. * This tries to reuse an existing #FieldInputs whenever possible to avoid copying it. @@ -655,6 +665,9 @@ FieldInput::FieldInput(const CPPType &type, std::string debug_name) field_inputs_ = std::move(field_inputs); } +/* Avoid generating the destructor in every translation unit. */ +FieldInput::~FieldInput() = default; + /* -------------------------------------------------------------------- * FieldConstant. */ diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index de508ddc540..f7ddd393b4d 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -30,10 +30,14 @@ set(INC ) set(SRC + intern/mesh_merge_by_distance.cc intern/mesh_to_curve_convert.cc + intern/point_merge_by_distance.cc intern/realize_instances.cc + GEO_mesh_merge_by_distance.hh GEO_mesh_to_curve.hh + GEO_point_merge_by_distance.hh GEO_realize_instances.hh ) diff --git a/source/blender/geometry/GEO_mesh_merge_by_distance.hh b/source/blender/geometry/GEO_mesh_merge_by_distance.hh new file mode 100644 index 00000000000..1d64680a02b --- /dev/null +++ b/source/blender/geometry/GEO_mesh_merge_by_distance.hh @@ -0,0 +1,55 @@ +/* + * 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 <optional> + +#include "BLI_index_mask.hh" +#include "BLI_span.hh" + +struct Mesh; + +/** \file + * \ingroup geo + */ + +namespace blender::geometry { + +/** + * Merge selected vertices into other selected vertices within the \a merge_distance. The merged + * indices favor speed over accuracy, since the results will depend on the order of the vertices. + * + * \returns #std::nullopt if the mesh should not be changed (no vertices are merged), in order to + * avoid copying the input. Otherwise returns the new mesh with merged geometry. + */ +std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh, + IndexMask selection, + float merge_distance); + +/** + * Merge selected vertices along edges to other selected vertices. Only vertices connected by edges + * are considered for merging. + * + * \returns #std::nullopt if the mesh should not be changed (no vertices are merged), in order to + * avoid copying the input. Otherwise returns the new mesh with merged geometry. + */ +std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh, + Span<bool> selection, + float merge_distance, + bool only_loose_edges); + +} // namespace blender::geometry diff --git a/source/blender/geometry/GEO_point_merge_by_distance.hh b/source/blender/geometry/GEO_point_merge_by_distance.hh new file mode 100644 index 00000000000..6766f3c559d --- /dev/null +++ b/source/blender/geometry/GEO_point_merge_by_distance.hh @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_index_mask.hh" + +#pragma once + +struct PointCloud; +class PointCloudComponent; + +/** \file + * \ingroup geo + */ + +namespace blender::geometry { + +/** + * Merge selected points into other selected points within the \a merge_distance. The merged + * indices favor speed over accuracy, since the results will depend on the order of the points. + */ +PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, + const float merge_distance, + const IndexMask selection); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc new file mode 100644 index 00000000000..1a07ebf31f6 --- /dev/null +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -0,0 +1,1729 @@ +/* + * 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_index_mask.hh" +#include "BLI_kdtree.h" +#include "BLI_math_vector.hh" +#include "BLI_vector.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" + +#include "GEO_mesh_merge_by_distance.hh" + +//#define USE_WELD_DEBUG +//#define USE_WELD_NORMALS + +namespace blender::geometry { + +/* Indicates when the element was not computed. */ +#define OUT_OF_CONTEXT (int)(-1) +/* Indicates if the edge or face will be collapsed. */ +#define ELEM_COLLAPSED (int)(-2) +/* indicates whether an edge or vertex in groups_map will be merged. */ +#define ELEM_MERGED (int)(-2) + +/* Used to indicate a range in an array specifying a group. */ +struct WeldGroup { + int len; + int ofs; +}; + +/* Edge groups that will be merged. Final vertices are also indicated. */ +struct WeldGroupEdge { + struct WeldGroup group; + int v1; + int v2; +}; + +struct WeldVert { + /* Indexes relative to the original Mesh. */ + int vert_dest; + int vert_orig; +}; + +struct WeldEdge { + union { + int flag; + struct { + /* Indexes relative to the original Mesh. */ + int edge_dest; + int edge_orig; + int vert_a; + int vert_b; + }; + }; +}; + +struct WeldLoop { + union { + int flag; + struct { + /* Indexes relative to the original Mesh. */ + int vert; + int edge; + int loop_orig; + int loop_skip_to; + }; + }; +}; + +struct WeldPoly { + union { + int flag; + struct { + /* Indexes relative to the original Mesh. */ + int poly_dst; + int poly_orig; + int loop_start; + int loop_end; + /* Final Polygon Size. */ + int len; + /* Group of loops that will be affected. */ + struct WeldGroup loops; + }; + }; +}; + +struct WeldMesh { + /* Group of vertices to be merged. */ + Array<WeldGroup> vert_groups; + Array<int> vert_groups_buffer; + + /* Group of edges to be merged. */ + Array<WeldGroupEdge> edge_groups; + Array<int> edge_groups_buffer; + /* From the original index of the vertex, this indicates which group it is or is going to be + * merged. */ + Array<int> edge_groups_map; + + /* References all polygons and loops that will be affected. */ + Vector<WeldLoop> wloop; + Vector<WeldPoly> wpoly; + WeldPoly *wpoly_new; + int wloop_len; + int wpoly_len; + int wpoly_new_len; + + /* From the actual index of the element in the mesh, it indicates what is the index of the Weld + * element above. */ + Array<int> loop_map; + Array<int> poly_map; + + int vert_kill_len; + int edge_kill_len; + int loop_kill_len; + int poly_kill_len; /* Including the new polygons. */ + + /* Size of the affected polygon with more sides. */ + int max_poly_len; +}; + +struct WeldLoopOfPolyIter { + int loop_start; + int loop_end; + Span<WeldLoop> wloop; + Span<MLoop> mloop; + Span<int> loop_map; + /* Weld group. */ + int *group; + + int l_curr; + int l_next; + + /* Return */ + int group_len; + int v; + int e; + char type; +}; + +/* -------------------------------------------------------------------- */ +/** \name Debug Utils + * \{ */ + +#ifdef USE_WELD_DEBUG +static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, + const WeldPoly &wp, + Span<WeldLoop> wloop, + Span<MLoop> mloop, + Span<int> loop_map, + int *group_buffer); + +static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter); + +static void weld_assert_edge_kill_len(Span<WeldEdge> wedge, const int supposed_kill_len) +{ + int kills = 0; + const WeldEdge *we = &wedge[0]; + for (int i = wedge.size(); i--; we++) { + int edge_dest = we->edge_dest; + /* Magically includes collapsed edges. */ + if (edge_dest != OUT_OF_CONTEXT) { + kills++; + } + } + BLI_assert(kills == supposed_kill_len); +} + +static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly, + Span<WeldPoly> wpoly_new, + Span<WeldLoop> wloop, + Span<MLoop> mloop, + Span<int> loop_map, + Span<int> poly_map, + Span<MPoly> mpoly, + const int supposed_poly_kill_len, + const int supposed_loop_kill_len) +{ + int poly_kills = 0; + int loop_kills = mloop.size(); + const MPoly *mp = &mpoly[0]; + for (int i = 0; i < mpoly.size(); i++, mp++) { + int poly_ctx = poly_map[i]; + if (poly_ctx != OUT_OF_CONTEXT) { + const WeldPoly *wp = &wpoly[poly_ctx]; + WeldLoopOfPolyIter iter; + if (!weld_iter_loop_of_poly_begin(&iter, *wp, wloop, mloop, loop_map, nullptr)) { + poly_kills++; + continue; + } + else { + if (wp->poly_dst != OUT_OF_CONTEXT) { + poly_kills++; + continue; + } + int remain = wp->len; + int l = wp->loop_start; + while (remain) { + int l_next = l + 1; + int loop_ctx = loop_map[l]; + if (loop_ctx != OUT_OF_CONTEXT) { + const WeldLoop *wl = &wloop[loop_ctx]; + if (wl->loop_skip_to != OUT_OF_CONTEXT) { + l_next = wl->loop_skip_to; + } + if (wl->flag != ELEM_COLLAPSED) { + loop_kills--; + remain--; + } + } + else { + loop_kills--; + remain--; + } + l = l_next; + } + } + } + else { + loop_kills -= mp->totloop; + } + } + + const WeldPoly *wp = wpoly_new.data(); + for (int i = wpoly_new.size(); i--; wp++) { + if (wp->poly_dst != OUT_OF_CONTEXT) { + poly_kills++; + continue; + } + int remain = wp->len; + int l = wp->loop_start; + while (remain) { + int l_next = l + 1; + int loop_ctx = loop_map[l]; + if (loop_ctx != OUT_OF_CONTEXT) { + const WeldLoop *wl = &wloop[loop_ctx]; + if (wl->loop_skip_to != OUT_OF_CONTEXT) { + l_next = wl->loop_skip_to; + } + if (wl->flag != ELEM_COLLAPSED) { + loop_kills--; + remain--; + } + } + else { + loop_kills--; + remain--; + } + l = l_next; + } + } + + BLI_assert(poly_kills == supposed_poly_kill_len); + BLI_assert(loop_kills == supposed_loop_kill_len); +} + +static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp, + Span<WeldLoop> wloop, + Span<MLoop> mloop, + Span<int> loop_map) +{ + const int len = wp.len; + Array<int, 64> verts(len); + WeldLoopOfPolyIter iter; + if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) { + return; + } + else { + int i = 0; + while (weld_iter_loop_of_poly_next(iter)) { + verts[i++] = iter.v; + } + } + for (int i = 0; i < len; i++) { + int va = verts[i]; + for (int j = i + 1; j < len; j++) { + int vb = verts[j]; + BLI_assert(va != vb); + } + } +} + +static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop) +{ + if (wp->flag == ELEM_COLLAPSED) { + return; + } + + int len = wp->len; + const WeldLoop *wl = &wloop[wp->loops.ofs]; + BLI_assert(wp->loop_start <= wl->loop_orig); + + int end_wloop = wp->loops.ofs + wp->loops.len; + const WeldLoop *wl_end = &wloop[end_wloop - 1]; + + int min_len = 0; + for (; wl <= wl_end; wl++) { + BLI_assert(wl->loop_skip_to == OUT_OF_CONTEXT); /* Not for this case. */ + if (wl->flag != ELEM_COLLAPSED) { + min_len++; + } + } + BLI_assert(len >= min_len); + + int max_len = wp->loop_end - wp->loop_start + 1; + BLI_assert(len <= max_len); +} + +#endif /* USE_WELD_DEBUG */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vert API + * \{ */ + +static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map, + const int vert_kill_len) +{ + Vector<WeldVert> wvert; + wvert.reserve(std::min<int>(2 * vert_kill_len, vert_dest_map.size())); + + for (const int i : vert_dest_map.index_range()) { + if (vert_dest_map[i] != OUT_OF_CONTEXT) { + WeldVert wv{}; + wv.vert_dest = vert_dest_map[i]; + wv.vert_orig = i; + wvert.append(wv); + } + } + return wvert; +} + +static void weld_vert_groups_setup(Span<WeldVert> wvert, + Span<int> vert_dest_map, + MutableSpan<int> r_vert_groups_map, + Array<int> &r_vert_groups_buffer, + Array<WeldGroup> &r_vert_groups) +{ + /* Get weld vert groups. */ + + int wgroups_len = 0; + for (const int i : vert_dest_map.index_range()) { + const int vert_dest = vert_dest_map[i]; + if (vert_dest != OUT_OF_CONTEXT) { + if (vert_dest != i) { + r_vert_groups_map[i] = ELEM_MERGED; + } + else { + r_vert_groups_map[i] = wgroups_len; + wgroups_len++; + } + } + else { + r_vert_groups_map[i] = OUT_OF_CONTEXT; + } + } + + r_vert_groups.reinitialize(wgroups_len); + r_vert_groups.fill({0, 0}); + MutableSpan<WeldGroup> wgroups = r_vert_groups; + + for (const WeldVert &wv : wvert) { + int group_index = r_vert_groups_map[wv.vert_dest]; + wgroups[group_index].len++; + } + + int ofs = 0; + for (WeldGroup &wg : wgroups) { + wg.ofs = ofs; + ofs += wg.len; + } + + BLI_assert(ofs == wvert.size()); + + r_vert_groups_buffer.reinitialize(ofs); + for (const WeldVert &wv : wvert) { + int group_index = r_vert_groups_map[wv.vert_dest]; + r_vert_groups_buffer[wgroups[group_index].ofs++] = wv.vert_orig; + } + + for (WeldGroup &wg : wgroups) { + wg.ofs -= wg.len; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edge API + * \{ */ + +static Vector<WeldEdge> weld_edge_ctx_alloc(Span<MEdge> medge, + Span<int> vert_dest_map, + MutableSpan<int> r_edge_dest_map, + MutableSpan<int> r_edge_ctx_map) +{ + /* Edge Context. */ + int wedge_len = 0; + + Vector<WeldEdge> wedge; + wedge.reserve(medge.size()); + + for (const int i : medge.index_range()) { + int v1 = medge[i].v1; + int v2 = medge[i].v2; + int v_dest_1 = vert_dest_map[v1]; + int v_dest_2 = vert_dest_map[v2]; + if ((v_dest_1 != OUT_OF_CONTEXT) || (v_dest_2 != OUT_OF_CONTEXT)) { + WeldEdge we{}; + we.vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1; + we.vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2; + we.edge_dest = OUT_OF_CONTEXT; + we.edge_orig = i; + wedge.append(we); + r_edge_dest_map[i] = i; + r_edge_ctx_map[i] = wedge_len++; + } + else { + r_edge_dest_map[i] = OUT_OF_CONTEXT; + r_edge_ctx_map[i] = OUT_OF_CONTEXT; + } + } + + return wedge; +} + +static void weld_edge_ctx_setup(MutableSpan<WeldGroup> r_vlinks, + MutableSpan<int> r_edge_dest_map, + MutableSpan<WeldEdge> r_wedge, + int *r_edge_kiil_len) +{ + /* Setup Edge Overlap. */ + int edge_kill_len = 0; + + MutableSpan<WeldGroup> v_links = r_vlinks; + + for (WeldEdge &we : r_wedge) { + int dst_vert_a = we.vert_a; + int dst_vert_b = we.vert_b; + + if (dst_vert_a == dst_vert_b) { + BLI_assert(we.edge_dest == OUT_OF_CONTEXT); + r_edge_dest_map[we.edge_orig] = ELEM_COLLAPSED; + we.flag = ELEM_COLLAPSED; + edge_kill_len++; + continue; + } + + v_links[dst_vert_a].len++; + v_links[dst_vert_b].len++; + } + + int link_len = 0; + for (WeldGroup &vl : r_vlinks) { + vl.ofs = link_len; + link_len += vl.len; + } + + if (link_len > 0) { + Array<int> link_edge_buffer(link_len); + + for (const int i : r_wedge.index_range()) { + const WeldEdge &we = r_wedge[i]; + if (we.flag == ELEM_COLLAPSED) { + continue; + } + + int dst_vert_a = we.vert_a; + int dst_vert_b = we.vert_b; + + link_edge_buffer[v_links[dst_vert_a].ofs++] = i; + link_edge_buffer[v_links[dst_vert_b].ofs++] = i; + } + + for (WeldGroup &vl : r_vlinks) { + /* Fix offset */ + vl.ofs -= vl.len; + } + + for (const int i : r_wedge.index_range()) { + const WeldEdge &we = r_wedge[i]; + if (we.edge_dest != OUT_OF_CONTEXT) { + /* No need to retest edges. + * (Already includes collapsed edges). */ + continue; + } + + int dst_vert_a = we.vert_a; + int dst_vert_b = we.vert_b; + + struct WeldGroup *link_a = &v_links[dst_vert_a]; + struct WeldGroup *link_b = &v_links[dst_vert_b]; + + int edges_len_a = link_a->len; + int edges_len_b = link_b->len; + + if (edges_len_a <= 1 || edges_len_b <= 1) { + continue; + } + + int *edges_ctx_a = &link_edge_buffer[link_a->ofs]; + int *edges_ctx_b = &link_edge_buffer[link_b->ofs]; + int edge_orig = we.edge_orig; + + for (; edges_len_a--; edges_ctx_a++) { + int e_ctx_a = *edges_ctx_a; + if (e_ctx_a == i) { + continue; + } + while (edges_len_b && *edges_ctx_b < e_ctx_a) { + edges_ctx_b++; + edges_len_b--; + } + if (edges_len_b == 0) { + break; + } + int e_ctx_b = *edges_ctx_b; + if (e_ctx_a == e_ctx_b) { + WeldEdge *we_b = &r_wedge[e_ctx_b]; + BLI_assert(ELEM(we_b->vert_a, dst_vert_a, dst_vert_b)); + BLI_assert(ELEM(we_b->vert_b, dst_vert_a, dst_vert_b)); + BLI_assert(we_b->edge_dest == OUT_OF_CONTEXT); + BLI_assert(we_b->edge_orig != edge_orig); + r_edge_dest_map[we_b->edge_orig] = edge_orig; + we_b->edge_dest = edge_orig; + edge_kill_len++; + } + } + } + +#ifdef USE_WELD_DEBUG + weld_assert_edge_kill_len(r_wedge, edge_kill_len); +#endif + } + + *r_edge_kiil_len = edge_kill_len; +} + +static void weld_edge_groups_setup(const int medge_len, + const int edge_kill_len, + MutableSpan<WeldEdge> wedge, + Span<int> wedge_map, + MutableSpan<int> r_edge_groups_map, + Array<int> &r_edge_groups_buffer, + Array<WeldGroupEdge> &r_edge_groups) +{ + /* Get weld edge groups. */ + int wgroups_len = wedge.size() - edge_kill_len; + r_edge_groups.reinitialize(wgroups_len); + r_edge_groups.fill({{0}}); + MutableSpan<WeldGroupEdge> wegroups = r_edge_groups; + + wgroups_len = 0; + for (const int i : IndexRange(medge_len)) { + int edge_ctx = wedge_map[i]; + if (edge_ctx != OUT_OF_CONTEXT) { + WeldEdge *we = &wedge[edge_ctx]; + int edge_dest = we->edge_dest; + if (edge_dest != OUT_OF_CONTEXT) { + BLI_assert(edge_dest != we->edge_orig); + r_edge_groups_map[i] = ELEM_MERGED; + } + else { + we->edge_dest = we->edge_orig; + wegroups[wgroups_len].v1 = we->vert_a; + wegroups[wgroups_len].v2 = we->vert_b; + r_edge_groups_map[i] = wgroups_len; + wgroups_len++; + } + } + else { + r_edge_groups_map[i] = OUT_OF_CONTEXT; + } + } + + BLI_assert(wgroups_len == wedge.size() - edge_kill_len); + + if (wgroups_len == 0) { + /* All edges in the context are collapsed. */ + return; + } + + for (const WeldEdge &we : wedge) { + if (we.flag == ELEM_COLLAPSED) { + continue; + } + int group_index = r_edge_groups_map[we.edge_dest]; + wegroups[group_index].group.len++; + } + + int ofs = 0; + for (WeldGroupEdge &wegrp : wegroups) { + wegrp.group.ofs = ofs; + ofs += wegrp.group.len; + } + + r_edge_groups_buffer.reinitialize(ofs); + for (const WeldEdge &we : wedge) { + if (we.flag == ELEM_COLLAPSED) { + continue; + } + int group_index = r_edge_groups_map[we.edge_dest]; + r_edge_groups_buffer[wegroups[group_index].group.ofs++] = we.edge_orig; + } + + for (WeldGroupEdge &wegrp : wegroups) { + wegrp.group.ofs -= wegrp.group.len; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Poly and Loop API + * \{ */ + +static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter &iter, + const WeldPoly &wp, + Span<WeldLoop> wloop, + Span<MLoop> mloop, + Span<int> loop_map, + int *group_buffer) +{ + if (wp.flag == ELEM_COLLAPSED) { + return false; + } + + iter.loop_start = wp.loop_start; + iter.loop_end = wp.loop_end; + iter.wloop = wloop; + iter.mloop = mloop; + iter.loop_map = loop_map; + iter.group = group_buffer; + + int group_len = 0; + if (group_buffer) { + /* First loop group needs more attention. */ + int loop_start, loop_end, l; + loop_start = iter.loop_start; + loop_end = l = iter.loop_end; + while (l >= loop_start) { + const int loop_ctx = loop_map[l]; + if (loop_ctx != OUT_OF_CONTEXT) { + const WeldLoop *wl = &wloop[loop_ctx]; + if (wl->flag == ELEM_COLLAPSED) { + l--; + continue; + } + } + break; + } + if (l != loop_end) { + group_len = loop_end - l; + int i = 0; + while (l < loop_end) { + iter.group[i++] = ++l; + } + } + } + iter.group_len = group_len; + + iter.l_next = iter.loop_start; +#ifdef USE_WELD_DEBUG + iter.v = OUT_OF_CONTEXT; +#endif + return true; +} + +static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter) +{ + const int loop_end = iter.loop_end; + Span<WeldLoop> wloop = iter.wloop; + Span<int> loop_map = iter.loop_map; + int l = iter.l_curr = iter.l_next; + if (l == iter.loop_start) { + /* `grupo_len` is already calculated in the first loop */ + } + else { + iter.group_len = 0; + } + while (l <= loop_end) { + int l_next = l + 1; + const int loop_ctx = loop_map[l]; + if (loop_ctx != OUT_OF_CONTEXT) { + const WeldLoop *wl = &wloop[loop_ctx]; + if (wl->loop_skip_to != OUT_OF_CONTEXT) { + l_next = wl->loop_skip_to; + } + if (wl->flag == ELEM_COLLAPSED) { + if (iter.group) { + iter.group[iter.group_len++] = l; + } + l = l_next; + continue; + } +#ifdef USE_WELD_DEBUG + BLI_assert(iter.v != wl->vert); +#endif + iter.v = wl->vert; + iter.e = wl->edge; + iter.type = 1; + } + else { + const MLoop &ml = iter.mloop[l]; +#ifdef USE_WELD_DEBUG + BLI_assert((uint)iter.v != ml.v); +#endif + iter.v = ml.v; + iter.e = ml.e; + iter.type = 0; + } + if (iter.group) { + iter.group[iter.group_len++] = l; + } + iter.l_next = l_next; + return true; + } + + return false; +} + +static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly, + Span<MLoop> mloop, + Span<int> vert_dest_map, + Span<int> edge_dest_map, + WeldMesh *r_weld_mesh) +{ + /* Loop/Poly Context. */ + Array<int> loop_map(mloop.size()); + Array<int> poly_map(mpoly.size()); + int wloop_len = 0; + int wpoly_len = 0; + int max_ctx_poly_len = 4; + + Vector<WeldLoop> wloop; + wloop.reserve(mloop.size()); + + Vector<WeldPoly> wpoly; + wpoly.reserve(mpoly.size()); + + int maybe_new_poly = 0; + + for (const int i : mpoly.index_range()) { + const MPoly &mp = mpoly[i]; + const int loopstart = mp.loopstart; + const int totloop = mp.totloop; + + int vert_ctx_len = 0; + + int prev_wloop_len = wloop_len; + for (const int i_loop : mloop.index_range().slice(loopstart, totloop)) { + int v = mloop[i_loop].v; + int e = mloop[i_loop].e; + int v_dest = vert_dest_map[v]; + int e_dest = edge_dest_map[e]; + bool is_vert_ctx = v_dest != OUT_OF_CONTEXT; + bool is_edge_ctx = e_dest != OUT_OF_CONTEXT; + if (is_vert_ctx) { + vert_ctx_len++; + } + if (is_vert_ctx || is_edge_ctx) { + WeldLoop wl{}; + wl.vert = is_vert_ctx ? v_dest : v; + wl.edge = is_edge_ctx ? e_dest : e; + wl.loop_orig = i_loop; + wl.loop_skip_to = OUT_OF_CONTEXT; + wloop.append(wl); + + loop_map[i_loop] = wloop_len++; + } + else { + loop_map[i_loop] = OUT_OF_CONTEXT; + } + } + if (wloop_len != prev_wloop_len) { + int loops_len = wloop_len - prev_wloop_len; + WeldPoly wp{}; + wp.poly_dst = OUT_OF_CONTEXT; + wp.poly_orig = i; + wp.loops.len = loops_len; + wp.loops.ofs = prev_wloop_len; + wp.loop_start = loopstart; + wp.loop_end = loopstart + totloop - 1; + wp.len = totloop; + wpoly.append(wp); + + poly_map[i] = wpoly_len++; + if (totloop > 5 && vert_ctx_len > 1) { + int max_new = (totloop / 3) - 1; + vert_ctx_len /= 2; + maybe_new_poly += MIN2(max_new, vert_ctx_len); + CLAMP_MIN(max_ctx_poly_len, totloop); + } + } + else { + poly_map[i] = OUT_OF_CONTEXT; + } + } + + if (mpoly.size() < (wpoly_len + maybe_new_poly)) { + wpoly.resize(wpoly_len + maybe_new_poly); + } + + WeldPoly *poly_new = wpoly.data() + wpoly_len; + + r_weld_mesh->wloop = std::move(wloop); + r_weld_mesh->wpoly = std::move(wpoly); + r_weld_mesh->wpoly_new = poly_new; + r_weld_mesh->wloop_len = wloop_len; + r_weld_mesh->wpoly_len = wpoly_len; + r_weld_mesh->wpoly_new_len = 0; + r_weld_mesh->loop_map = std::move(loop_map); + r_weld_mesh->poly_map = std::move(poly_map); + r_weld_mesh->max_poly_len = max_ctx_poly_len; +} + +static void weld_poly_split_recursive(Span<int> vert_dest_map, +#ifdef USE_WELD_DEBUG + const Span<MLoop> mloop, +#endif + int ctx_verts_len, + WeldPoly *r_wp, + WeldMesh *r_weld_mesh, + int *r_poly_kill, + int *r_loop_kill) +{ + int poly_len = r_wp->len; + if (poly_len > 3 && ctx_verts_len > 1) { + const int ctx_loops_len = r_wp->loops.len; + const int ctx_loops_ofs = r_wp->loops.ofs; + MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; + WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; + + int loop_kill = 0; + + WeldLoop *poly_loops = &wloop[ctx_loops_ofs]; + WeldLoop *wla = &poly_loops[0]; + WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1]; + while (wla_prev->flag == ELEM_COLLAPSED) { + wla_prev--; + } + const int la_len = ctx_loops_len - 1; + for (int la = 0; la < la_len; la++, wla++) { + wa_continue: + if (wla->flag == ELEM_COLLAPSED) { + continue; + } + int vert_a = wla->vert; + /* Only test vertices that will be merged. */ + if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) { + int lb = la + 1; + WeldLoop *wlb = wla + 1; + WeldLoop *wlb_prev = wla; + int killed_ab = 0; + ctx_verts_len = 1; + for (; lb < ctx_loops_len; lb++, wlb++) { + BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT); + if (wlb->flag == ELEM_COLLAPSED) { + killed_ab++; + continue; + } + int vert_b = wlb->vert; + if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) { + ctx_verts_len++; + } + if (vert_a == vert_b) { + const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; + const int dist_b = poly_len - dist_a; + + BLI_assert(dist_a != 0 && dist_b != 0); + if (dist_a == 1 || dist_b == 1) { + BLI_assert(dist_a != dist_b); + BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED)); + } + else { + WeldLoop *wl_tmp = nullptr; + if (dist_a == 2) { + wl_tmp = wlb_prev; + BLI_assert(wla->flag != ELEM_COLLAPSED); + BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); + wla->flag = ELEM_COLLAPSED; + wl_tmp->flag = ELEM_COLLAPSED; + loop_kill += 2; + poly_len -= 2; + } + if (dist_b == 2) { + if (wl_tmp != nullptr) { + r_wp->flag = ELEM_COLLAPSED; + *r_poly_kill += 1; + } + else { + wl_tmp = wla_prev; + BLI_assert(wlb->flag != ELEM_COLLAPSED); + BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); + wlb->flag = ELEM_COLLAPSED; + wl_tmp->flag = ELEM_COLLAPSED; + } + loop_kill += 2; + poly_len -= 2; + } + if (wl_tmp == nullptr) { + const int new_loops_len = lb - la; + const int new_loops_ofs = ctx_loops_ofs + la; + + WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++]; + new_wp->poly_dst = OUT_OF_CONTEXT; + new_wp->poly_orig = r_wp->poly_orig; + new_wp->loops.len = new_loops_len; + new_wp->loops.ofs = new_loops_ofs; + new_wp->loop_start = wla->loop_orig; + new_wp->loop_end = wlb_prev->loop_orig; + new_wp->len = dist_a; + weld_poly_split_recursive(vert_dest_map, +#ifdef USE_WELD_DEBUG + mloop, +#endif + ctx_verts_len, + new_wp, + r_weld_mesh, + r_poly_kill, + r_loop_kill); + BLI_assert(dist_b == poly_len - dist_a); + poly_len = dist_b; + if (wla_prev->loop_orig > wla->loop_orig) { + /* New start. */ + r_wp->loop_start = wlb->loop_orig; + } + else { + /* The `loop_start` doesn't change but some loops must be skipped. */ + wla_prev->loop_skip_to = wlb->loop_orig; + } + wla = wlb; + la = lb; + goto wa_continue; + } + break; + } + } + if (wlb->flag != ELEM_COLLAPSED) { + wlb_prev = wlb; + } + } + } + if (wla->flag != ELEM_COLLAPSED) { + wla_prev = wla; + } + } + r_wp->len = poly_len; + *r_loop_kill += loop_kill; + +#ifdef USE_WELD_DEBUG + weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map); +#endif + } +} + +static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, +#ifdef USE_WELD_DEBUG + Span<MPoly> mpoly, +#endif + const int mvert_len, + Span<int> vert_dest_map, + const int remain_edge_ctx_len, + MutableSpan<WeldGroup> r_vlinks, + WeldMesh *r_weld_mesh) +{ + MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly; + MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; + WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; + int wpoly_len = r_weld_mesh->wpoly_len; + int wpoly_new_len = 0; + int poly_kill_len = 0; + int loop_kill_len = 0; + + Span<int> loop_map = r_weld_mesh->loop_map; + + if (remain_edge_ctx_len) { + + /* Setup Poly/Loop. Note that `wpoly_len` may be different than `wpoly.size()` here. */ + for (const int i : IndexRange(wpoly_len)) { + WeldPoly &wp = wpoly[i]; + const int ctx_loops_len = wp.loops.len; + const int ctx_loops_ofs = wp.loops.ofs; + + int poly_len = wp.len; + int ctx_verts_len = 0; + WeldLoop *wl = &wloop[ctx_loops_ofs]; + for (int l = ctx_loops_len; l--; wl++) { + const int edge_dest = wl->edge; + if (edge_dest == ELEM_COLLAPSED) { + wl->flag = ELEM_COLLAPSED; + if (poly_len == 3) { + wp.flag = ELEM_COLLAPSED; + poly_kill_len++; + loop_kill_len += 3; + poly_len = 0; + break; + } + loop_kill_len++; + poly_len--; + } + else { + const int vert_dst = wl->vert; + if (vert_dest_map[vert_dst] != OUT_OF_CONTEXT) { + ctx_verts_len++; + } + } + } + + if (poly_len) { + wp.len = poly_len; +#ifdef USE_WELD_DEBUG + weld_assert_poly_len(wp, wloop); +#endif + + weld_poly_split_recursive(vert_dest_map, +#ifdef USE_WELD_DEBUG + mloop, +#endif + ctx_verts_len, + &wp, + r_weld_mesh, + &poly_kill_len, + &loop_kill_len); + + wpoly_new_len = r_weld_mesh->wpoly_new_len; + } + } + +#ifdef USE_WELD_DEBUG + weld_assert_poly_and_loop_kill_len(wpoly, + {wpoly_new, wpoly_new_len}, + wloop, + mloop, + loop_map, + r_weld_mesh->poly_map, + mpoly, + poly_kill_len, + loop_kill_len); +#endif + + /* Setup Polygon Overlap. */ + + const int wpoly_and_new_len = wpoly_len + wpoly_new_len; + + r_vlinks.fill({0, 0}); + MutableSpan<WeldGroup> v_links = r_vlinks; + + for (const int i : IndexRange(wpoly_and_new_len)) { + const WeldPoly &wp = wpoly[i]; + WeldLoopOfPolyIter iter; + if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { + while (weld_iter_loop_of_poly_next(iter)) { + v_links[iter.v].len++; + } + } + } + + int link_len = 0; + for (const int i : IndexRange(mvert_len)) { + v_links[i].ofs = link_len; + link_len += v_links[i].len; + } + + if (link_len) { + Array<int> link_poly_buffer(link_len); + + for (const int i : IndexRange(wpoly_and_new_len)) { + const WeldPoly &wp = wpoly[i]; + WeldLoopOfPolyIter iter; + if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { + while (weld_iter_loop_of_poly_next(iter)) { + link_poly_buffer[v_links[iter.v].ofs++] = i; + } + } + } + + for (WeldGroup &vl : r_vlinks) { + /* Fix offset */ + vl.ofs -= vl.len; + } + + int polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b; + polys_len_b = p_ctx_b = 0; /* silence warnings */ + + for (const int i : IndexRange(wpoly_and_new_len)) { + const WeldPoly &wp = wpoly[i]; + if (wp.poly_dst != OUT_OF_CONTEXT) { + /* No need to retest poly. + * (Already includes collapsed polygons). */ + continue; + } + + WeldLoopOfPolyIter iter; + weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr); + weld_iter_loop_of_poly_next(iter); + struct WeldGroup *link_a = &v_links[iter.v]; + polys_len_a = link_a->len; + if (polys_len_a == 1) { + BLI_assert(link_poly_buffer[link_a->ofs] == i); + continue; + } + int wp_len = wp.len; + polys_ctx_a = &link_poly_buffer[link_a->ofs]; + for (; polys_len_a--; polys_ctx_a++) { + p_ctx_a = *polys_ctx_a; + if (p_ctx_a == i) { + continue; + } + + WeldPoly *wp_tmp = &wpoly[p_ctx_a]; + if (wp_tmp->len != wp_len) { + continue; + } + + WeldLoopOfPolyIter iter_b = iter; + while (weld_iter_loop_of_poly_next(iter_b)) { + struct WeldGroup *link_b = &v_links[iter_b.v]; + polys_len_b = link_b->len; + if (polys_len_b == 1) { + BLI_assert(link_poly_buffer[link_b->ofs] == i); + polys_len_b = 0; + break; + } + + polys_ctx_b = &link_poly_buffer[link_b->ofs]; + for (; polys_len_b; polys_len_b--, polys_ctx_b++) { + p_ctx_b = *polys_ctx_b; + if (p_ctx_b < p_ctx_a) { + continue; + } + if (p_ctx_b >= p_ctx_a) { + if (p_ctx_b > p_ctx_a) { + polys_len_b = 0; + } + break; + } + } + if (polys_len_b == 0) { + break; + } + } + if (polys_len_b == 0) { + continue; + } + BLI_assert(p_ctx_a > i); + BLI_assert(p_ctx_a == p_ctx_b); + BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT); + BLI_assert(wp_tmp != &wp); + wp_tmp->poly_dst = wp.poly_orig; + loop_kill_len += wp_tmp->len; + poly_kill_len++; + } + } + } + } + else { + poly_kill_len = r_weld_mesh->wpoly_len; + loop_kill_len = r_weld_mesh->wloop_len; + + for (WeldPoly &wp : wpoly) { + wp.flag = ELEM_COLLAPSED; + } + } + +#ifdef USE_WELD_DEBUG + weld_assert_poly_and_loop_kill_len(wpoly, + {wpoly_new, wpoly_new_len}, + wloop, + mloop, + loop_map, + r_weld_mesh->poly_map, + mpoly, + poly_kill_len, + loop_kill_len); +#endif + + r_weld_mesh->wpoly_new = wpoly_new; + r_weld_mesh->poly_kill_len = poly_kill_len; + r_weld_mesh->loop_kill_len = loop_kill_len; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh API + * \{ */ + +static void weld_mesh_context_create(const Mesh &mesh, + MutableSpan<int> vert_dest_map, + const int vert_kill_len, + WeldMesh *r_weld_mesh) +{ + Span<MEdge> medge{mesh.medge, mesh.totedge}; + Span<MPoly> mpoly{mesh.mpoly, mesh.totpoly}; + Span<MLoop> mloop{mesh.mloop, mesh.totloop}; + const int mvert_len = mesh.totvert; + + Vector<WeldVert> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map, vert_kill_len); + r_weld_mesh->vert_kill_len = vert_kill_len; + + Array<int> edge_dest_map(medge.size()); + Array<int> edge_ctx_map(medge.size()); + Vector<WeldEdge> wedge = weld_edge_ctx_alloc(medge, vert_dest_map, edge_dest_map, edge_ctx_map); + + Array<WeldGroup> v_links(mvert_len, {0, 0}); + weld_edge_ctx_setup(v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len); + + weld_poly_loop_ctx_alloc(mpoly, mloop, vert_dest_map, edge_dest_map, r_weld_mesh); + + weld_poly_loop_ctx_setup(mloop, +#ifdef USE_WELD_DEBUG + mpoly, + +#endif + mvert_len, + vert_dest_map, + wedge.size() - r_weld_mesh->edge_kill_len, + v_links, + r_weld_mesh); + + weld_vert_groups_setup(wvert, + vert_dest_map, + vert_dest_map, + r_weld_mesh->vert_groups_buffer, + r_weld_mesh->vert_groups); + + weld_edge_groups_setup(medge.size(), + r_weld_mesh->edge_kill_len, + wedge, + edge_ctx_map, + edge_dest_map, + r_weld_mesh->edge_groups_buffer, + r_weld_mesh->edge_groups); + + r_weld_mesh->edge_groups_map = std::move(edge_dest_map); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData + * \{ */ + +static void customdata_weld( + const CustomData *source, CustomData *dest, const int *src_indices, int count, int dest_index) +{ + if (count == 1) { + CustomData_copy_data(source, dest, src_indices[0], dest_index, 1); + return; + } + + CustomData_interp(source, dest, (const int *)src_indices, nullptr, nullptr, count, dest_index); + + int src_i, dest_i; + int j; + + float co[3] = {0.0f, 0.0f, 0.0f}; +#ifdef USE_WELD_NORMALS + float no[3] = {0.0f, 0.0f, 0.0f}; +#endif + int crease = 0; + int bweight = 0; + short flag = 0; + + /* interpolates a layer at a time */ + dest_i = 0; + for (src_i = 0; src_i < source->totlayer; src_i++) { + const int type = source->layers[src_i].type; + + /* find the first dest layer with type >= the source type + * (this should work because layers are ordered by type) + */ + while (dest_i < dest->totlayer && dest->layers[dest_i].type < type) { + dest_i++; + } + + /* if there are no more dest layers, we're done */ + if (dest_i == dest->totlayer) { + break; + } + + /* if we found a matching layer, add the data */ + if (dest->layers[dest_i].type == type) { + void *src_data = source->layers[src_i].data; + + if (type == CD_MVERT) { + for (j = 0; j < count; j++) { + MVert *mv_src = &((MVert *)src_data)[src_indices[j]]; + add_v3_v3(co, mv_src->co); +#ifdef USE_WELD_NORMALS + short *mv_src_no = mv_src->no; + no[0] += mv_src_no[0]; + no[1] += mv_src_no[1]; + no[2] += mv_src_no[2]; +#endif + bweight += mv_src->bweight; + flag |= mv_src->flag; + } + } + else if (type == CD_MEDGE) { + for (j = 0; j < count; j++) { + MEdge *me_src = &((MEdge *)src_data)[src_indices[j]]; + crease += me_src->crease; + bweight += me_src->bweight; + flag |= me_src->flag; + } + } + else if (CustomData_layer_has_interp(dest, dest_i)) { + /* Already calculated. + * TODO: Optimize by exposing `typeInfo->interp`. */ + } + else if (CustomData_layer_has_math(dest, dest_i)) { + const int size = CustomData_sizeof(type); + void *dst_data = dest->layers[dest_i].data; + void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); + for (j = 0; j < count; j++) { + CustomData_data_add( + type, v_dst, POINTER_OFFSET(src_data, (size_t)src_indices[j] * size)); + } + } + else { + CustomData_copy_layer_type_data(source, dest, type, src_indices[0], dest_index, 1); + } + + /* if there are multiple source & dest layers of the same type, + * we don't want to copy all source layers to the same dest, so + * increment dest_i + */ + dest_i++; + } + } + + float fac = 1.0f / count; + + for (dest_i = 0; dest_i < dest->totlayer; dest_i++) { + CustomDataLayer *layer_dst = &dest->layers[dest_i]; + const int type = layer_dst->type; + if (type == CD_MVERT) { + MVert *mv = &((MVert *)layer_dst->data)[dest_index]; + mul_v3_fl(co, fac); + bweight *= fac; + CLAMP_MAX(bweight, 255); + + copy_v3_v3(mv->co, co); +#ifdef USE_WELD_NORMALS + mul_v3_fl(no, fac); + short *mv_no = mv->no; + mv_no[0] = (short)no[0]; + mv_no[1] = (short)no[1]; + mv_no[2] = (short)no[2]; +#endif + + mv->flag = (char)flag; + mv->bweight = (char)bweight; + } + else if (type == CD_MEDGE) { + MEdge *me = &((MEdge *)layer_dst->data)[dest_index]; + crease *= fac; + bweight *= fac; + CLAMP_MAX(crease, 255); + CLAMP_MAX(bweight, 255); + + me->crease = (char)crease; + me->bweight = (char)bweight; + me->flag = flag; + } + else if (CustomData_layer_has_interp(dest, dest_i)) { + /* Already calculated. */ + } + else if (CustomData_layer_has_math(dest, dest_i)) { + const int size = CustomData_sizeof(type); + void *dst_data = layer_dst->data; + void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); + CustomData_data_multiply(type, v_dst, fac); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Vertex Merging + * \{ */ + +static Mesh *create_merged_mesh(const Mesh &mesh, + MutableSpan<int> vert_dest_map, + const int removed_vertex_count) +{ + Span<MPoly> mpoly{mesh.mpoly, mesh.totpoly}; + Span<MLoop> mloop{mesh.mloop, mesh.totloop}; + const int totvert = mesh.totvert; + const int totedge = mesh.totedge; + const int totloop = mesh.totloop; + const int totpoly = mesh.totpoly; + + WeldMesh weld_mesh; + weld_mesh_context_create(mesh, vert_dest_map, removed_vertex_count, &weld_mesh); + + const int result_nverts = totvert - weld_mesh.vert_kill_len; + const int result_nedges = totedge - weld_mesh.edge_kill_len; + const int result_nloops = totloop - weld_mesh.loop_kill_len; + const int result_npolys = totpoly - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len; + + Mesh *result = BKE_mesh_new_nomain_from_template( + &mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys); + + /* Vertices. */ + + /* Be careful when editing this array, to avoid new allocations it uses the same buffer as + * #vert_dest_map. This map will be used to adjust the edges, polys and loops. */ + MutableSpan<int> vert_final = vert_dest_map; + + int dest_index = 0; + for (int i = 0; i < totvert; i++) { + int source_index = i; + int count = 0; + while (i < totvert && vert_dest_map[i] == OUT_OF_CONTEXT) { + vert_final[i] = dest_index + count; + count++; + i++; + } + if (count) { + CustomData_copy_data(&mesh.vdata, &result->vdata, source_index, dest_index, count); + dest_index += count; + } + if (i == totvert) { + break; + } + if (vert_dest_map[i] != ELEM_MERGED) { + struct WeldGroup *wgroup = &weld_mesh.vert_groups[vert_dest_map[i]]; + customdata_weld(&mesh.vdata, + &result->vdata, + &weld_mesh.vert_groups_buffer[wgroup->ofs], + wgroup->len, + dest_index); + vert_final[i] = dest_index; + dest_index++; + } + } + + BLI_assert(dest_index == result_nverts); + + /* Edges. */ + + /* Be careful when editing this array, to avoid new allocations it uses the same buffer as + * #edge_groups_map. This map will be used to adjust the polys and loops. */ + MutableSpan<int> edge_final = weld_mesh.edge_groups_map; + + dest_index = 0; + for (int i = 0; i < totedge; i++) { + const int source_index = i; + int count = 0; + while (i < totedge && weld_mesh.edge_groups_map[i] == OUT_OF_CONTEXT) { + edge_final[i] = dest_index + count; + count++; + i++; + } + if (count) { + CustomData_copy_data(&mesh.edata, &result->edata, source_index, dest_index, count); + MEdge *me = &result->medge[dest_index]; + dest_index += count; + for (; count--; me++) { + me->v1 = vert_final[me->v1]; + me->v2 = vert_final[me->v2]; + } + } + if (i == totedge) { + break; + } + if (weld_mesh.edge_groups_map[i] != ELEM_MERGED) { + struct WeldGroupEdge *wegrp = &weld_mesh.edge_groups[weld_mesh.edge_groups_map[i]]; + customdata_weld(&mesh.edata, + &result->edata, + &weld_mesh.edge_groups_buffer[wegrp->group.ofs], + wegrp->group.len, + dest_index); + MEdge *me = &result->medge[dest_index]; + me->v1 = vert_final[wegrp->v1]; + me->v2 = vert_final[wegrp->v2]; + /* "For now, assume that all merged edges are loose. This flag will be cleared in the + * Polys/Loops step". */ + me->flag |= ME_LOOSEEDGE; + + edge_final[i] = dest_index; + dest_index++; + } + } + + BLI_assert(dest_index == result_nedges); + + /* Polys/Loops. */ + + MPoly *r_mp = &result->mpoly[0]; + MLoop *r_ml = &result->mloop[0]; + int r_i = 0; + int loop_cur = 0; + Array<int, 64> group_buffer(weld_mesh.max_poly_len); + for (const int i : mpoly.index_range()) { + const MPoly &mp = mpoly[i]; + const int loop_start = loop_cur; + const int poly_ctx = weld_mesh.poly_map[i]; + if (poly_ctx == OUT_OF_CONTEXT) { + int mp_loop_len = mp.totloop; + CustomData_copy_data(&mesh.ldata, &result->ldata, mp.loopstart, loop_cur, mp_loop_len); + loop_cur += mp_loop_len; + for (; mp_loop_len--; r_ml++) { + r_ml->v = vert_final[r_ml->v]; + r_ml->e = edge_final[r_ml->e]; + } + } + else { + const WeldPoly &wp = weld_mesh.wpoly[poly_ctx]; + WeldLoopOfPolyIter iter; + if (!weld_iter_loop_of_poly_begin( + iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { + continue; + } + + if (wp.poly_dst != OUT_OF_CONTEXT) { + continue; + } + while (weld_iter_loop_of_poly_next(iter)) { + customdata_weld( + &mesh.ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur); + int v = vert_final[iter.v]; + int e = edge_final[iter.e]; + r_ml->v = v; + r_ml->e = e; + r_ml++; + loop_cur++; + if (iter.type) { + result->medge[e].flag &= ~ME_LOOSEEDGE; + } + BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0); + } + } + + CustomData_copy_data(&mesh.pdata, &result->pdata, i, r_i, 1); + r_mp->loopstart = loop_start; + r_mp->totloop = loop_cur - loop_start; + r_mp++; + r_i++; + } + + for (const int i : IndexRange(weld_mesh.wpoly_new_len)) { + const WeldPoly &wp = weld_mesh.wpoly_new[i]; + const int loop_start = loop_cur; + WeldLoopOfPolyIter iter; + if (!weld_iter_loop_of_poly_begin( + iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { + continue; + } + + if (wp.poly_dst != OUT_OF_CONTEXT) { + continue; + } + while (weld_iter_loop_of_poly_next(iter)) { + customdata_weld(&mesh.ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur); + int v = vert_final[iter.v]; + int e = edge_final[iter.e]; + r_ml->v = v; + r_ml->e = e; + r_ml++; + loop_cur++; + if (iter.type) { + result->medge[e].flag &= ~ME_LOOSEEDGE; + } + BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0); + } + + r_mp->loopstart = loop_start; + r_mp->totloop = loop_cur - loop_start; + r_mp++; + r_i++; + } + + BLI_assert((int)r_i == result_npolys); + BLI_assert(loop_cur == result_nloops); + + /* We could only update the normals of the elements in context, but the next modifier can make it + * dirty anyway which would make the work useless. */ + BKE_mesh_normals_tag_dirty(result); + + return result; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Merge Map Creation + * \{ */ + +std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh, + const IndexMask selection, + const float merge_distance) +{ + Array<int> vert_dest_map(mesh.totvert, OUT_OF_CONTEXT); + + KDTree_3d *tree = BLI_kdtree_3d_new(selection.size()); + + for (const int i : selection) { + BLI_kdtree_3d_insert(tree, i, mesh.mvert[i].co); + } + + BLI_kdtree_3d_balance(tree); + const int vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast( + tree, merge_distance, false, vert_dest_map.data()); + BLI_kdtree_3d_free(tree); + + if (vert_kill_len == 0) { + return std::nullopt; + } + + return create_merged_mesh(mesh, vert_dest_map, vert_kill_len); +} + +struct WeldVertexCluster { + float co[3]; + int merged_verts; +}; + +std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh, + Span<bool> selection, + const float merge_distance, + const bool only_loose_edges) +{ + Span<MVert> verts{mesh.mvert, mesh.totvert}; + Span<MEdge> edges{mesh.medge, mesh.totedge}; + + int vert_kill_len = 0; + + /* From the original index of the vertex. + * This indicates which vert it is or is going to be merged. */ + Array<int> vert_dest_map(mesh.totvert, OUT_OF_CONTEXT); + + Array<WeldVertexCluster> vert_clusters(mesh.totvert); + + for (const int i : verts.index_range()) { + WeldVertexCluster &vc = vert_clusters[i]; + copy_v3_v3(vc.co, verts[i].co); + vc.merged_verts = 0; + } + const float merge_dist_sq = square_f(merge_distance); + + range_vn_i(vert_dest_map.data(), mesh.totvert, 0); + + /* Collapse Edges that are shorter than the threshold. */ + for (const int i : edges.index_range()) { + int v1 = edges[i].v1; + int v2 = edges[i].v2; + + if (only_loose_edges && (edges[i].flag & ME_LOOSEEDGE) == 0) { + continue; + } + while (v1 != vert_dest_map[v1]) { + v1 = vert_dest_map[v1]; + } + while (v2 != vert_dest_map[v2]) { + v2 = vert_dest_map[v2]; + } + if (v1 == v2) { + continue; + } + if (!selection.is_empty() && (!selection[v1] || !selection[v2])) { + continue; + } + if (v1 > v2) { + SWAP(int, v1, v2); + } + WeldVertexCluster *v1_cluster = &vert_clusters[v1]; + WeldVertexCluster *v2_cluster = &vert_clusters[v2]; + + float edgedir[3]; + sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co); + const float dist_sq = len_squared_v3(edgedir); + if (dist_sq <= merge_dist_sq) { + float influence = (v2_cluster->merged_verts + 1) / + (float)(v1_cluster->merged_verts + v2_cluster->merged_verts + 2); + madd_v3_v3fl(v1_cluster->co, edgedir, influence); + + v1_cluster->merged_verts += v2_cluster->merged_verts + 1; + vert_dest_map[v2] = v1; + vert_kill_len++; + } + } + + if (vert_kill_len == 0) { + return std::nullopt; + } + + for (const int i : IndexRange(mesh.totvert)) { + if (i == vert_dest_map[i]) { + vert_dest_map[i] = OUT_OF_CONTEXT; + } + else { + int v = i; + while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) { + v = vert_dest_map[v]; + } + vert_dest_map[v] = v; + vert_dest_map[i] = v; + } + } + + return create_merged_mesh(mesh, vert_dest_map, vert_kill_len); +} + +/** \} */ + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc new file mode 100644 index 00000000000..daa08a3bce6 --- /dev/null +++ b/source/blender/geometry/intern/point_merge_by_distance.cc @@ -0,0 +1,183 @@ +/* + * 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_kdtree.h" +#include "BLI_task.hh" + +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" +#include "BKE_pointcloud.h" + +#include "GEO_point_merge_by_distance.hh" + +namespace blender::geometry { + +PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, + const float merge_distance, + const IndexMask selection) +{ + const PointCloud &src_pointcloud = *src_points.get_for_read(); + const int src_size = src_pointcloud.totpoint; + Span<float3> positions{reinterpret_cast<float3 *>(src_pointcloud.co), src_size}; + + /* Create the KD tree based on only the selected points, to speed up merge detection and + * balancing. */ + KDTree_3d *tree = BLI_kdtree_3d_new(selection.size()); + for (const int i : selection.index_range()) { + BLI_kdtree_3d_insert(tree, i, positions[selection[i]]); + } + BLI_kdtree_3d_balance(tree); + + /* Find the duplicates in the KD tree. Because the tree only contains the selected points, the + * resulting indices are indices into the selection, rather than indices of the source point + * cloud. */ + Array<int> selection_merge_indices(selection.size(), -1); + const int duplicate_count = BLI_kdtree_3d_calc_duplicates_fast( + tree, merge_distance, false, selection_merge_indices.data()); + BLI_kdtree_3d_free(tree); + + /* Create the new point cloud and add it to a temporary component for the attribute API. */ + const int dst_size = src_size - duplicate_count; + PointCloud *dst_pointcloud = BKE_pointcloud_new_nomain(dst_size); + PointCloudComponent dst_points; + dst_points.replace(dst_pointcloud, GeometryOwnershipType::Editable); + + /* By default, every point is just "merged" with itself. Then fill in the results of the merge + * finding, converting from indices into the selection to indices into the full input point + * cloud. */ + Array<int> merge_indices(src_size); + for (const int i : merge_indices.index_range()) { + merge_indices[i] = i; + } + for (const int i : selection_merge_indices.index_range()) { + const int merge_index = selection_merge_indices[i]; + if (merge_index != -1) { + const int src_merge_index = selection[merge_index]; + const int src_index = selection[i]; + merge_indices[src_index] = src_merge_index; + } + } + + /* For every source index, find the corresponding index in the result by iterating through the + * source indices and counting how many merges happened before that point. */ + int merged_points = 0; + Array<int> src_to_dst_indices(src_size); + for (const int i : IndexRange(src_size)) { + src_to_dst_indices[i] = i - merged_points; + if (merge_indices[i] != i) { + merged_points++; + } + } + + /* In order to use a contiguous array as the storage for every destination point's source + * indices, first the number of source points must be counted for every result point. */ + Array<int> point_merge_counts(dst_size, 0); + for (const int i : IndexRange(src_size)) { + const int merge_index = merge_indices[i]; + const int dst_index = src_to_dst_indices[merge_index]; + point_merge_counts[dst_index]++; + } + + /* This array stores an offset into `merge_map` for every result point. */ + Array<int> map_offsets(dst_size + 1); + int offset = 0; + for (const int i : IndexRange(dst_size)) { + map_offsets[i] = offset; + offset += point_merge_counts[i]; + } + map_offsets.last() = offset; + + point_merge_counts.fill(0); + + /* This array stores all of the source indices for every result point. The size is the source + * size because every input point is either merged with another or copied directly. */ + Array<int> merge_map(src_size); + for (const int i : IndexRange(src_size)) { + const int merge_index = merge_indices[i]; + const int dst_index = src_to_dst_indices[merge_index]; + + const IndexRange point_range(map_offsets[dst_index], + map_offsets[dst_index + 1] - map_offsets[dst_index]); + MutableSpan<int> point_merge_indices = merge_map.as_mutable_span().slice(point_range); + point_merge_indices[point_merge_counts[dst_index]] = i; + point_merge_counts[dst_index]++; + } + + Set<bke::AttributeIDRef> attributes = src_points.attribute_ids(); + + /* Transfer the ID attribute if it exists, using the ID of the first merged point. */ + if (attributes.contains("id")) { + VArray<int> src = src_points.attribute_get_for_read<int>("id", ATTR_DOMAIN_POINT, 0); + bke::OutputAttribute_Typed<int> dst = dst_points.attribute_try_get_for_output_only<int>( + "id", ATTR_DOMAIN_POINT); + Span<int> src_ids = src.get_internal_span(); + MutableSpan<int> dst_ids = dst.as_span(); + + threading::parallel_for(IndexRange(dst_size), 1024, [&](IndexRange range) { + for (const int i_dst : range) { + const IndexRange point_range(map_offsets[i_dst], + map_offsets[i_dst + 1] - map_offsets[i_dst]); + dst_ids[i_dst] = src_ids[point_range.first()]; + } + }); + + dst.save(); + attributes.remove_contained("id"); + } + + /* Transfer all other attributes. */ + for (const bke::AttributeIDRef &id : attributes) { + if (!id.should_be_kept()) { + continue; + } + + bke::ReadAttributeLookup src_attribute = src_points.attribute_try_get_for_read(id); + attribute_math::convert_to_static_type(src_attribute.varray.type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { + bke::OutputAttribute_Typed<T> dst_attribute = + dst_points.attribute_try_get_for_output_only<T>(id, ATTR_DOMAIN_POINT); + Span<T> src = src_attribute.varray.get_internal_span().typed<T>(); + MutableSpan<T> dst = dst_attribute.as_span(); + + threading::parallel_for(IndexRange(dst_size), 1024, [&](IndexRange range) { + for (const int i_dst : range) { + /* Create a separate mixer for every point to avoid allocating temporary buffers + * in the mixer the size of the result point cloud and to improve memory locality. */ + attribute_math::DefaultMixer<T> mixer{dst.slice(i_dst, 1)}; + + const IndexRange point_range(map_offsets[i_dst], + map_offsets[i_dst + 1] - map_offsets[i_dst]); + Span<int> src_merge_indices = merge_map.as_span().slice(point_range); + for (const int i_src : src_merge_indices) { + mixer.mix_in(0, src[i_src]); + } + + mixer.finalize(); + } + }); + + dst_attribute.save(); + } + }); + } + + return dst_pointcloud; +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 4022794d53f..18dd74cb688 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -1269,7 +1269,7 @@ static void remove_id_attribute_from_instances(GeometrySet &geometry_set) { geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) { if (sub_geometry.has<InstancesComponent>()) { - InstancesComponent &component = geometry_set.get_component_for_write<InstancesComponent>(); + InstancesComponent &component = sub_geometry.get_component_for_write<InstancesComponent>(); component.attributes().remove("id"); } }); diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 5ee75619259..a3f468a20dc 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -92,10 +92,6 @@ set(SRC set(LIB ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4bb66b78dd0..cffea5e9dcc 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -323,6 +323,7 @@ set(GLSL_SRC shaders/material/gpu_shader_material_output_material.glsl shaders/material/gpu_shader_material_output_world.glsl shaders/material/gpu_shader_material_particle_info.glsl + shaders/material/gpu_shader_material_point_info.glsl shaders/material/gpu_shader_material_principled.glsl shaders/material/gpu_shader_material_refraction.glsl shaders/material/gpu_shader_material_rgb_curves.glsl @@ -374,7 +375,7 @@ set(GLSL_SRC shaders/gpu_shader_common_obinfos_lib.glsl - intern/gpu_shader_shared_utils.h + GPU_shader_shared_utils.h ) set(GLSL_C) @@ -392,7 +393,7 @@ set(GLSL_SOURCE_CONTENT "") foreach(GLSL_FILE ${GLSL_SRC}) get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME) string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME}) - string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\"\)\n") + string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n") endforeach() set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_gpu_source_list.h") @@ -401,48 +402,58 @@ list(APPEND SRC ${glsl_source_list_file}) list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) set(SHADER_CREATE_INFOS -#../draw/engines/workbench/shaders/workbench_effect_cavity_info.hh -#../draw/engines/workbench/shaders/workbench_prepass_info.hh -../draw/intern/shaders/draw_fullscreen_info.hh -../draw/intern/shaders/draw_object_infos_info.hh -../draw/intern/shaders/draw_view_info.hh - -shaders/infos/gpu_clip_planes_info.hh -shaders/infos/gpu_shader_2D_area_borders_info.hh -shaders/infos/gpu_shader_2D_checker_info.hh -shaders/infos/gpu_shader_2D_diag_stripes_info.hh -shaders/infos/gpu_shader_2D_flat_color_info.hh -shaders/infos/gpu_shader_2D_image_color_info.hh -shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh -shaders/infos/gpu_shader_2D_image_info.hh -shaders/infos/gpu_shader_2D_image_multi_rect_color_info.hh -shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh -shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh -shaders/infos/gpu_shader_2D_image_rect_color_info.hh -shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh -shaders/infos/gpu_shader_2D_nodelink_info.hh -shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh -shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh -shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh -shaders/infos/gpu_shader_2D_smooth_color_info.hh -shaders/infos/gpu_shader_2D_uniform_color_info.hh -shaders/infos/gpu_shader_3D_depth_only_info.hh -shaders/infos/gpu_shader_3D_flat_color_info.hh -shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh -shaders/infos/gpu_shader_3D_point_info.hh -shaders/infos/gpu_shader_3D_smooth_color_info.hh -shaders/infos/gpu_shader_3D_uniform_color_info.hh -shaders/infos/gpu_shader_gpencil_stroke_info.hh -shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh -shaders/infos/gpu_shader_keyframe_shape_info.hh -shaders/infos/gpu_shader_simple_lighting_info.hh -shaders/infos/gpu_shader_text_info.hh -shaders/infos/gpu_srgb_to_framebuffer_space_info.hh + ../draw/engines/workbench/shaders/infos/workbench_composite_info.hh + ../draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh + ../draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh + ../draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh + ../draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh + ../draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh + ../draw/engines/workbench/shaders/infos/workbench_prepass_info.hh + ../draw/engines/workbench/shaders/infos/workbench_shadow_info.hh + ../draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh + ../draw/engines/workbench/shaders/infos/workbench_volume_info.hh + ../draw/engines/image/shaders/infos/engine_image_info.hh + ../draw/intern/shaders/draw_fullscreen_info.hh + ../draw/intern/shaders/draw_object_infos_info.hh + ../draw/intern/shaders/draw_view_info.hh + ../draw/intern/shaders/draw_hair_refine_info.hh + + shaders/infos/gpu_clip_planes_info.hh + shaders/infos/gpu_shader_2D_area_borders_info.hh + shaders/infos/gpu_shader_2D_checker_info.hh + shaders/infos/gpu_shader_2D_diag_stripes_info.hh + shaders/infos/gpu_shader_2D_flat_color_info.hh + shaders/infos/gpu_shader_2D_image_color_info.hh + shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh + shaders/infos/gpu_shader_2D_image_info.hh + shaders/infos/gpu_shader_2D_image_multi_rect_color_info.hh + shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh + shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh + shaders/infos/gpu_shader_2D_image_rect_color_info.hh + shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh + shaders/infos/gpu_shader_2D_nodelink_info.hh + shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh + shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh + shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh + shaders/infos/gpu_shader_2D_smooth_color_info.hh + shaders/infos/gpu_shader_2D_uniform_color_info.hh + shaders/infos/gpu_shader_3D_depth_only_info.hh + shaders/infos/gpu_shader_3D_flat_color_info.hh + shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh + shaders/infos/gpu_shader_3D_point_info.hh + shaders/infos/gpu_shader_3D_smooth_color_info.hh + shaders/infos/gpu_shader_3D_uniform_color_info.hh + shaders/infos/gpu_shader_gpencil_stroke_info.hh + shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh + shaders/infos/gpu_shader_keyframe_shape_info.hh + shaders/infos/gpu_shader_simple_lighting_info.hh + shaders/infos/gpu_shader_text_info.hh + shaders/infos/gpu_srgb_to_framebuffer_space_info.hh ) set(SHADER_CREATE_INFOS_CONTENT "") foreach(DESCRIPTOR_FILE ${SHADER_CREATE_INFOS}) -string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n") + string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n") endforeach() set(shader_create_info_list_file "${CMAKE_CURRENT_BINARY_DIR}/gpu_shader_create_info_list.hh") diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h index 9796ac63272..18a35f9c71a 100644 --- a/source/blender/gpu/GPU_debug.h +++ b/source/blender/gpu/GPU_debug.h @@ -31,6 +31,8 @@ extern "C" { #endif +#define GPU_DEBUG_SHADER_COMPILATION_GROUP "Shader Compilation" + void GPU_debug_group_begin(const char *name); void GPU_debug_group_end(void); /** diff --git a/source/blender/gpu/GPU_select.h b/source/blender/gpu/GPU_select.h index c02af763311..3076058c075 100644 --- a/source/blender/gpu/GPU_select.h +++ b/source/blender/gpu/GPU_select.h @@ -32,7 +32,7 @@ extern "C" { struct rcti; /** Flags for mode of operation. */ -enum { +typedef enum eGPUSelectMode { GPU_SELECT_ALL = 1, /* gpu_select_query */ GPU_SELECT_NEAREST_FIRST_PASS = 2, @@ -40,13 +40,32 @@ enum { /* gpu_select_pick */ GPU_SELECT_PICK_ALL = 4, GPU_SELECT_PICK_NEAREST = 5, -}; +} eGPUSelectMode; + +/** + * The result of calling #GPU_select_begin & #GPU_select_end. + */ +typedef struct GPUSelectResult { + /** The selection identifier matching the value passed in by #GPU_select_load_id. */ + unsigned int id; + /** + * The nearest depth. + * - Only supported by picking modes (#GPU_SELECT_PICK_ALL and #GPU_SELECT_PICK_NEAREST) + * since occlusion quires don't provide a convenient way of accessing the depth-buffer. + * - OpenGL's `GL_SELECT` supported both near and far depths, + * this has not been included as Blender doesn't need this however support could be added. + */ + unsigned int depth; +} GPUSelectResult; /** * Initialize and provide buffer for results. */ -void GPU_select_begin( - unsigned int *buffer, unsigned int bufsize, const struct rcti *input, char mode, int oldhits); +void GPU_select_begin(GPUSelectResult *buffer, + unsigned int buffer_len, + const struct rcti *input, + eGPUSelectMode mode, + int oldhits); /** * Loads a new selection id and ends previous query, if any. * In second pass of selection it also returns @@ -79,8 +98,8 @@ void GPU_select_cache_end(void); * * Note that comparing depth as uint is fine. */ -const uint *GPU_select_buffer_near(const uint *buffer, int hits); -uint GPU_select_buffer_remove_by_id(uint *buffer, int hits, uint select_id); +const GPUSelectResult *GPU_select_buffer_near(const GPUSelectResult *buffer, int hits); +uint GPU_select_buffer_remove_by_id(GPUSelectResult *buffer, int hits, uint select_id); /** * Part of the solution copied from `rect_subregion_stride_calc`. */ diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 97b9b26ba2a..05c992274eb 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -69,6 +69,9 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, int tf_count, const char *shname); GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info); +GPUShader *GPU_shader_create_from_info_name(const char *info_name); + +const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name); struct GPU_ShaderCreateFromArray_Params { const char **vert, **geom, **frag, **defs; @@ -148,9 +151,14 @@ typedef enum { } GPUUniformBuiltin; typedef enum { + /** Deprecated */ GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */ GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */ GPU_UNIFORM_BLOCK_INFO, /* infoBlock */ + /** New ones */ + GPU_UNIFORM_BLOCK_DRW_VIEW, + GPU_UNIFORM_BLOCK_DRW_MODEL, + GPU_UNIFORM_BLOCK_DRW_INFOS, GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ } GPUUniformBlockBuiltin; diff --git a/source/blender/gpu/GPU_shader_shared.h b/source/blender/gpu/GPU_shader_shared.h index 334b974acd8..636a8f53ba6 100644 --- a/source/blender/gpu/GPU_shader_shared.h +++ b/source/blender/gpu/GPU_shader_shared.h @@ -22,14 +22,7 @@ */ #ifndef USE_GPU_SHADER_CREATE_INFO -# include "intern/gpu_shader_shared_utils.h" -#endif - -#ifdef __cplusplus -using blender::float2; -using blender::float3; -using blender::float4; -using blender::float4x4; +# include "GPU_shader_shared_utils.h" #endif struct NodeLinkData { diff --git a/source/blender/gpu/intern/gpu_shader_shared_utils.h b/source/blender/gpu/GPU_shader_shared_utils.h index f061f0f968a..0d283fb1e66 100644 --- a/source/blender/gpu/intern/gpu_shader_shared_utils.h +++ b/source/blender/gpu/GPU_shader_shared_utils.h @@ -27,11 +27,10 @@ * Some preprocessing is done by the GPU back-end to make it GLSL compatible. * * IMPORTANT: - * - Don't add trailing comma at the end of the enum. Our custom pre-processor will now trim it - * for GLSL. * - Always use `u` suffix for enum values. GLSL do not support implicit cast. * - Define all values. This is in order to simplify custom pre-processor code. - * - Always use uint32_t as underlying type. + * - (C++ only) Always use `uint32_t` as underlying type (`enum eMyEnum : uint32_t`). + * - (C only) do NOT use the enum type inside UBO/SSBO structs and use `uint` instead. * - Use float suffix by default for float literals to avoid double promotion in C++. * - Pack one float or int after a vec3/ivec3 to fulfill alignment rules. * @@ -74,31 +73,44 @@ # define bool3 bvec3 # define bool4 bvec4 -#else /* C */ +#else /* C / C++ */ # pragma once # include "BLI_assert.h" # ifdef __cplusplus # include "BLI_float4x4.hh" -# else +# include "BLI_math_vec_types.hh" +using blender::float2; +using blender::float3; +using blender::float4; +using blender::float4x4; +using blender::int2; +using blender::int3; +using blender::int4; +using blender::uint2; +using blender::uint3; +using blender::uint4; +using bool1 = int; +using bool2 = blender::int2; +using bool3 = blender::int3; +using bool4 = blender::int4; + +# else /* C */ typedef float float2[2]; typedef float float3[3]; typedef float float4[4]; typedef float float4x4[4][4]; -# endif typedef int int2[2]; typedef int int3[2]; typedef int int4[4]; typedef uint uint2[2]; typedef uint uint3[3]; typedef uint uint4[4]; -typedef int int2[2]; -typedef int int3[2]; -typedef int int4[4]; typedef int bool1; typedef int bool2[2]; typedef int bool3[2]; typedef int bool4[4]; +# endif #endif diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 22cd7eab927..bd52b8321bb 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -37,11 +37,14 @@ ENUM_OPERATORS(eGPUWriteMask, GPU_WRITE_COLOR) typedef enum eGPUBarrier { GPU_BARRIER_NONE = 0, - GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0), - GPU_BARRIER_TEXTURE_FETCH = (1 << 1), - GPU_BARRIER_SHADER_STORAGE = (1 << 2), - GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 3), - GPU_BARRIER_ELEMENT_ARRAY = (1 << 4), + GPU_BARRIER_COMMAND = (1 << 0), + GPU_BARRIER_FRAMEBUFFER = (1 << 1), + GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 2), + GPU_BARRIER_SHADER_STORAGE = (1 << 3), + GPU_BARRIER_TEXTURE_FETCH = (1 << 4), + GPU_BARRIER_TEXTURE_UPDATE = (1 << 5), + GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 6), + GPU_BARRIER_ELEMENT_ARRAY = (1 << 7), } eGPUBarrier; ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY) diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index d65115541fa..7812c7a7257 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -273,6 +273,7 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl); * \warning Only work for 2D texture for now. * \warning Only clears the mip 0 of the texture. * \param data_format: data format of the pixel data. + * \note The format is float for unorm textures. * \param data: 1 pixel worth of data to fill the texture with. */ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data); diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 568462ec2a6..e1c6901470a 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -711,10 +711,10 @@ static char *code_generate_vertex(GPUNodeGraph *graph, LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { const char *type_str = gpu_data_type_to_string(attr->gputype); const char *prefix = attr_prefix_get(attr->type); - /* XXX FIXME : see notes in mesh_render_data_create() */ - /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ + /* XXX FIXME: see notes in mesh_render_data_create() */ + /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ if (attr->type == CD_ORCO) { - /* OPTI : orco is computed from local positions, but only if no modifier is present. */ + /* OPTI: orco is computed from local positions, but only if no modifier is present. */ BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 5b1eac2e82f..28f5b8b02b0 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -437,7 +437,7 @@ void GPU_framebuffer_blit(GPUFrameBuffer *gpufb_read, fb_read->blit_to(blit_buffers, read_slot, fb_write, write_slot, 0, 0); - /* FIXME(fclem) sRGB is not saved. */ + /* FIXME(@fclem): sRGB is not saved. */ prev_fb->bind(true); } diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh index a936e889e57..e7505258d2a 100644 --- a/source/blender/gpu/intern/gpu_framebuffer_private.hh +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -43,8 +43,9 @@ typedef enum GPUAttachmentType : int { GPU_FB_COLOR_ATTACHMENT3, GPU_FB_COLOR_ATTACHMENT4, GPU_FB_COLOR_ATTACHMENT5, - /* Number of maximum output slots. - * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */ + GPU_FB_COLOR_ATTACHMENT6, + GPU_FB_COLOR_ATTACHMENT7, + /* Number of maximum output slots. */ /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to * the maximum number of COLOR attachments specified by glDrawBuffers. */ GPU_FB_MAX_ATTACHMENT, diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 6451086f5fc..11c61e4cf72 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -44,6 +44,8 @@ #include "BKE_node.h" #include "BKE_scene.h" +#include "NOD_shader.h" + #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_texture.h" diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c index 74e0270c42a..712ab44adb3 100644 --- a/source/blender/gpu/intern/gpu_material_library.c +++ b/source/blender/gpu/intern/gpu_material_library.c @@ -91,6 +91,7 @@ extern char datatoc_gpu_shader_material_output_aov_glsl[]; extern char datatoc_gpu_shader_material_output_material_glsl[]; extern char datatoc_gpu_shader_material_output_world_glsl[]; extern char datatoc_gpu_shader_material_particle_info_glsl[]; +extern char datatoc_gpu_shader_material_point_info_glsl[]; extern char datatoc_gpu_shader_material_principled_glsl[]; extern char datatoc_gpu_shader_material_refraction_glsl[]; extern char datatoc_gpu_shader_material_rgb_curves_glsl[]; @@ -295,7 +296,7 @@ static GPUMaterialLibrary gpu_shader_material_glass_library = { static GPUMaterialLibrary gpu_shader_material_hair_info_library = { .code = datatoc_gpu_shader_material_hair_info_glsl, - .dependencies = {NULL}, + .dependencies = {&gpu_shader_material_hash_library, NULL}, }; static GPUMaterialLibrary gpu_shader_material_holdout_library = { @@ -388,6 +389,11 @@ static GPUMaterialLibrary gpu_shader_material_particle_info_library = { .dependencies = {NULL}, }; +static GPUMaterialLibrary gpu_shader_material_point_info_library = { + .code = datatoc_gpu_shader_material_point_info_glsl, + .dependencies = {&gpu_shader_material_hash_library, NULL}, +}; + static GPUMaterialLibrary gpu_shader_material_principled_library = { .code = datatoc_gpu_shader_material_principled_glsl, .dependencies = {NULL}, @@ -644,6 +650,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = { &gpu_shader_material_output_material_library, &gpu_shader_material_output_world_library, &gpu_shader_material_particle_info_library, + &gpu_shader_material_point_info_library, &gpu_shader_material_principled_library, &gpu_shader_material_refraction_library, &gpu_shader_material_rgb_curves_library, diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index aadb52fb49c..afef0e70ccc 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -43,24 +43,33 @@ * \{ */ /* Internal algorithm used */ -enum { +typedef enum eGPUSelectAlgo { /** glBegin/EndQuery(GL_SAMPLES_PASSED... ), `gpu_select_query.c` * Only sets 4th component (ID) correctly. */ ALGO_GL_QUERY = 1, /** Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.c` * Only sets 4th component (ID) correctly. */ ALGO_GL_PICK = 2, -}; +} eGPUSelectAlgo; typedef struct GPUSelectState { /* To ignore selection id calls when not initialized */ bool select_is_active; /* mode of operation */ - char mode; + eGPUSelectMode mode; /* internal algorithm for selection */ - char algorithm; + eGPUSelectAlgo algorithm; /* allow GPU_select_begin/end without drawing */ bool use_cache; + /** + * Signifies that #GPU_select_cache_begin has been called, + * future calls to #GPU_select_begin should initialize the cache. + * + * \note #GPU_select_cache_begin could perform initialization but doesn't as it's inconvenient + * for callers making the cache begin/end calls outside lower level selection logic + * where the `mode` to pass to #GPU_select_begin yet isn't known. + */ + bool use_cache_needs_init; } GPUSelectState; static GPUSelectState g_select_state = {0}; @@ -71,7 +80,11 @@ static GPUSelectState g_select_state = {0}; /** \name Public API * \{ */ -void GPU_select_begin(uint *buffer, uint bufsize, const rcti *input, char mode, int oldhits) +void GPU_select_begin(GPUSelectResult *buffer, + const uint buffer_len, + const rcti *input, + eGPUSelectMode mode, + int oldhits) { if (mode == GPU_SELECT_NEAREST_SECOND_PASS) { /* In the case hits was '-1', @@ -90,15 +103,32 @@ void GPU_select_begin(uint *buffer, uint bufsize, const rcti *input, char mode, g_select_state.algorithm = ALGO_GL_QUERY; } + /* This function is called when cache has already been initialized, + * so only manipulate cache values when cache is pending. */ + if (g_select_state.use_cache_needs_init) { + g_select_state.use_cache_needs_init = false; + + switch (g_select_state.algorithm) { + case ALGO_GL_QUERY: { + g_select_state.use_cache = false; + break; + } + default: { + g_select_state.use_cache = true; + gpu_select_pick_cache_begin(); + break; + } + } + } + switch (g_select_state.algorithm) { case ALGO_GL_QUERY: { - g_select_state.use_cache = false; - gpu_select_query_begin((uint(*)[4])buffer, bufsize / 4, input, mode, oldhits); + gpu_select_query_begin(buffer, buffer_len, input, mode, oldhits); break; } default: /* ALGO_GL_PICK */ { - gpu_select_pick_begin((uint(*)[4])buffer, bufsize / 4, input, mode); + gpu_select_pick_begin(buffer, buffer_len, input, mode); break; } } @@ -154,12 +184,13 @@ uint GPU_select_end(void) void GPU_select_cache_begin(void) { - /* validate on GPU_select_begin, clear if not supported */ - BLI_assert(g_select_state.use_cache == false); - g_select_state.use_cache = true; - if (g_select_state.algorithm == ALGO_GL_PICK) { - gpu_select_pick_cache_begin(); - } + BLI_assert(g_select_state.select_is_active == false); + /* Ensure #GPU_select_cache_end is always called. */ + BLI_assert(g_select_state.use_cache_needs_init == false); + + /* Signal that cache should be used, instead of calling the algorithms cache-begin function. + * This is more convenient as the exact method of selection may not be known by the caller. */ + g_select_state.use_cache_needs_init = true; } void GPU_select_cache_load_id(void) @@ -173,9 +204,12 @@ void GPU_select_cache_load_id(void) void GPU_select_cache_end(void) { if (g_select_state.algorithm == ALGO_GL_PICK) { + BLI_assert(g_select_state.use_cache == true); gpu_select_pick_cache_end(); } g_select_state.use_cache = false; + /* Paranoid assignment, should already be false. */ + g_select_state.use_cache_needs_init = false; } bool GPU_select_is_cached(void) @@ -189,35 +223,35 @@ bool GPU_select_is_cached(void) /** \name Utilities * \{ */ -const uint *GPU_select_buffer_near(const uint *buffer, int hits) +const GPUSelectResult *GPU_select_buffer_near(const GPUSelectResult *buffer, int hits) { - const uint *buffer_near = NULL; + const GPUSelectResult *buffer_near = NULL; uint depth_min = (uint)-1; for (int i = 0; i < hits; i++) { - if (buffer[1] < depth_min) { - BLI_assert(buffer[3] != -1); - depth_min = buffer[1]; + if (buffer->depth < depth_min) { + BLI_assert(buffer->id != -1); + depth_min = buffer->depth; buffer_near = buffer; } - buffer += 4; + buffer++; } return buffer_near; } -uint GPU_select_buffer_remove_by_id(uint *buffer, int hits, uint select_id) +uint GPU_select_buffer_remove_by_id(GPUSelectResult *buffer, int hits, uint select_id) { - uint *buffer_src = buffer; - uint *buffer_dst = buffer; + GPUSelectResult *buffer_src = buffer; + GPUSelectResult *buffer_dst = buffer; int hits_final = 0; for (int i = 0; i < hits; i++) { - if (buffer_src[3] != select_id) { + if (buffer_src->id != select_id) { if (buffer_dst != buffer_src) { - memcpy(buffer_dst, buffer_src, sizeof(int[4])); + memcpy(buffer_dst, buffer_src, sizeof(GPUSelectResult)); } - buffer_dst += 4; + buffer_dst++; hits_final += 1; } - buffer_src += 4; + buffer_src++; } return hits_final; } diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index 737e4c6e874..a5c0b2b1a14 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -55,15 +55,19 @@ /** \name #SubRectStride * \{ */ -/* For looping over a sub-region of a rect, could be moved into 'rct.c'. */ +/** For looping over a sub-region of a #rcti, could be moved into 'rct.c'. */ typedef struct SubRectStride { - uint start; /* start here */ - uint span; /* read these */ - uint span_len; /* len times (read span 'len' times). */ - uint skip; /* skip those */ + /** Start here. */ + uint start; + /** Read these. */ + uint span; + /** `len` times (read span 'len' times). */ + uint span_len; + /** Skip those. */ + uint skip; } SubRectStride; -/* we may want to change back to float if uint isn't well supported */ +/** We may want to change back to float if `uint` isn't well supported. */ typedef uint depth_t; /** @@ -104,11 +108,11 @@ BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr) /* -------------------------------------------------------------------- */ /** \name #DepthBufCache * - * Result of reading #glReadPixels, + * Result of reading #GPU_framebuffer_read_depth, * use for both cache and non-cached storage. * \{ */ -/** Store result of #glReadPixels. */ +/** Store result of #GPU_framebuffer_read_depth. */ typedef struct DepthBufCache { struct DepthBufCache *next, *prev; uint id; @@ -174,7 +178,7 @@ static bool depth_buf_subrect_depth_any_filled(const DepthBufCache *rect_src, const DepthBufCache *rect_dst, const SubRectStride *sub_rect) { - /* same as above but different rect sizes */ + /* Same as above but different rectangle sizes. */ const depth_t *prev = rect_src->buf + sub_rect->start; const depth_t *curr = rect_dst->buf + sub_rect->start; for (uint i = 0; i < sub_rect->span_len; i++) { @@ -235,66 +239,68 @@ static int depth_cmp(const void *v1, const void *v2) /** \name Main Selection Begin/End/Load API * \{ */ -/* depth sorting */ +/** Depth sorting. */ typedef struct GPUPickState { - /* cache on initialization */ - uint (*buffer)[4]; + /** Cache on initialization. */ + GPUSelectResult *buffer; + uint buffer_len; + /** Mode of this operation. */ + eGPUSelectMode mode; - /* Buffer size (stores number of integers, for actual size multiply by sizeof integer). */ - uint bufsize; - /* mode of operation */ - char mode; - - /* OpenGL drawing, never use when (is_cached == true). */ + /** GPU drawing, never use when `is_cached == true`. */ struct { - /* The current depth, accumulated as we draw */ + /** The current depth, accumulated while drawing. */ DepthBufCache *rect_depth; - /* Scratch buffer, avoid allocs every time (when not caching) */ + /** Scratch buffer, avoid allocations every time (when not caching). */ DepthBufCache *rect_depth_test; - /* Pass to glReadPixels (x, y, w, h) */ + /** Pass to `GPU_framebuffer_read_depth(x, y, w, h)`. */ int clip_readpixels[4]; - /* Set after first draw */ + /** Set after first draw. */ bool is_init; uint prev_id; - } gl; + } gpu; - /* src: data stored in 'cache' and 'gl', - * dst: use when cached region is smaller (where src -> dst isn't 1:1) */ + /** + * `src`: data stored in 'cache' and 'gpu', + * `dst`: use when cached region is smaller (where `src` -> `dst` isn't 1:1). + */ struct { rcti clip_rect; uint rect_len; } src, dst; - /* Store cache between `GPU_select_cache_begin/end` */ + /** Store cache between `GPU_select_cache_begin/end` */ bool use_cache; bool is_cached; struct { - /* Cleanup used for iterating over both source and destination buffers: - * src.clip_rect -> dst.clip_rect */ + /** + * Cleanup used for iterating over both source and destination buffers: + * `src.clip_rect` -> `dst.clip_rect`. + */ SubRectStride sub_rect; - /* List of DepthBufCache, sized of 'src.clip_rect' */ + /** List of #DepthBufCache, sized of 'src.clip_rect'. */ ListBase bufs; } cache; - /* Picking methods. */ + /** Picking methods. */ union { - /* GPU_SELECT_PICK_ALL */ + /** #GPU_SELECT_PICK_ALL */ struct { DepthID *hits; uint hits_len; uint hits_len_alloc; } all; - /* GPU_SELECT_PICK_NEAREST */ + /** #GPU_SELECT_PICK_NEAREST */ struct { uint *rect_id; } nearest; }; - /* Previous state to restore after drawing. */ + /** Previous state to restore after drawing. */ int viewport[4]; int scissor[4]; eGPUWriteMask write_mask; @@ -303,37 +309,44 @@ typedef struct GPUPickState { static GPUPickState g_pick_state = {0}; -void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, char mode) +void gpu_select_pick_begin(GPUSelectResult *buffer, + const uint buffer_len, + const rcti *input, + eGPUSelectMode mode) { GPUPickState *ps = &g_pick_state; #ifdef DEBUG_PRINT - printf("%s: mode=%d, use_cache=%d, is_cache=%d\n", __func__, mode, ps->use_cache, ps->is_cached); + printf("%s: mode=%d, use_cache=%d, is_cache=%d\n", + __func__, + (int)mode, + ps->use_cache, + ps->is_cached); #endif GPU_debug_group_begin("Selection Pick"); - ps->bufsize = bufsize; ps->buffer = buffer; + ps->buffer_len = buffer_len; ps->mode = mode; const uint rect_len = (uint)(BLI_rcti_size_x(input) * BLI_rcti_size_y(input)); ps->dst.clip_rect = *input; ps->dst.rect_len = rect_len; - /* Restrict OpenGL operations for when we don't have cache */ + /* Avoids unnecessary GPU operations when cache is available and they are unnecessary. */ if (ps->is_cached == false) { ps->write_mask = GPU_write_mask_get(); ps->depth_test = GPU_depth_test_get(); GPU_scissor_get(ps->scissor); - /* disable writing to the framebuffer */ + /* Disable writing to the frame-buffer. */ GPU_color_mask(false, false, false, false); GPU_depth_mask(true); - /* Always use #GL_LEQUAL even though GPU_SELECT_PICK_ALL always clears the buffer. This is - * because individual objects themselves might have sections that overlap and we need these - * to have the correct distance information. */ + /* Always use #GPU_DEPTH_LESS_EQUAL even though #GPU_SELECT_PICK_ALL always clears the buffer. + * This is because individual objects themselves might have sections that overlap and we need + * these to have the correct distance information. */ GPU_depth_test(GPU_DEPTH_LESS_EQUAL); float viewport[4]; @@ -342,35 +355,35 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c ps->src.clip_rect = *input; ps->src.rect_len = rect_len; - ps->gl.clip_readpixels[0] = (int)viewport[0]; - ps->gl.clip_readpixels[1] = (int)viewport[1]; - ps->gl.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect); - ps->gl.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect); + ps->gpu.clip_readpixels[0] = (int)viewport[0]; + ps->gpu.clip_readpixels[1] = (int)viewport[1]; + ps->gpu.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect); + ps->gpu.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect); - GPU_viewport(UNPACK4(ps->gl.clip_readpixels)); + GPU_viewport(UNPACK4(ps->gpu.clip_readpixels)); /* It's possible we don't want to clear depth buffer, * so existing elements are masked by current z-buffer. */ GPU_clear_depth(1.0f); /* scratch buffer (read new values here) */ - ps->gl.rect_depth_test = depth_buf_malloc(rect_len); - ps->gl.rect_depth = depth_buf_malloc(rect_len); + ps->gpu.rect_depth_test = depth_buf_malloc(rect_len); + ps->gpu.rect_depth = depth_buf_malloc(rect_len); - /* set initial 'far' value */ + /* Set initial 'far' value. */ for (uint i = 0; i < rect_len; i++) { - ps->gl.rect_depth->buf[i] = DEPTH_MAX; + ps->gpu.rect_depth->buf[i] = DEPTH_MAX; } - ps->gl.is_init = false; - ps->gl.prev_id = 0; + ps->gpu.is_init = false; + ps->gpu.prev_id = 0; } else { - /* Using cache (ps->is_cached == true) */ - /* src.clip_rect -> dst.clip_rect */ + /* Using cache `ps->is_cached == true`. */ + /* `src.clip_rect` -> `dst.clip_rect`. */ rect_subregion_stride_calc(&ps->src.clip_rect, &ps->dst.clip_rect, &ps->cache.sub_rect); - BLI_assert(ps->gl.rect_depth == NULL); - BLI_assert(ps->gl.rect_depth_test == NULL); + BLI_assert(ps->gpu.rect_depth == NULL); + BLI_assert(ps->gpu.rect_depth_test == NULL); } if (mode == GPU_SELECT_PICK_ALL) { @@ -379,7 +392,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c ps->all.hits_len_alloc = ALLOC_DEPTHS; } else { - /* Set to 0xff for SELECT_ID_NONE */ + /* Set to 0xff for #SELECT_ID_NONE. */ ps->nearest.rect_id = MEM_mallocN(sizeof(uint) * ps->dst.rect_len, __func__); memset(ps->nearest.rect_id, 0xff, sizeof(uint) * ps->dst.rect_len); } @@ -411,7 +424,7 @@ static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr) } } else { - /* same as above but different rect sizes */ + /* Same as above but different rectangle sizes. */ const depth_t *curr = rect_curr->buf + ps->cache.sub_rect.start; for (uint i = 0; i < ps->cache.sub_rect.span_len; i++) { const depth_t *curr_end = curr + ps->cache.sub_rect.span; @@ -424,7 +437,7 @@ static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr) #undef EVAL_TEST - /* ensure enough space */ + /* Ensure enough space. */ if (UNLIKELY(ps->all.hits_len == ps->all.hits_len_alloc)) { ps->all.hits_len_alloc += ALLOC_DEPTHS; ps->all.hits = MEM_reallocN(ps->all.hits, ps->all.hits_len_alloc * sizeof(*ps->all.hits)); @@ -439,7 +452,7 @@ static void gpu_select_load_id_pass_nearest(const DepthBufCache *rect_prev, { GPUPickState *ps = &g_pick_state; const uint id = rect_curr->id; - /* keep track each pixels ID in 'nearest.rect_id' */ + /* Keep track each pixels ID in `nearest.rect_id`. */ if (id != SELECT_ID_NONE) { uint *id_ptr = ps->nearest.rect_id; @@ -483,8 +496,8 @@ bool gpu_select_pick_load_id(uint id, bool end) { GPUPickState *ps = &g_pick_state; - if (ps->gl.is_init) { - if (id == ps->gl.prev_id && !end) { + if (ps->gpu.is_init) { + if (id == ps->gpu.prev_id && !end) { /* No need to read if we are still drawing for the same id since * all these depths will be merged / de-duplicated in the end. */ return true; @@ -493,21 +506,21 @@ bool gpu_select_pick_load_id(uint id, bool end) const uint rect_len = ps->src.rect_len; GPUFrameBuffer *fb = GPU_framebuffer_active_get(); GPU_framebuffer_read_depth( - fb, UNPACK4(ps->gl.clip_readpixels), GPU_DATA_UINT, ps->gl.rect_depth_test->buf); + fb, UNPACK4(ps->gpu.clip_readpixels), GPU_DATA_UINT, ps->gpu.rect_depth_test->buf); /* Perform initial check since most cases the array remains unchanged. */ bool do_pass = false; if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { - if (depth_buf_rect_depth_any(ps->gl.rect_depth_test, rect_len)) { - ps->gl.rect_depth_test->id = ps->gl.prev_id; - gpu_select_load_id_pass_all(ps->gl.rect_depth_test); + if (depth_buf_rect_depth_any(ps->gpu.rect_depth_test, rect_len)) { + ps->gpu.rect_depth_test->id = ps->gpu.prev_id; + gpu_select_load_id_pass_all(ps->gpu.rect_depth_test); do_pass = true; } } else { - if (depth_buf_rect_depth_any_filled(ps->gl.rect_depth, ps->gl.rect_depth_test, rect_len)) { - ps->gl.rect_depth_test->id = ps->gl.prev_id; - gpu_select_load_id_pass_nearest(ps->gl.rect_depth, ps->gl.rect_depth_test); + if (depth_buf_rect_depth_any_filled(ps->gpu.rect_depth, ps->gpu.rect_depth_test, rect_len)) { + ps->gpu.rect_depth_test->id = ps->gpu.prev_id; + gpu_select_load_id_pass_nearest(ps->gpu.rect_depth, ps->gpu.rect_depth_test); do_pass = true; } } @@ -515,11 +528,11 @@ bool gpu_select_pick_load_id(uint id, bool end) if (do_pass) { /* Store depth in cache */ if (ps->use_cache) { - BLI_addtail(&ps->cache.bufs, ps->gl.rect_depth); - ps->gl.rect_depth = depth_buf_malloc(ps->src.rect_len); + BLI_addtail(&ps->cache.bufs, ps->gpu.rect_depth); + ps->gpu.rect_depth = depth_buf_malloc(ps->src.rect_len); } - SWAP(DepthBufCache *, ps->gl.rect_depth, ps->gl.rect_depth_test); + SWAP(DepthBufCache *, ps->gpu.rect_depth, ps->gpu.rect_depth_test); if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { /* (fclem) This is to be on the safe side. I don't know if this is required. */ @@ -533,8 +546,8 @@ bool gpu_select_pick_load_id(uint id, bool end) } } - ps->gl.is_init = true; - ps->gl.prev_id = id; + ps->gpu.is_init = true; + ps->gpu.prev_id = id; return true; } @@ -548,9 +561,9 @@ uint gpu_select_pick_end(void) #endif if (ps->is_cached == false) { - if (ps->gl.is_init) { + if (ps->gpu.is_init) { /* force finishing last pass */ - gpu_select_pick_load_id(ps->gl.prev_id, true); + gpu_select_pick_load_id(ps->gpu.prev_id, true); } GPU_write_mask(ps->write_mask); GPU_depth_test(ps->depth_test); @@ -559,39 +572,39 @@ uint gpu_select_pick_end(void) GPU_debug_group_end(); - /* assign but never free directly since it may be in cache */ + /* Assign but never free directly since it may be in cache. */ DepthBufCache *rect_depth_final; /* Store depth in cache */ if (ps->use_cache && !ps->is_cached) { - BLI_addtail(&ps->cache.bufs, ps->gl.rect_depth); - ps->gl.rect_depth = NULL; + BLI_addtail(&ps->cache.bufs, ps->gpu.rect_depth); + ps->gpu.rect_depth = NULL; rect_depth_final = ps->cache.bufs.last; } else if (ps->is_cached) { rect_depth_final = ps->cache.bufs.last; } else { - /* common case, no cache */ - rect_depth_final = ps->gl.rect_depth; + /* Common case, no cache. */ + rect_depth_final = ps->gpu.rect_depth; } - uint maxhits = g_pick_state.bufsize; + uint maxhits = g_pick_state.buffer_len; DepthID *depth_data; uint depth_data_len = 0; if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { depth_data = ps->all.hits; depth_data_len = ps->all.hits_len; - /* move ownership */ + /* Move ownership. */ ps->all.hits = NULL; ps->all.hits_len = 0; ps->all.hits_len_alloc = 0; } else { - /* GPU_SELECT_PICK_NEAREST */ + /* #GPU_SELECT_PICK_NEAREST */ - /* Over alloc (unlikely we have as many depths as pixels) */ + /* Over allocate (unlikely we have as many depths as pixels). */ uint depth_data_len_first_pass = 0; depth_data = MEM_mallocN(ps->dst.rect_len * sizeof(*depth_data), __func__); @@ -624,7 +637,7 @@ uint gpu_select_pick_end(void) } } else { - /* same as above but different rect sizes */ + /* Same as above but different rectangle sizes. */ uint i_src = ps->cache.sub_rect.start, i_dst = 0; for (uint j = 0; j < ps->cache.sub_rect.span_len; j++) { const uint i_src_end = i_src + ps->cache.sub_rect.span; @@ -640,7 +653,7 @@ uint gpu_select_pick_end(void) qsort(depth_data, depth_data_len_first_pass, sizeof(DepthID), depth_id_cmp); - /* Sort by ID's then keep the best depth for each ID */ + /* Sort by ID's then keep the best depth for each ID. */ depth_data_len = 0; { DepthID *depth_last = NULL; @@ -657,25 +670,22 @@ uint gpu_select_pick_end(void) } /* Finally sort each unique (id, depth) pair by depth - * so the final hit-list is sorted by depth (nearest first) */ + * so the final hit-list is sorted by depth (nearest first). */ uint hits = 0; if (depth_data_len > maxhits) { hits = (uint)-1; } else { - /* leave sorting up to the caller */ + /* Leave sorting up to the caller. */ qsort(depth_data, depth_data_len, sizeof(DepthID), depth_cmp); for (uint i = 0; i < depth_data_len; i++) { #ifdef DEBUG_PRINT printf(" hit: %u: depth %u\n", depth_data[i].id, depth_data[i].depth); #endif - /* first 3 are dummy values */ - g_pick_state.buffer[hits][0] = 1; - g_pick_state.buffer[hits][1] = 0x0; /* depth_data[i].depth; */ /* unused */ - g_pick_state.buffer[hits][2] = 0x0; /* z-far is currently never used. */ - g_pick_state.buffer[hits][3] = depth_data[i].id; + g_pick_state.buffer[hits].depth = depth_data[i].depth; + g_pick_state.buffer[hits].id = depth_data[i].id; hits++; } BLI_assert(hits < maxhits); @@ -683,8 +693,8 @@ uint gpu_select_pick_end(void) MEM_freeN(depth_data); - MEM_SAFE_FREE(ps->gl.rect_depth); - MEM_SAFE_FREE(ps->gl.rect_depth_test); + MEM_SAFE_FREE(ps->gpu.rect_depth); + MEM_SAFE_FREE(ps->gpu.rect_depth_test); if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { /* 'hits' already freed as 'depth_data' */ @@ -744,8 +754,8 @@ void gpu_select_pick_cache_load_id(void) #endif LISTBASE_FOREACH (DepthBufCache *, rect_depth, &ps->cache.bufs) { if (rect_depth->next != NULL) { - /* we know the buffers differ, but this sub-region may not. - * double check before adding an id-pass */ + /* We know the buffers differ, but this sub-region may not. + * Double check before adding an id-pass. */ if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { if (depth_buf_subrect_depth_any(rect_depth->next, &ps->cache.sub_rect)) { gpu_select_load_id_pass_all(rect_depth->next); diff --git a/source/blender/gpu/intern/gpu_select_private.h b/source/blender/gpu/intern/gpu_select_private.h index e5a84a037a6..0ec9f083c07 100644 --- a/source/blender/gpu/intern/gpu_select_private.h +++ b/source/blender/gpu/intern/gpu_select_private.h @@ -31,7 +31,10 @@ extern "C" { /* gpu_select_pick */ -void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, char mode); +void gpu_select_pick_begin(GPUSelectResult *buffer, + uint buffer_len, + const rcti *input, + eGPUSelectMode mode); bool gpu_select_pick_load_id(uint id, bool end); uint gpu_select_pick_end(void); @@ -46,7 +49,7 @@ void gpu_select_pick_cache_load_id(void); /* gpu_select_sample_query */ void gpu_select_query_begin( - uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits); + GPUSelectResult *buffer, uint buffer_len, const rcti *input, eGPUSelectMode mode, int oldhits); bool gpu_select_query_load_id(uint id); uint gpu_select_query_end(void); diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc index a430d4a9d62..7559358aaca 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.cc +++ b/source/blender/gpu/intern/gpu_select_sample_query.cc @@ -48,22 +48,22 @@ using namespace blender; using namespace blender::gpu; struct GPUSelectQueryState { - /* Tracks whether a query has been issued so that gpu_load_id can end the previous one. */ + /** Tracks whether a query has been issued so that gpu_load_id can end the previous one. */ bool query_issued; - /* GPU queries abstraction. Contains an array of queries. */ + /** GPU queries abstraction. Contains an array of queries. */ QueryPool *queries; - /* Array holding the id corresponding id to each query. */ + /** Array holding the id corresponding id to each query. */ Vector<uint> *ids; - /* Cache on initialization. */ - uint (*buffer)[4]; - /* Buffer size (stores number of integers, for actual size multiply by `sizeof(int)`). */ - uint bufsize; - /* Mode of operation. */ - char mode; + /** Cache on initialization. */ + GPUSelectResult *buffer; + /** The capacity of the `buffer` array. */ + uint buffer_len; + /** Mode of operation. */ + eGPUSelectMode mode; uint index; int oldhits; - /* Previous state to restore after drawing. */ + /** Previous state to restore after drawing. */ int viewport[4]; int scissor[4]; eGPUWriteMask write_mask; @@ -72,14 +72,17 @@ struct GPUSelectQueryState { static GPUSelectQueryState g_query_state = {false}; -void gpu_select_query_begin( - uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits) +void gpu_select_query_begin(GPUSelectResult *buffer, + uint buffer_len, + const rcti *input, + const eGPUSelectMode mode, + int oldhits) { GPU_debug_group_begin("Selection Queries"); g_query_state.query_issued = false; - g_query_state.bufsize = bufsize; g_query_state.buffer = buffer; + g_query_state.buffer_len = buffer_len; g_query_state.mode = mode; g_query_state.index = 0; g_query_state.oldhits = oldhits; @@ -111,7 +114,7 @@ void gpu_select_query_begin( /* occlusion queries operates on fragments that pass tests and since we are interested on all * objects in the view frustum independently of their order, we need to disable the depth test */ if (mode == GPU_SELECT_ALL) { - /* glQueries on Windows+Intel drivers only works with depth testing turned on. + /* #glQueries on Windows+Intel drivers only works with depth testing turned on. * See T62947 for details */ GPU_depth_test(GPU_DEPTH_ALWAYS); GPU_depth_mask(true); @@ -138,10 +141,11 @@ bool gpu_select_query_load_id(uint id) g_query_state.query_issued = true; if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS) { - /* Second pass should never run if first pass fails, can read past 'bufsize' in this case. */ + /* Second pass should never run if first pass fails, + * can read past `buffer_len` in this case. */ BLI_assert(g_query_state.oldhits != -1); if (g_query_state.index < g_query_state.oldhits) { - if (g_query_state.buffer[g_query_state.index][3] == id) { + if (g_query_state.buffer[g_query_state.index].id == id) { g_query_state.index++; return true; } @@ -154,7 +158,7 @@ bool gpu_select_query_load_id(uint id) uint gpu_select_query_end() { uint hits = 0; - const uint maxhits = g_query_state.bufsize; + const uint maxhits = g_query_state.buffer_len; if (g_query_state.query_issued) { g_query_state.queries->end_query(); @@ -168,10 +172,8 @@ uint gpu_select_query_end() if (result[i] != 0) { if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) { if (hits < maxhits) { - g_query_state.buffer[hits][0] = 1; - g_query_state.buffer[hits][1] = 0xFFFF; - g_query_state.buffer[hits][2] = 0xFFFF; - g_query_state.buffer[hits][3] = ids[i]; + g_query_state.buffer[hits].depth = 0xFFFF; + g_query_state.buffer[hits].id = ids[i]; hits++; } else { @@ -183,9 +185,8 @@ uint gpu_select_query_end() int j; /* search in buffer and make selected object first */ for (j = 0; j < g_query_state.oldhits; j++) { - if (g_query_state.buffer[j][3] == ids[i]) { - g_query_state.buffer[j][1] = 0; - g_query_state.buffer[j][2] = 0; + if (g_query_state.buffer[j].id == ids[i]) { + g_query_state.buffer[j].depth = 0; } } break; diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 3b41e804fd4..7db34917ad7 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -26,6 +26,7 @@ #include "BLI_string_utils.h" #include "GPU_capabilities.h" +#include "GPU_debug.h" #include "GPU_matrix.h" #include "GPU_platform.h" @@ -249,6 +250,23 @@ GPUShader *GPU_shader_create_compute(const char *computecode, shname); } +const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name) +{ + return gpu_shader_create_info_get(info_name); +} + +GPUShader *GPU_shader_create_from_info_name(const char *info_name) +{ + using namespace blender::gpu::shader; + const GPUShaderCreateInfo *_info = gpu_shader_create_info_get(info_name); + const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(_info); + if (!info.do_static_compilation_) { + printf("Warning: Trying to compile \"%s\" which was not marked for static compilation.\n", + info.name_.c_str()); + } + return GPU_shader_create_from_info(_info); +} + GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) { using namespace blender::gpu::shader; @@ -256,6 +274,8 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) const_cast<ShaderCreateInfo &>(info).finalize(); + GPU_debug_group_begin(GPU_DEBUG_SHADER_COMPILATION_GROUP); + /* At least a vertex shader and a fragment shader are required, or only a compute shader. */ if (info.compute_source_.is_empty()) { if (info.vertex_source_.is_empty()) { @@ -284,22 +304,23 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) std::string defines = shader->defines_declare(info); std::string resources = shader->resources_declare(info); - char *shader_shared_utils = nullptr; defines += "#define USE_GPU_SHADER_CREATE_INFO\n"; - Vector<char *> typedefs; - for (auto filename : info.typedef_sources_) { - typedefs.append(gpu_shader_dependency_get_source(filename.c_str())); + Vector<const char *> typedefs; + if (!info.typedef_sources_.is_empty() || !info.typedef_source_generated.empty()) { + typedefs.append(gpu_shader_dependency_get_source("GPU_shader_shared_utils.h").c_str()); + } + if (!info.typedef_source_generated.empty()) { + typedefs.append(info.typedef_source_generated.c_str()); } - if (!typedefs.is_empty()) { - shader_shared_utils = gpu_shader_dependency_get_source("gpu_shader_shared_utils.h"); + for (auto filename : info.typedef_sources_) { + typedefs.append(gpu_shader_dependency_get_source(filename).c_str()); } if (!info.vertex_source_.is_empty()) { - uint32_t builtins = 0; + auto code = gpu_shader_dependency_get_resolved_source(info.vertex_source_); std::string interface = shader->vertex_interface_declare(info); - char *code = gpu_shader_dependency_get_resolved_source(info.vertex_source_.c_str(), &builtins); Vector<const char *> sources; standard_defines(sources); @@ -308,26 +329,19 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) sources.append("#define USE_GEOMETRY_SHADER\n"); } sources.append(defines.c_str()); - if (!typedefs.is_empty()) { - sources.append(shader_shared_utils); - } - for (auto *types : typedefs) { - sources.append(types); - } + sources.extend(typedefs); sources.append(resources.c_str()); sources.append(interface.c_str()); - sources.append(code); + sources.extend(code); + sources.extend(info.dependencies_generated); + sources.append(info.vertex_source_generated.c_str()); shader->vertex_shader_from_glsl(sources); - - free(code); } if (!info.fragment_source_.is_empty()) { - uint32_t builtins = 0; + auto code = gpu_shader_dependency_get_resolved_source(info.fragment_source_); std::string interface = shader->fragment_interface_declare(info); - char *code = gpu_shader_dependency_get_resolved_source(info.fragment_source_.c_str(), - &builtins); Vector<const char *> sources; standard_defines(sources); @@ -336,84 +350,57 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) sources.append("#define USE_GEOMETRY_SHADER\n"); } sources.append(defines.c_str()); - if (!typedefs.is_empty()) { - sources.append(shader_shared_utils); - } - for (auto *types : typedefs) { - sources.append(types); - } + sources.extend(typedefs); sources.append(resources.c_str()); sources.append(interface.c_str()); - sources.append(code); + sources.extend(code); + sources.extend(info.dependencies_generated); + sources.append(info.fragment_source_generated.c_str()); shader->fragment_shader_from_glsl(sources); - - free(code); } if (!info.geometry_source_.is_empty()) { - uint32_t builtins = 0; - std::string interface = shader->geometry_interface_declare(info); + auto code = gpu_shader_dependency_get_resolved_source(info.geometry_source_); std::string layout = shader->geometry_layout_declare(info); - char *code = gpu_shader_dependency_get_resolved_source(info.geometry_source_.c_str(), - &builtins); + std::string interface = shader->geometry_interface_declare(info); Vector<const char *> sources; standard_defines(sources); sources.append("#define GPU_GEOMETRY_SHADER\n"); sources.append(defines.c_str()); - if (!typedefs.is_empty()) { - sources.append(shader_shared_utils); - } - for (auto *types : typedefs) { - sources.append(types); - } + sources.extend(typedefs); sources.append(resources.c_str()); sources.append(layout.c_str()); sources.append(interface.c_str()); - sources.append(code); + sources.extend(code); shader->geometry_shader_from_glsl(sources); - - free(code); } if (!info.compute_source_.is_empty()) { - uint32_t builtins = 0; - char *code = gpu_shader_dependency_get_resolved_source(info.compute_source_.c_str(), - &builtins); + auto code = gpu_shader_dependency_get_resolved_source(info.compute_source_); + std::string layout = shader->compute_layout_declare(info); Vector<const char *> sources; standard_defines(sources); sources.append("#define GPU_COMPUTE_SHADER\n"); sources.append(defines.c_str()); - if (!typedefs.is_empty()) { - sources.append(shader_shared_utils); - } - for (auto *types : typedefs) { - sources.append(types); - } + sources.extend(typedefs); sources.append(resources.c_str()); - sources.append(code); + sources.append(layout.c_str()); + sources.extend(code); shader->compute_shader_from_glsl(sources); - - free(code); - } - - for (auto *types : typedefs) { - free(types); - } - - if (shader_shared_utils) { - free(shader_shared_utils); } if (!shader->finalize(&info)) { delete shader; + GPU_debug_group_end(); return nullptr; } + GPU_debug_group_end(); return wrap(shader); } diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index 40e54ab4394..716d6e33a81 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -39,6 +39,8 @@ #include "DNA_userdef_types.h" +#include "NOD_shader.h" + #include "DRW_engine.h" #include "bmesh.h" @@ -167,7 +169,7 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *UNUSED(mesh), /** \name Stubs of BKE_material.h * \{ */ -void BKE_material_defaults_free_gpu(void) +void BKE_material_defaults_free_gpu() { /* This function is reachable via GPU_exit. */ } diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 9c8dbf8401f..6b1163fdc78 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -41,9 +41,6 @@ #include "GPU_texture.h" #include "GPU_uniform_buffer.h" -/* TODO(jbakker): Need a better way to retrieve create_infos. */ -#include "gpu_shader_create_info_private.hh" - /* Adjust these constants as needed. */ #define MAX_DEFINE_LENGTH 256 #define MAX_EXT_DEFINE_LENGTH 512 @@ -366,7 +363,7 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, /* common case */ if (sh_cfg == GPU_SHADER_CFG_DEFAULT) { if (stages->create_info != NULL) { - *sh_p = GPU_shader_create_from_info(gpu_shader_create_info_get(stages->create_info)); + *sh_p = GPU_shader_create_from_info_name(stages->create_info); } else { *sh_p = GPU_shader_create_from_arrays_named( @@ -392,8 +389,7 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR)); /* In rare cases geometry shaders calculate clipping themselves. */ if (stages->clipped_create_info != NULL) { - *sh_p = GPU_shader_create_from_info( - gpu_shader_create_info_get(stages->clipped_create_info)); + *sh_p = GPU_shader_create_from_info_name(stages->clipped_create_info); } else { const char *world_clip_lib = datatoc_gpu_shader_cfg_world_clip_lib_glsl; diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index b8ae6bc3868..fe0304828c2 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -27,11 +27,15 @@ #include "BLI_set.hh" #include "BLI_string_ref.hh" +#include "GPU_capabilities.h" +#include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_texture.h" #include "gpu_shader_create_info.hh" #include "gpu_shader_create_info_private.hh" +#include "gpu_shader_dependency_private.h" +#include "gpu_shader_private.hh" #undef GPU_SHADER_INTERFACE_INFO #undef GPU_SHADER_CREATE_INFO @@ -51,6 +55,8 @@ void ShaderCreateInfo::finalize() } finalized_ = true; + Set<StringRefNull> deps_merged; + for (auto &info_name : additional_infos_) { const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>( gpu_shader_create_info_get(info_name.c_str())); @@ -70,42 +76,81 @@ void ShaderCreateInfo::finalize() batch_resources_.extend(info.batch_resources_); pass_resources_.extend(info.pass_resources_); - typedef_sources_.extend(info.typedef_sources_); + typedef_sources_.extend_non_duplicates(info.typedef_sources_); validate(info); - if (info.local_group_size_[0] != 0) { - BLI_assert(local_group_size_[0] == 0); - for (int i = 0; i < 3; i++) { - local_group_size_[i] = info.local_group_size_[i]; + auto assert_no_overlap = [&](const bool test, const StringRefNull error) { + if (!test) { + std::cout << name_ << ": Validation failed while merging " << info.name_ << " : "; + std::cout << error << std::endl; + BLI_assert(0); } + }; + + if (!deps_merged.add(info.name_)) { + assert_no_overlap(false, "additional info already merged via another info"); + } + + if (info.compute_layout_.local_size_x != -1) { + assert_no_overlap(compute_layout_.local_size_x == -1, "Compute layout already defined"); + compute_layout_ = info.compute_layout_; } + if (!info.vertex_source_.is_empty()) { - BLI_assert(vertex_source_.is_empty()); + assert_no_overlap(vertex_source_.is_empty(), "Vertex source already existing"); vertex_source_ = info.vertex_source_; } if (!info.geometry_source_.is_empty()) { - BLI_assert(geometry_source_.is_empty()); + assert_no_overlap(geometry_source_.is_empty(), "Geometry source already existing"); geometry_source_ = info.geometry_source_; geometry_layout_ = info.geometry_layout_; } if (!info.fragment_source_.is_empty()) { - BLI_assert(fragment_source_.is_empty()); + assert_no_overlap(fragment_source_.is_empty(), "Fragment source already existing"); fragment_source_ = info.fragment_source_; } if (!info.compute_source_.is_empty()) { - BLI_assert(compute_source_.is_empty()); + assert_no_overlap(compute_source_.is_empty(), "Compute source already existing"); compute_source_ = info.compute_source_; } do_static_compilation_ = do_static_compilation_ || info.do_static_compilation_; } + + if (auto_resource_location_) { + int images = 0, samplers = 0, ubos = 0, ssbos = 0; + + auto set_resource_slot = [&](Resource &res) { + switch (res.bind_type) { + case Resource::BindType::UNIFORM_BUFFER: + res.slot = ubos++; + break; + case Resource::BindType::STORAGE_BUFFER: + res.slot = ssbos++; + break; + case Resource::BindType::SAMPLER: + res.slot = samplers++; + break; + case Resource::BindType::IMAGE: + res.slot = images++; + break; + } + }; + + for (auto &res : batch_resources_) { + set_resource_slot(res); + } + for (auto &res : pass_resources_) { + set_resource_slot(res); + } + } } void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info) { - { - /* Check same bindpoints usage in OGL. */ + if (!auto_resource_location_) { + /* Check same bind-points usage in OGL. */ Set<int> images, samplers, ubos, ssbos; auto register_resource = [&](const Resource &res) -> bool { @@ -124,26 +169,26 @@ void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info) }; auto print_error_msg = [&](const Resource &res) { - std::cerr << name_ << ": Validation failed : Overlapping "; + std::cout << name_ << ": Validation failed : Overlapping "; switch (res.bind_type) { case Resource::BindType::UNIFORM_BUFFER: - std::cerr << "Uniform Buffer " << res.uniformbuf.name; + std::cout << "Uniform Buffer " << res.uniformbuf.name; break; case Resource::BindType::STORAGE_BUFFER: - std::cerr << "Storage Buffer " << res.storagebuf.name; + std::cout << "Storage Buffer " << res.storagebuf.name; break; case Resource::BindType::SAMPLER: - std::cerr << "Sampler " << res.sampler.name; + std::cout << "Sampler " << res.sampler.name; break; case Resource::BindType::IMAGE: - std::cerr << "Image " << res.image.name; + std::cout << "Image " << res.image.name; break; default: - std::cerr << "Unknown Type"; + std::cout << "Unknown Type"; break; } - std::cerr << " (" << res.slot << ") while merging " << other_info.name_ << std::endl; + std::cout << " (" << res.slot << ") while merging " << other_info.name_ << std::endl; }; for (auto &res : batch_resources_) { @@ -159,7 +204,7 @@ void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info) } } { - /* TODO(fclem) Push constant validation. */ + /* TODO(@fclem): Push constant validation. */ } } @@ -194,6 +239,22 @@ void gpu_shader_create_info_init() # include "gpu_shader_baked.hh" #endif + /* WORKAROUND: Replace draw_mesh info with the legacy one for systems that have problems with UBO + * indexing. */ + if (GPU_type_matches(GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_ANY, GPU_DRIVER_ANY) || + GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY) || GPU_crappy_amd_driver()) { + draw_modelmat = draw_modelmat_legacy; + } + + for (ShaderCreateInfo *info : g_create_infos->values()) { + if (info->do_static_compilation_) { + info->builtins_ |= gpu_shader_dependency_get_builtins(info->vertex_source_); + info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_); + info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_); + info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_); + } + } + /* TEST */ // gpu_shader_create_info_compile_all(); } @@ -213,25 +274,82 @@ void gpu_shader_create_info_exit() bool gpu_shader_create_info_compile_all() { + using namespace blender::gpu; + int success = 0; + int total = 0; for (ShaderCreateInfo *info : g_create_infos->values()) { if (info->do_static_compilation_) { - // printf("Compiling %s: ... \n", info->name_.c_str()); + total++; GPUShader *shader = GPU_shader_create_from_info( reinterpret_cast<const GPUShaderCreateInfo *>(info)); if (shader == nullptr) { printf("Compilation %s Failed\n", info->name_.c_str()); - return false; + } + else { + success++; + +#if 0 /* TODO(fclem): This is too verbose for now. Make it a cmake option. */ + /* Test if any resource is optimized out and print a warning if that's the case. */ + /* TODO(fclem): Limit this to OpenGL backend. */ + const ShaderInterface *interface = unwrap(shader)->interface; + + blender::Vector<ShaderCreateInfo::Resource> all_resources; + all_resources.extend(info->pass_resources_); + all_resources.extend(info->batch_resources_); + + for (ShaderCreateInfo::Resource &res : all_resources) { + blender::StringRefNull name = ""; + const ShaderInput *input = nullptr; + + switch (res.bind_type) { + case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER: + input = interface->ubo_get(res.slot); + name = res.uniformbuf.name; + break; + case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER: + input = interface->ssbo_get(res.slot); + name = res.storagebuf.name; + break; + case ShaderCreateInfo::Resource::BindType::SAMPLER: + input = interface->texture_get(res.slot); + name = res.sampler.name; + break; + case ShaderCreateInfo::Resource::BindType::IMAGE: + input = interface->texture_get(res.slot); + name = res.image.name; + break; + } + + if (input == nullptr) { + std::cout << "Error: " << info->name_; + std::cout << ": Resource « " << name << " » not found in the shader interface\n"; + } + else if (input->location == -1) { + std::cout << "Warning: " << info->name_; + std::cout << ": Resource « " << name << " » is optimized out\n"; + } + } +#endif } GPU_shader_free(shader); - // printf("Success\n"); } } - return true; + printf("===============================\n"); + printf("Shader Test compilation result: \n"); + printf("%d Total\n", total); + printf("%d Passed\n", success); + printf("%d Failed\n", total - success); + printf("===============================\n"); + return success == total; } /* Runtime create infos are not registered in the dictionary and cannot be searched. */ const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name) { + if (g_create_infos->contains(info_name) == false) { + printf("Error: Cannot find shader create info named \"%s\"\n", info_name); + return nullptr; + } ShaderCreateInfo *info = g_create_infos->lookup(info_name); return reinterpret_cast<const GPUShaderCreateInfo *>(info); } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index c7b3ac5ca37..dafa741977c 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -30,8 +30,11 @@ #include "BLI_string_ref.hh" #include "BLI_vector.hh" +#include "GPU_material.h" #include "GPU_texture.h" +#include <iostream> + namespace blender::gpu::shader { #ifndef GPU_SHADER_CREATE_INFO @@ -62,13 +65,74 @@ enum class Type { BOOL, }; +/* All of these functions is a bit out of place */ +static inline Type to_type(const eGPUType type) +{ + switch (type) { + case GPU_FLOAT: + return Type::FLOAT; + case GPU_VEC2: + return Type::VEC2; + case GPU_VEC3: + return Type::VEC3; + case GPU_VEC4: + return Type::VEC4; + case GPU_MAT3: + return Type::MAT3; + case GPU_MAT4: + return Type::MAT4; + default: + BLI_assert_msg(0, "Error: Cannot convert eGPUType to shader::Type."); + return Type::FLOAT; + } +} + +static inline std::ostream &operator<<(std::ostream &stream, const Type type) +{ + switch (type) { + case Type::FLOAT: + return stream << "float"; + case Type::VEC2: + return stream << "vec2"; + case Type::VEC3: + return stream << "vec3"; + case Type::VEC4: + return stream << "vec4"; + case Type::MAT3: + return stream << "mat3"; + case Type::MAT4: + return stream << "mat4"; + default: + BLI_assert(0); + return stream; + } +} + +static inline std::ostream &operator<<(std::ostream &stream, const eGPUType type) +{ + switch (type) { + case GPU_CLOSURE: + return stream << "Closure"; + default: + return stream << to_type(type); + } +} + enum class BuiltinBits { - /** Allow getting barycentic coordinates inside the fragment shader. NOTE: emulated on OpenGL. */ + NONE = 0, + /** + * Allow getting barycentric coordinates inside the fragment shader. + * \note Emulated on OpenGL. + */ BARYCENTRIC_COORD = (1 << 0), FRAG_COORD = (1 << 2), FRONT_FACING = (1 << 4), GLOBAL_INVOCATION_ID = (1 << 5), INSTANCE_ID = (1 << 6), + /** + * Allow setting the target layer when the output is a layered frame-buffer. + * \note Emulated through geometry shader on older hardware. + */ LAYER = (1 << 7), LOCAL_INVOCATION_ID = (1 << 8), LOCAL_INVOCATION_INDEX = (1 << 9), @@ -122,10 +186,13 @@ enum class ImageType { /* Storage qualifiers. */ enum class Qualifier { - RESTRICT = (1 << 0), - READ_ONLY = (1 << 1), - WRITE_ONLY = (1 << 2), - QUALIFIER_MAX = (WRITE_ONLY << 1) - 1, + /** Restrict flag is set by default. Unless specified otherwise. */ + NO_RESTRICT = (1 << 0), + READ = (1 << 1), + WRITE = (1 << 2), + /** Shorthand version of combined flags. */ + READ_WRITE = READ | WRITE, + QUALIFIER_MAX = (WRITE << 1) - 1, }; ENUM_OPERATORS(Qualifier, Qualifier::QUALIFIER_MAX); @@ -218,18 +285,45 @@ struct ShaderCreateInfo { bool do_static_compilation_ = false; /** If true, all additionally linked create info will be merged into this one. */ bool finalized_ = false; + /** If true, all resources will have an automatic location assigned. */ + bool auto_resource_location_ = false; /** * Maximum length of all the resource names including each null terminator. * Only for names used by gpu::ShaderInterface. */ size_t interface_names_size_ = 0; - /** Only for compute shaders. */ - int local_group_size_[3] = {0, 0, 0}; + /** Manually set builtins. */ + BuiltinBits builtins_ = BuiltinBits::NONE; + /** Manually set generated code. */ + std::string vertex_source_generated = ""; + std::string fragment_source_generated = ""; + std::string typedef_source_generated = ""; + /** Manually set generated dependencies. */ + Vector<const char *, 0> dependencies_generated; + +#define TEST_EQUAL(a, b, _member) \ + if (!((a)._member == (b)._member)) { \ + return false; \ + } + +#define TEST_VECTOR_EQUAL(a, b, _vector) \ + TEST_EQUAL(a, b, _vector.size()); \ + for (auto i : _vector.index_range()) { \ + TEST_EQUAL(a, b, _vector[i]); \ + } struct VertIn { int index; Type type; StringRefNull name; + + bool operator==(const VertIn &b) + { + TEST_EQUAL(*this, b, index); + TEST_EQUAL(*this, b, type); + TEST_EQUAL(*this, b, name); + return true; + } }; Vector<VertIn> vertex_inputs_; @@ -239,14 +333,47 @@ struct ShaderCreateInfo { PrimitiveOut primitive_out; /** Set to -1 by default to check if used. */ int max_vertices = -1; + + bool operator==(const GeometryStageLayout &b) + { + TEST_EQUAL(*this, b, primitive_in); + TEST_EQUAL(*this, b, invocations); + TEST_EQUAL(*this, b, primitive_out); + TEST_EQUAL(*this, b, max_vertices); + return true; + } }; GeometryStageLayout geometry_layout_; + struct ComputeStageLayout { + int local_size_x = -1; + int local_size_y = -1; + int local_size_z = -1; + + bool operator==(const ComputeStageLayout &b) + { + TEST_EQUAL(*this, b, local_size_x); + TEST_EQUAL(*this, b, local_size_y); + TEST_EQUAL(*this, b, local_size_z); + return true; + } + }; + ComputeStageLayout compute_layout_; + struct FragOut { int index; Type type; DualBlend blend; StringRefNull name; + + bool operator==(const FragOut &b) + { + TEST_EQUAL(*this, b, index); + TEST_EQUAL(*this, b, type); + TEST_EQUAL(*this, b, blend); + TEST_EQUAL(*this, b, name); + return true; + } }; Vector<FragOut> fragment_outputs_; @@ -292,6 +419,35 @@ struct ShaderCreateInfo { }; Resource(BindType type, int _slot) : bind_type(type), slot(_slot){}; + + bool operator==(const Resource &b) + { + TEST_EQUAL(*this, b, bind_type); + TEST_EQUAL(*this, b, slot); + switch (bind_type) { + case UNIFORM_BUFFER: + TEST_EQUAL(*this, b, uniformbuf.type_name); + TEST_EQUAL(*this, b, uniformbuf.name); + break; + case STORAGE_BUFFER: + TEST_EQUAL(*this, b, storagebuf.qualifiers); + TEST_EQUAL(*this, b, storagebuf.type_name); + TEST_EQUAL(*this, b, storagebuf.name); + break; + case SAMPLER: + TEST_EQUAL(*this, b, sampler.type); + TEST_EQUAL(*this, b, sampler.sampler); + TEST_EQUAL(*this, b, sampler.name); + break; + case IMAGE: + TEST_EQUAL(*this, b, image.format); + TEST_EQUAL(*this, b, image.type); + TEST_EQUAL(*this, b, image.qualifiers); + TEST_EQUAL(*this, b, image.name); + break; + } + return true; + } }; /** * Resources are grouped by frequency of change. @@ -305,10 +461,17 @@ struct ShaderCreateInfo { Vector<StageInterfaceInfo *> geometry_out_interfaces_; struct PushConst { - int index; Type type; StringRefNull name; int array_size; + + bool operator==(const PushConst &b) + { + TEST_EQUAL(*this, b, type); + TEST_EQUAL(*this, b, name); + TEST_EQUAL(*this, b, array_size); + return true; + } }; Vector<PushConst> push_constants_; @@ -366,7 +529,20 @@ struct ShaderCreateInfo { return *(Self *)this; } - /* Only needed if geometry shader is enabled. */ + Self &local_group_size(int local_size_x = -1, int local_size_y = -1, int local_size_z = -1) + { + compute_layout_.local_size_x = local_size_x; + compute_layout_.local_size_y = local_size_y; + compute_layout_.local_size_z = local_size_z; + return *(Self *)this; + } + + /** + * Only needed if geometry shader is enabled. + * IMPORTANT: Input and output instance name will have respectively "_in" and "_out" suffix + * appended in the geometry shader IF AND ONLY IF the vertex_out interface instance name matches + * the geometry_out interface instance name. + */ Self &geometry_out(StageInterfaceInfo &interface) { geometry_out_interfaces_.append(&interface); @@ -481,22 +657,14 @@ struct ShaderCreateInfo { /** \name Push constants * * Data managed by GPUShader. Can be set through uniform functions. Must be less than 128bytes. - * One slot represents 4bytes. Each element needs to have enough empty space left after it. - * example: - * [0] = PUSH_CONSTANT(MAT4, "ModelMatrix"), - * ---- 16 slots occupied by ModelMatrix ---- - * [16] = PUSH_CONSTANT(VEC4, "color"), - * ---- 4 slots occupied by color ---- - * [20] = PUSH_CONSTANT(BOOL, "srgbToggle"), - * The maximum slot is 31. * \{ */ - Self &push_constant(int slot, Type type, StringRefNull name, int array_size = 0) + Self &push_constant(Type type, StringRefNull name, int array_size = 0) { BLI_assert_msg(name.find("[") == -1, "Array syntax is forbidden for push constants." "Use the array_size parameter instead."); - push_constants_.append({slot, type, name, array_size}); + push_constants_.append({type, name, array_size}); interface_names_size_ += name.size() + 1; return *(Self *)this; } @@ -504,20 +672,6 @@ struct ShaderCreateInfo { /** \} */ /* -------------------------------------------------------------------- */ - /** \name Compute shaders Local Group Size - * \{ */ - - Self &local_group_size(int x, int y = 1, int z = 1) - { - local_group_size_[0] = x; - local_group_size_[1] = y; - local_group_size_[2] = z; - return *(Self *)this; - } - - /** \} */ - - /* -------------------------------------------------------------------- */ /** \name Defines * \{ */ @@ -539,6 +693,18 @@ struct ShaderCreateInfo { return *(Self *)this; } + Self &builtins(BuiltinBits builtin) + { + builtins_ |= builtin; + return *(Self *)this; + } + + Self &auto_resource_location(bool value) + { + auto_resource_location_ = value; + return *(Self *)this; + } + /** \} */ /* -------------------------------------------------------------------- */ @@ -551,7 +717,9 @@ struct ShaderCreateInfo { StringRefNull info_name1 = "", StringRefNull info_name2 = "", StringRefNull info_name3 = "", - StringRefNull info_name4 = "") + StringRefNull info_name4 = "", + StringRefNull info_name5 = "", + StringRefNull info_name6 = "") { additional_infos_.append(info_name0); if (!info_name1.is_empty()) { @@ -566,6 +734,12 @@ struct ShaderCreateInfo { if (!info_name4.is_empty()) { additional_infos_.append(info_name4); } + if (!info_name5.is_empty()) { + additional_infos_.append(info_name5); + } + if (!info_name6.is_empty()) { + additional_infos_.append(info_name6); + } return *(Self *)this; } @@ -601,6 +775,77 @@ struct ShaderCreateInfo { void validate(const ShaderCreateInfo &other_info); /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Operators. + * + * \{ */ + + /* Comparison operator for GPUPass cache. We only compare if it will create the same shader code. + * So we do not compare name and some other internal stuff. */ + bool operator==(const ShaderCreateInfo &b) + { + TEST_EQUAL(*this, b, builtins_); + TEST_EQUAL(*this, b, vertex_source_generated); + TEST_EQUAL(*this, b, fragment_source_generated); + TEST_EQUAL(*this, b, typedef_source_generated); + TEST_VECTOR_EQUAL(*this, b, vertex_inputs_); + TEST_EQUAL(*this, b, geometry_layout_); + TEST_EQUAL(*this, b, compute_layout_); + TEST_VECTOR_EQUAL(*this, b, fragment_outputs_); + TEST_VECTOR_EQUAL(*this, b, pass_resources_); + TEST_VECTOR_EQUAL(*this, b, batch_resources_); + TEST_VECTOR_EQUAL(*this, b, vertex_out_interfaces_); + TEST_VECTOR_EQUAL(*this, b, geometry_out_interfaces_); + TEST_VECTOR_EQUAL(*this, b, push_constants_); + TEST_VECTOR_EQUAL(*this, b, typedef_sources_); + TEST_EQUAL(*this, b, vertex_source_); + TEST_EQUAL(*this, b, geometry_source_); + TEST_EQUAL(*this, b, fragment_source_); + TEST_EQUAL(*this, b, compute_source_); + TEST_VECTOR_EQUAL(*this, b, additional_infos_); + TEST_VECTOR_EQUAL(*this, b, defines_); + return true; + } + + /** Debug print */ + friend std::ostream &operator<<(std::ostream &stream, const ShaderCreateInfo &info) + { + /* TODO(@fclem): Complete print. */ + + auto print_resource = [&](const Resource &res) { + switch (res.bind_type) { + case Resource::BindType::UNIFORM_BUFFER: + stream << "UNIFORM_BUFFER(" << res.slot << ", " << res.uniformbuf.name << ")" + << std::endl; + break; + case Resource::BindType::STORAGE_BUFFER: + stream << "STORAGE_BUFFER(" << res.slot << ", " << res.storagebuf.name << ")" + << std::endl; + break; + case Resource::BindType::SAMPLER: + stream << "SAMPLER(" << res.slot << ", " << res.sampler.name << ")" << std::endl; + break; + case Resource::BindType::IMAGE: + stream << "IMAGE(" << res.slot << ", " << res.image.name << ")" << std::endl; + break; + } + }; + + /* TODO(@fclem): Order the resources. */ + for (auto &res : info.batch_resources_) { + print_resource(res); + } + for (auto &res : info.pass_resources_) { + print_resource(res); + } + return stream; + } + + /** \} */ + +#undef TEST_EQUAL +#undef TEST_VECTOR_EQUAL }; } // namespace blender::gpu::shader diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 2d56e1cec05..06ad0817bfb 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -34,7 +34,7 @@ #include "gpu_shader_dependency_private.h" extern "C" { -#define SHADER_SOURCE(datatoc, filename) extern char datatoc[]; +#define SHADER_SOURCE(datatoc, filename, filepath) extern char datatoc[]; #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" #undef SHADER_SOURCE @@ -45,18 +45,20 @@ namespace blender::gpu { using GPUSourceDictionnary = Map<StringRef, struct GPUSource *>; struct GPUSource { + StringRefNull fullpath; StringRefNull filename; StringRefNull source; Vector<GPUSource *> dependencies; bool dependencies_init = false; shader::BuiltinBits builtins = (shader::BuiltinBits)0; + std::string processed_source; - GPUSource(const char *file, const char *datatoc) : filename(file), source(datatoc) + GPUSource(const char *path, const char *file, const char *datatoc) + : fullpath(path), filename(file), source(datatoc) { /* Scan for builtins. */ /* FIXME: This can trigger false positive caused by disabled #if blocks. */ /* TODO(fclem): Could be made faster by scanning once. */ - /* TODO(fclem): BARYCENTRIC_COORD. */ if (source.find("gl_FragCoord", 0)) { builtins |= shader::BuiltinBits::FRAG_COORD; } @@ -69,9 +71,6 @@ struct GPUSource { if (source.find("gl_InstanceID", 0)) { builtins |= shader::BuiltinBits::INSTANCE_ID; } - if (source.find("gl_Layer", 0)) { - builtins |= shader::BuiltinBits::LAYER; - } if (source.find("gl_LocalInvocationID", 0)) { builtins |= shader::BuiltinBits::LOCAL_INVOCATION_ID; } @@ -99,27 +98,216 @@ struct GPUSource { if (source.find("gl_WorkGroupSize", 0)) { builtins |= shader::BuiltinBits::WORK_GROUP_SIZE; } + + /* TODO(fclem): We could do that at compile time. */ + /* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */ + if (filename.endswith(".h") || filename.endswith(".hh")) { + enum_preprocess(); + } }; - void init_dependencies(const GPUSourceDictionnary &dict) + bool is_in_comment(const StringRef &input, int64_t offset) { - if (dependencies_init) { + return (input.rfind("/*", offset) > input.rfind("*/", offset)) || + (input.rfind("//", offset) > input.rfind("\n", offset)); + } + + template<bool check_whole_word = true, bool reversed = false, typename T> + int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0) + { + while (true) { + if constexpr (reversed) { + offset = input.rfind(keyword, offset); + } + else { + offset = input.find(keyword, offset); + } + if (offset > 0) { + if constexpr (check_whole_word) { + /* Fix false positive if something has "enum" as suffix. */ + char previous_char = input[offset - 1]; + if (!(ELEM(previous_char, '\n', '\t', ' ', ':'))) { + offset += (reversed) ? -1 : 1; + continue; + } + } + /* Fix case where the keyword is in a comment. */ + if (is_in_comment(input, offset)) { + offset += (reversed) ? -1 : 1; + continue; + } + } + return offset; + } + } + + void print_error(const StringRef &input, int64_t offset, const StringRef message) + { + std::cout << " error: " << message << "\n"; + StringRef sub = input.substr(0, offset); + int64_t line_number = std::count(sub.begin(), sub.end(), '\n') + 1; + int64_t line_end = input.find("\n", offset); + int64_t line_start = input.rfind("\n", offset) + 1; + int64_t char_number = offset - line_start + 1; + char line_prefix[16] = ""; + SNPRINTF(line_prefix, "%5ld | ", line_number); + + /* TODO Use clog. */ + + std::cout << fullpath << ":" << line_number << ":" << char_number; + + std::cout << " error: " << message << "\n"; + std::cout << line_prefix << input.substr(line_start, line_end - line_start) << "\n"; + std::cout << " | "; + for (int64_t i = 0; i < char_number - 1; i++) { + std::cout << " "; + } + std::cout << "^\n"; + } + + /** + * Transform C,C++ enum declaration into GLSL compatible defines and constants: + * + * \code{.cpp} + * enum eMyEnum : uint32_t { + * ENUM_1 = 0u, + * ENUM_2 = 1u, + * ENUM_3 = 2u, + * }; + * \endcode + * + * or + * + * \code{.c} + * enum eMyEnum { + * ENUM_1 = 0u, + * ENUM_2 = 1u, + * ENUM_3 = 2u, + * }; + * \endcode + * + * becomes + * + * \code{.glsl} + * #define eMyEnum uint + * const uint ENUM_1 = 0u, ENUM_2 = 1u, ENUM_3 = 2u; + * \endcode + * + * IMPORTANT: This has some requirements: + * - Enums needs to have underlying types specified to uint32_t to make them usable in UBO/SSBO. + * - All values needs to be specified using constant literals to avoid compiler differences. + * - All values needs to have the 'u' suffix to avoid GLSL compiler errors. + */ + void enum_preprocess() + { + const StringRefNull input = source; + std::string output; + int64_t cursor = 0; + int64_t last_pos = 0; + const bool is_cpp = filename.endswith(".hh"); + +#define find_keyword find_str<true, false> +#define find_token find_str<false, false> +#define rfind_token find_str<false, true> +#define CHECK(test_value, str, ofs, msg) \ + if ((test_value) == -1) { \ + print_error(str, ofs, msg); \ + cursor++; \ + continue; \ + } + + while (true) { + cursor = find_keyword(input, "enum ", cursor); + if (cursor == -1) { + break; + } + /* Output anything between 2 enums blocks. */ + output += input.substr(last_pos, cursor - last_pos); + + /* Extract enum type name. */ + int64_t name_start = input.find(" ", cursor); + + int64_t values_start = find_token(input, '{', cursor); + CHECK(values_start, input, cursor, "Malformed enum class. Expected \'{\' after typename."); + + StringRef enum_name = input.substr(name_start, values_start - name_start); + if (is_cpp) { + int64_t name_end = find_token(enum_name, ":"); + CHECK(name_end, input, name_start, "Expected \':\' after C++ enum name."); + + int64_t underlying_type = find_keyword(enum_name, "uint32_t", name_end); + CHECK(underlying_type, input, name_start, "C++ enums needs uint32_t underlying type."); + + enum_name = input.substr(name_start, name_end); + } + + output += "#define " + enum_name + " uint\n"; + + /* Extract enum values. */ + int64_t values_end = find_token(input, '}', values_start); + CHECK(values_end, input, cursor, "Malformed enum class. Expected \'}\' after values."); + + /* Skip opening brackets. */ + values_start += 1; + + StringRef enum_values = input.substr(values_start, values_end - values_start); + + /* Really poor check. Could be done better. */ + int64_t token = find_token(enum_values, '{'); + int64_t not_found = (token == -1) ? 0 : -1; + CHECK(not_found, input, values_start + token, "Unexpected \'{\' token inside enum values."); + + /* Do not capture the comma after the last value (if present). */ + int64_t last_equal = rfind_token(enum_values, '=', values_end); + int64_t last_comma = rfind_token(enum_values, ',', values_end); + if (last_comma > last_equal) { + enum_values = input.substr(values_start, last_comma); + } + + output += "const uint " + enum_values; + + int64_t semicolon_found = (input[values_end + 1] == ';') ? 0 : -1; + CHECK(semicolon_found, input, values_end + 1, "Expected \';\' after enum type declaration."); + + /* Skip the curly bracket but not the semicolon. */ + cursor = last_pos = values_end + 1; + } + /* If nothing has been changed, do not allocate processed_source. */ + if (last_pos == 0) { return; } + +#undef find_keyword +#undef find_token +#undef rfind_token + + if (last_pos != 0) { + output += input.substr(last_pos); + } + processed_source = output; + source = processed_source.c_str(); + }; + + /* Return 1 one error. */ + int init_dependencies(const GPUSourceDictionnary &dict) + { + if (dependencies_init) { + return 0; + } dependencies_init = true; int64_t pos = 0; while (true) { pos = source.find("pragma BLENDER_REQUIRE(", pos); if (pos == -1) { - return; + return 0; } - int64_t start = source.find("(", pos) + 1; - int64_t end = source.find(")", pos); + int64_t start = source.find('(', pos) + 1; + int64_t end = source.find(')', pos); if (end == -1) { /* TODO Use clog. */ std::cout << "Error: " << filename << " : Malformed BLENDER_REQUIRE: Missing \")\"." << std::endl; - return; + return 1; } StringRef dependency_name = source.substr(start, end - start); GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr); @@ -127,10 +315,13 @@ struct GPUSource { /* TODO Use clog. */ std::cout << "Error: " << filename << " : Dependency not found \"" << dependency_name << "\"." << std::endl; - return; + return 1; } /* Recursive. */ - dependency_source->init_dependencies(dict); + int result = dependency_source->init_dependencies(dict); + if (result != 0) { + return 1; + } for (auto *dep : dependency_source->dependencies) { dependencies.append_non_duplicates(dep); @@ -141,13 +332,21 @@ struct GPUSource { } /* Returns the final string with all includes done. */ - void build(std::string &str, shader::BuiltinBits &out_builtins) + void build(Vector<const char *> &result) const { for (auto *dep : dependencies) { - out_builtins |= builtins; - str += dep->source; + result.append(dep->source.c_str()); } - str += source; + result.append(source.c_str()); + } + + shader::BuiltinBits builtins_get() const + { + shader::BuiltinBits out_builtins = shader::BuiltinBits::NONE; + for (auto *dep : dependencies) { + out_builtins |= dep->builtins; + } + return out_builtins; } }; @@ -161,15 +360,17 @@ void gpu_shader_dependency_init() { g_sources = new GPUSourceDictionnary(); -#define SHADER_SOURCE(datatoc, filename) \ - g_sources->add_new(filename, new GPUSource(filename, datatoc)); +#define SHADER_SOURCE(datatoc, filename, filepath) \ + g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc)); #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" #undef SHADER_SOURCE + int errors = 0; for (auto *value : g_sources->values()) { - value->init_dependencies(*g_sources); + errors += value->init_dependencies(*g_sources); } + BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting"); } void gpu_shader_dependency_exit() @@ -180,18 +381,47 @@ void gpu_shader_dependency_exit() delete g_sources; } -char *gpu_shader_dependency_get_resolved_source(const char *shader_source_name, uint32_t *builtins) +namespace blender::gpu::shader { + +BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull shader_source_name) { + if (shader_source_name.is_empty()) { + return shader::BuiltinBits::NONE; + } + if (g_sources->contains(shader_source_name) == false) { + std::cout << "Error: Could not find \"" << shader_source_name + << "\" in the list of registered source.\n"; + BLI_assert(0); + return shader::BuiltinBits::NONE; + } GPUSource *source = g_sources->lookup(shader_source_name); - std::string str; - shader::BuiltinBits out_builtins; - source->build(str, out_builtins); - *builtins |= (uint32_t)out_builtins; - return strdup(str.c_str()); + return source->builtins_get(); } -char *gpu_shader_dependency_get_source(const char *shader_source_name) +Vector<const char *> gpu_shader_dependency_get_resolved_source( + const StringRefNull shader_source_name) +{ + Vector<const char *> result; + GPUSource *source = g_sources->lookup(shader_source_name); + source->build(result); + return result; +} + +StringRefNull gpu_shader_dependency_get_source(const StringRefNull shader_source_name) { GPUSource *src = g_sources->lookup(shader_source_name); - return strdup(src->source.c_str()); + return src->source; +} + +StringRefNull gpu_shader_dependency_get_filename_from_source_string( + const StringRefNull source_string) +{ + for (auto &source : g_sources->values()) { + if (source->source.c_str() == source_string.c_str()) { + return source->filename; + } + } + return ""; } + +} // namespace blender::gpu::shader diff --git a/source/blender/gpu/intern/gpu_shader_dependency_private.h b/source/blender/gpu/intern/gpu_shader_dependency_private.h index b129ca74a48..13261dd2021 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency_private.h +++ b/source/blender/gpu/intern/gpu_shader_dependency_private.h @@ -34,11 +34,32 @@ void gpu_shader_dependency_init(void); void gpu_shader_dependency_exit(void); -/* User must free the resulting string using free. */ -char *gpu_shader_dependency_get_resolved_source(const char *shader_source_name, - uint32_t *builtins); -char *gpu_shader_dependency_get_source(const char *shader_source_name); - #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +# include "BLI_string_ref.hh" +# include "BLI_vector.hh" + +# include "gpu_shader_create_info.hh" + +namespace blender::gpu::shader { + +BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull source_name); + +Vector<const char *> gpu_shader_dependency_get_resolved_source(const StringRefNull source_name); +StringRefNull gpu_shader_dependency_get_source(const StringRefNull source_name); + +/** + * \brief Find the name of the file from which the given string was generated. + * \return Return filename or empty string. + * \note source_string needs to be identical to the one given by gpu_shader_dependency_get_source() + */ +StringRefNull gpu_shader_dependency_get_filename_from_source_string( + const StringRefNull source_string); + +} // namespace blender::gpu::shader + +#endif diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index 735b8fea71d..514cfc01f09 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -68,6 +68,7 @@ class ShaderInterface { uint16_t enabled_ubo_mask_ = 0; uint8_t enabled_ima_mask_ = 0; uint64_t enabled_tex_mask_ = 0; + uint16_t enabled_ssbo_mask_ = 0; /** Location of builtin uniforms. Fast access, no lookup needed. */ int32_t builtins_[GPU_NUM_UNIFORMS]; int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]; @@ -107,6 +108,10 @@ class ShaderInterface { { return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name); } + inline const ShaderInput *ssbo_get(const int binding) const + { + return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, binding); + } inline const char *input_name_get(const ShaderInput *input) const { @@ -189,11 +194,11 @@ inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u) case GPU_UNIFORM_COLOR: return "color"; case GPU_UNIFORM_BASE_INSTANCE: - return "baseInstance"; + return "gpu_BaseInstance"; case GPU_UNIFORM_RESOURCE_CHUNK: - return "resourceChunk"; + return "drw_resourceChunk"; case GPU_UNIFORM_RESOURCE_ID: - return "resourceId"; + return "drw_ResourceID"; case GPU_UNIFORM_SRGB_TRANSFORM: return "srgbTarget"; @@ -211,6 +216,13 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu return "modelBlock"; case GPU_UNIFORM_BLOCK_INFO: return "infoBlock"; + + case GPU_UNIFORM_BLOCK_DRW_VIEW: + return "drw_view"; + case GPU_UNIFORM_BLOCK_DRW_MODEL: + return "drw_matrices"; + case GPU_UNIFORM_BLOCK_DRW_INFOS: + return "drw_infos"; default: return NULL; } diff --git a/source/blender/gpu/intern/gpu_shader_log.cc b/source/blender/gpu/intern/gpu_shader_log.cc index 12459b4b721..c879e912c5d 100644 --- a/source/blender/gpu/intern/gpu_shader_log.cc +++ b/source/blender/gpu/intern/gpu_shader_log.cc @@ -26,7 +26,9 @@ #include "BLI_dynstr.h" #include "BLI_string.h" #include "BLI_string_utils.h" +#include "BLI_vector.hh" +#include "gpu_shader_dependency_private.h" #include "gpu_shader_private.hh" #include "GPU_platform.h" @@ -41,6 +43,14 @@ namespace blender::gpu { /** \name Debug functions * \{ */ +/* Number of lines before and after the error line to print for compilation errors. */ +#define DEBUG_CONTEXT_LINES 0 +/** + * Print dependencies sources list before the shader report. + * Useful to debug include order or missing dependencies. + */ +#define DEBUG_DEPENDENCIES 0 + void Shader::print_log(Span<const char *> sources, char *log, const char *stage, @@ -61,6 +71,30 @@ void Shader::print_log(Span<const char *> sources, BLI_dynstr_appendf(dynstr, "\n"); +#if DEBUG_DEPENDENCIES + BLI_dynstr_appendf( + dynstr, "%s%sIncluded files (in order):%s\n", info_col, line_prefix, reset_col); +#endif + + Vector<int64_t> sources_end_line; + for (StringRefNull src : sources) { + int64_t cursor = 0, line_count = 0; + while ((cursor = src.find('\n', cursor) + 1)) { + line_count++; + } + if (sources_end_line.is_empty() == false) { + line_count += sources_end_line.last(); + } + sources_end_line.append(line_count); +#if DEBUG_DEPENDENCIES + StringRefNull filename = shader::gpu_shader_dependency_get_filename_from_source_string(src); + if (!filename.is_empty()) { + BLI_dynstr_appendf( + dynstr, "%s%s %s%s\n", info_col, line_prefix, filename.c_str(), reset_col); + } +#endif + } + char *log_line = log, *line_end; LogCursor previous_location; @@ -73,12 +107,32 @@ void Shader::print_log(Span<const char *> sources, continue; } + /* Silence not useful lines. */ + StringRef logref = StringRefNull(log_line).substr(0, (size_t)line_end - (size_t)log_line); + if (logref.endswith(" shader failed to compile with the following errors:") || + logref.endswith(" No code generated")) { + log_line += (size_t)line_end - (size_t)log_line; + continue; + } + GPULogItem log_item; log_line = parser->parse_line(log_line, log_item); + /* Sanitize output. Really bad values can happen when the error line is buggy. */ + if (log_item.cursor.source >= sources.size()) { + log_item.cursor.source = -1; + } + if (log_item.cursor.row >= sources_end_line.last()) { + log_item.cursor.source = -1; + log_item.cursor.row = -1; + } + if (log_item.cursor.row == -1) { found_line_id = false; } + else if (log_item.source_base_row && log_item.cursor.source > 0) { + log_item.cursor.row += sources_end_line[log_item.cursor.source - 1]; + } const char *src_line = sources_combined; @@ -98,15 +152,14 @@ void Shader::print_log(Span<const char *> sources, /* error_line is 1 based in this case. */ int src_line_index = 1; while ((src_line_end = strchr(src_line, '\n'))) { - if (src_line_index == log_item.cursor.row) { + if (src_line_index >= log_item.cursor.row) { found_line_id = true; break; } -/* TODO(fclem) Make this an option to display N lines before error. */ -#if 0 /* Uncomment to print shader file up to the error line to have more context. */ - BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); - BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); -#endif + if (src_line_index >= log_item.cursor.row - DEBUG_CONTEXT_LINES) { + BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); + BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); + } /* Continue to next line. */ src_line = src_line_end + 1; src_line_index++; @@ -129,10 +182,53 @@ void Shader::print_log(Span<const char *> sources, BLI_dynstr_appendf(dynstr, "^"); } BLI_dynstr_appendf(dynstr, "\n"); + + /* Skip the error line. */ + src_line = src_line_end + 1; + src_line_index++; + while ((src_line_end = strchr(src_line, '\n'))) { + if (src_line_index > log_item.cursor.row + DEBUG_CONTEXT_LINES) { + break; + } + BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); + BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); + /* Continue to next line. */ + src_line = src_line_end + 1; + src_line_index++; + } } } BLI_dynstr_appendf(dynstr, line_prefix); + /* Search the correct source index. */ + int row_in_file = log_item.cursor.row; + int source_index = log_item.cursor.source; + if (source_index <= 0) { + for (auto i : sources_end_line.index_range()) { + if (log_item.cursor.row <= sources_end_line[i]) { + source_index = i; + break; + } + } + } + if (source_index > 0) { + row_in_file -= sources_end_line[source_index - 1]; + } + /* Print the filename the error line is coming from. */ + if (source_index > 0) { + StringRefNull filename = shader::gpu_shader_dependency_get_filename_from_source_string( + sources[source_index]); + if (!filename.is_empty()) { + BLI_dynstr_appendf(dynstr, + "%s%s:%d:%d: %s", + info_col, + filename.c_str(), + row_in_file, + log_item.cursor.column + 1, + reset_col); + } + } + if (log_item.severity == Severity::Error) { BLI_dynstr_appendf(dynstr, "%s%s%s: ", err_col, "Error", info_col); } diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh index 7837af0dcf2..93c33811ee0 100644 --- a/source/blender/gpu/intern/gpu_shader_private.hh +++ b/source/blender/gpu/intern/gpu_shader_private.hh @@ -77,6 +77,7 @@ class Shader { virtual std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const = 0; virtual std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const = 0; virtual std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const = 0; + virtual std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const = 0; /* DEPRECATED: Kept only because of BGL API. */ virtual int program_handle_get() const = 0; @@ -119,6 +120,7 @@ struct LogCursor { struct GPULogItem { LogCursor cursor; + bool source_base_row = false; Severity severity = Severity::Unknown; }; diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 73b59b9f06f..3195e98da5e 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -451,7 +451,6 @@ inline bool validate_data_format(eGPUTextureFormat tex_format, eGPUDataFormat da } } -/* Definitely not complete, edit according to the gl specification. */ inline eGPUDataFormat to_data_format(eGPUTextureFormat tex_format) { switch (tex_format) { @@ -462,16 +461,27 @@ inline eGPUDataFormat to_data_format(eGPUTextureFormat tex_format) case GPU_DEPTH24_STENCIL8: case GPU_DEPTH32F_STENCIL8: return GPU_DATA_UINT_24_8; - case GPU_R8UI: case GPU_R16UI: - case GPU_RG16UI: case GPU_R32UI: + case GPU_RG16UI: + case GPU_RG32UI: + case GPU_RGBA16UI: + case GPU_RGBA32UI: return GPU_DATA_UINT; - case GPU_RG16I: case GPU_R16I: + case GPU_R32I: + case GPU_R8I: + case GPU_RG16I: + case GPU_RG32I: + case GPU_RG8I: + case GPU_RGBA16I: + case GPU_RGBA32I: + case GPU_RGBA8I: return GPU_DATA_INT; case GPU_R8: + case GPU_R8UI: case GPU_RG8: + case GPU_RG8UI: case GPU_RGBA8: case GPU_RGBA8UI: case GPU_SRGB8_A8: diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index c604859fa94..7a20278e5aa 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -137,19 +137,24 @@ struct DRWData **GPU_viewport_data_get(GPUViewport *viewport) static void gpu_viewport_textures_create(GPUViewport *viewport) { int *size = viewport->size; + float empty_pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f}; if (viewport->color_render_tx[0] == NULL) { viewport->color_render_tx[0] = GPU_texture_create_2d( "dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL); + GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel); viewport->color_overlay_tx[0] = GPU_texture_create_2d( "dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); + GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel); } if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == NULL) { viewport->color_render_tx[1] = GPU_texture_create_2d( "dtxl_color_stereo", UNPACK2(size), 1, GPU_RGBA16F, NULL); + GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel); viewport->color_overlay_tx[1] = GPU_texture_create_2d( "dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); + GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel); } /* Can be shared with GPUOffscreen. */ diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 1a445ebd7eb..2ee7c0503f4 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -240,6 +240,7 @@ static void detect_workarounds() GLContext::unused_fb_slot_workaround = true; /* Turn off extensions. */ GCaps.shader_image_load_store_support = false; + GCaps.shader_storage_buffer_objects_support = false; GLContext::base_instance_support = false; GLContext::clear_texture_support = false; GLContext::copy_image_support = false; @@ -247,6 +248,8 @@ static void detect_workarounds() GLContext::direct_state_access_support = false; GLContext::fixed_restart_index_support = false; GLContext::geometry_shader_invocations = false; + GLContext::layered_rendering_support = false; + GLContext::native_barycentric_support = false; GLContext::multi_bind_support = false; GLContext::multi_draw_indirect_support = false; GLContext::shader_draw_parameters_support = false; @@ -419,6 +422,12 @@ static void detect_workarounds() strstr(renderer, "HD Graphics 4000")) { GLContext::generate_mipmap_workaround = true; } + + /* Buggy interface query functions cause crashes when handling SSBOs (T93680) */ + if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY) && + (strstr(renderer, "HD Graphics 4400") || strstr(renderer, "HD Graphics 4600"))) { + GCaps.shader_storage_buffer_objects_support = false; + } } // namespace blender::gpu /** Internal capabilities. */ @@ -438,6 +447,8 @@ bool GLContext::direct_state_access_support = false; bool GLContext::explicit_location_support = false; bool GLContext::geometry_shader_invocations = false; bool GLContext::fixed_restart_index_support = false; +bool GLContext::layered_rendering_support = false; +bool GLContext::native_barycentric_support = false; bool GLContext::multi_bind_support = false; bool GLContext::multi_draw_indirect_support = false; bool GLContext::shader_draw_parameters_support = false; @@ -498,6 +509,8 @@ void GLBackend::capabilities_init() GLContext::explicit_location_support = GLEW_VERSION_4_3; GLContext::geometry_shader_invocations = GLEW_ARB_gpu_shader5; GLContext::fixed_restart_index_support = GLEW_ARB_ES3_compatibility; + GLContext::layered_rendering_support = GLEW_AMD_vertex_shader_layer; + GLContext::native_barycentric_support = GLEW_AMD_shader_explicit_vertex_parameter; GLContext::multi_bind_support = GLEW_ARB_multi_bind; GLContext::multi_draw_indirect_support = GLEW_ARB_multi_draw_indirect; GLContext::shader_draw_parameters_support = GLEW_ARB_shader_draw_parameters; diff --git a/source/blender/gpu/opengl/gl_compute.cc b/source/blender/gpu/opengl/gl_compute.cc index fa8317dde4a..14164ee181b 100644 --- a/source/blender/gpu/opengl/gl_compute.cc +++ b/source/blender/gpu/opengl/gl_compute.cc @@ -28,8 +28,8 @@ namespace blender::gpu { void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len) { + GL_CHECK_RESOURCES("Compute"); glDispatchCompute(group_x_len, group_y_len, group_z_len); - debug::check_gl_error("Dispatch Compute"); } } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index dd22418972b..b7a74863ac4 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -72,6 +72,8 @@ class GLContext : public Context { static bool explicit_location_support; static bool geometry_shader_invocations; static bool fixed_restart_index_support; + static bool layered_rendering_support; + static bool native_barycentric_support; static bool multi_bind_support; static bool multi_draw_indirect_support; static bool shader_draw_parameters_support; diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc index 0a06d9cdb7c..95dea43636d 100644 --- a/source/blender/gpu/opengl/gl_debug.cc +++ b/source/blender/gpu/opengl/gl_debug.cc @@ -108,6 +108,11 @@ static void APIENTRY debug_callback(GLenum UNUSED(source), GPU_debug_get_groups_names(sizeof(debug_groups), debug_groups); CLG_Severity clog_severity; + if (GPU_debug_group_match(GPU_DEBUG_SHADER_COMPILATION_GROUP)) { + /** Do not duplicate shader compilation error/warnings. */ + return; + } + switch (type) { case GL_DEBUG_TYPE_ERROR: case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: @@ -191,6 +196,9 @@ void init_gl_callbacks() void check_gl_error(const char *info) { + if (!(G.debug & G_DEBUG_GPU)) { + return; + } GLenum error = glGetError(); #define ERROR_CASE(err) \ @@ -339,7 +347,7 @@ void object_label(GLenum type, GLuint object, const char *name) char label[64]; SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type)); /* Small convenience for caller. */ - if (ELEM(type, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_VERTEX_SHADER)) { + if (ELEM(type, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_VERTEX_SHADER, GL_COMPUTE_SHADER)) { type = GL_SHADER; } if (ELEM(type, GL_UNIFORM_BUFFER)) { diff --git a/source/blender/gpu/opengl/gl_debug_layer.cc b/source/blender/gpu/opengl/gl_debug_layer.cc index e624cb9ee46..b82bf10ebe9 100644 --- a/source/blender/gpu/opengl/gl_debug_layer.cc +++ b/source/blender/gpu/opengl/gl_debug_layer.cc @@ -82,6 +82,8 @@ DEBUG_FUNC_DECLARE(PFNGLDRAWBUFFERSPROC, void, glDrawBuffers, GLsizei, n, const DEBUG_FUNC_DECLARE(PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC, void, glDrawElementsInstancedBaseVertexBaseInstance, GLenum, mode, GLsizei, count, GLenum, type, const void *, indices, GLsizei, primcount, GLint, basevertex, GLuint, baseinstance); DEBUG_FUNC_DECLARE(PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC, void, glDrawElementsInstancedBaseVertex, GLenum, mode, GLsizei, count, GLenum, type, const void *, indices, GLsizei, instancecount, GLint, basevertex); DEBUG_FUNC_DECLARE(PFNGLENDQUERYPROC, void, glEndQuery, GLenum, target); +DEBUG_FUNC_DECLARE(PFNGLDISPATCHCOMPUTEPROC, void, glDispatchCompute, GLuint, num_groups_x, GLuint, num_groups_y, GLuint, num_groups_z); +DEBUG_FUNC_DECLARE(PFNGLDISPATCHCOMPUTEINDIRECTPROC, void, glDispatchComputeIndirect, GLintptr, indirect); DEBUG_FUNC_DECLARE(PFNGLENDTRANSFORMFEEDBACKPROC, void, glEndTransformFeedback, void); DEBUG_FUNC_DECLARE(PFNGLFRAMEBUFFERTEXTURE2DPROC, void, glFramebufferTexture2D, GLenum, target, GLenum, attachment, GLenum, textarget, GLuint, texture, GLint, level); DEBUG_FUNC_DECLARE(PFNGLFRAMEBUFFERTEXTURELAYERPROC, void, glFramebufferTextureLayer, GLenum, target, GLenum, attachment, GLuint, texture, GLint, level, GLint, layer); @@ -130,6 +132,8 @@ void init_debug_layer() DEBUG_WRAP(glDeleteSamplers); DEBUG_WRAP(glDeleteShader); DEBUG_WRAP(glDeleteVertexArrays); + DEBUG_WRAP(glDispatchCompute); + DEBUG_WRAP(glDispatchComputeIndirect); DEBUG_WRAP(glDrawArraysInstanced); DEBUG_WRAP(glDrawArraysInstancedBaseInstance); DEBUG_WRAP(glDrawBuffers); diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc index 13f03195437..106a12bfefd 100644 --- a/source/blender/gpu/opengl/gl_framebuffer.cc +++ b/source/blender/gpu/opengl/gl_framebuffer.cc @@ -429,8 +429,15 @@ void GLFrameBuffer::read(eGPUFrameBufferBits plane, switch (plane) { case GPU_DEPTH_BIT: format = GL_DEPTH_COMPONENT; + BLI_assert_msg( + this->attachments_[GPU_FB_DEPTH_ATTACHMENT].tex != nullptr || + this->attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != nullptr, + "GPUFramebuffer: Error: Trying to read depth without a depth buffer attached."); break; case GPU_COLOR_BIT: + BLI_assert_msg( + mode != GL_NONE, + "GPUFramebuffer: Error: Trying to read a color slot without valid attachment."); format = channel_len_to_gl(channel_len); /* TODO: needed for selection buffers to work properly, this should be handled better. */ if (format == GL_RED && type == GL_UNSIGNED_INT) { diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh index 9ebe549efe7..00a7676dd3f 100644 --- a/source/blender/gpu/opengl/gl_framebuffer.hh +++ b/source/blender/gpu/opengl/gl_framebuffer.hh @@ -141,6 +141,8 @@ static inline GLenum to_gl(const GPUAttachmentType type) ATTACHMENT(COLOR_ATTACHMENT3); ATTACHMENT(COLOR_ATTACHMENT4); ATTACHMENT(COLOR_ATTACHMENT5); + ATTACHMENT(COLOR_ATTACHMENT6); + ATTACHMENT(COLOR_ATTACHMENT7); default: BLI_assert(0); return GL_COLOR_ATTACHMENT0; diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index cec85abae6f..9da35a2bc97 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -85,7 +85,7 @@ static const char *to_string(const Interpolation &interp) case Interpolation::NO_PERSPECTIVE: return "noperspective"; default: - return "unkown"; + return "unknown"; } } @@ -123,6 +123,74 @@ static const char *to_string(const Type &type) case Type::BOOL: return "bool"; default: + return "unknown"; + } +} + +static const char *to_string(const eGPUTextureFormat &type) +{ + switch (type) { + case GPU_RGBA8UI: + return "rgba8ui"; + case GPU_RGBA8I: + return "rgba8i"; + case GPU_RGBA8: + return "rgba8"; + case GPU_RGBA32UI: + return "rgba32ui"; + case GPU_RGBA32I: + return "rgba32i"; + case GPU_RGBA32F: + return "rgba32f"; + case GPU_RGBA16UI: + return "rgba16ui"; + case GPU_RGBA16I: + return "rgba16i"; + case GPU_RGBA16F: + return "rgba16f"; + case GPU_RGBA16: + return "rgba16"; + case GPU_RG8UI: + return "rg8ui"; + case GPU_RG8I: + return "rg8i"; + case GPU_RG8: + return "rg8"; + case GPU_RG32UI: + return "rg32ui"; + case GPU_RG32I: + return "rg32i"; + case GPU_RG32F: + return "rg32f"; + case GPU_RG16UI: + return "rg16ui"; + case GPU_RG16I: + return "rg16i"; + case GPU_RG16F: + return "rg16f"; + case GPU_RG16: + return "rg16"; + case GPU_R8UI: + return "r8ui"; + case GPU_R8I: + return "r8i"; + case GPU_R8: + return "r8"; + case GPU_R32UI: + return "r32ui"; + case GPU_R32I: + return "r32i"; + case GPU_R32F: + return "r32f"; + case GPU_R16UI: + return "r16ui"; + case GPU_R16I: + return "r16i"; + case GPU_R16F: + return "r16f"; + case GPU_R16: + return "r16"; + default: return "unkown"; } } @@ -217,6 +285,8 @@ static void print_image_type(std::ostream &os, case ImageType::UINT_2D_ARRAY: case ImageType::SHADOW_2D: case ImageType::SHADOW_2D_ARRAY: + case ImageType::DEPTH_2D: + case ImageType::DEPTH_2D_ARRAY: os << "2D"; break; case ImageType::FLOAT_3D: @@ -232,6 +302,8 @@ static void print_image_type(std::ostream &os, case ImageType::UINT_CUBE_ARRAY: case ImageType::SHADOW_CUBE: case ImageType::SHADOW_CUBE_ARRAY: + case ImageType::DEPTH_CUBE: + case ImageType::DEPTH_CUBE_ARRAY: os << "Cube"; break; default: @@ -250,6 +322,8 @@ static void print_image_type(std::ostream &os, case ImageType::UINT_CUBE_ARRAY: case ImageType::SHADOW_2D_ARRAY: case ImageType::SHADOW_CUBE_ARRAY: + case ImageType::DEPTH_2D_ARRAY: + case ImageType::DEPTH_CUBE_ARRAY: os << "Array"; break; default: @@ -271,16 +345,16 @@ static void print_image_type(std::ostream &os, static std::ostream &print_qualifier(std::ostream &os, const Qualifier &qualifiers) { - if ((qualifiers & Qualifier::RESTRICT) != Qualifier::RESTRICT) { - os << "restrict"; + if (bool(qualifiers & Qualifier::NO_RESTRICT) == false) { + os << "restrict "; } - if ((qualifiers & Qualifier::READ_ONLY) != Qualifier::READ_ONLY) { - os << "readonly"; + if (bool(qualifiers & Qualifier::READ) == false) { + os << "writeonly "; } - if ((qualifiers & Qualifier::WRITE_ONLY) != Qualifier::WRITE_ONLY) { - os << "writeonly"; + if (bool(qualifiers & Qualifier::WRITE) == false) { + os << "readonly "; } - return os << " "; + return os; } static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res) @@ -288,7 +362,7 @@ static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &r if (GLContext::explicit_location_support) { os << "layout(binding = " << res.slot; if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) { - os << ", " << res.image.format; + os << ", " << to_string(res.image.format); } else if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) { os << ", std140"; @@ -328,8 +402,8 @@ static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &r array_offset = res.storagebuf.name.find_first_of("["); name_no_array = (array_offset == -1) ? res.storagebuf.name : StringRef(res.storagebuf.name.c_str(), array_offset); - os << "buffer "; print_qualifier(os, res.storagebuf.qualifiers); + os << "buffer "; os << name_no_array << " { " << res.storagebuf.type_name << " _" << res.storagebuf.name << "; };\n"; break; @@ -364,7 +438,7 @@ static void print_interface(std::ostream &os, const StageInterfaceInfo &iface, const StringRefNull &suffix = "") { - /* TODO(fclem) Move that to interface check. */ + /* TODO(@fclem): Move that to interface check. */ // if (iface.instance_name.is_empty()) { // BLI_assert_msg(0, "Interfaces require an instance name for geometry shader."); // std::cout << iface.name << ": Interfaces require an instance name for geometry shader.\n"; @@ -403,18 +477,37 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const } ss << "\n/* Push Constants. */\n"; for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) { - if (GLContext::explicit_location_support) { - ss << "layout(location = " << uniform.index << ") "; - } ss << "uniform " << to_string(uniform.type) << " " << uniform.name; if (uniform.array_size > 0) { ss << "[" << uniform.array_size << "]"; } ss << ";\n"; } +#if 0 /* T95278: This is not be enough to prevent some compilers think it is recursive. */ for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) { - ss << "#define " << uniform.name << " (" << uniform.name << ")\n"; + /* T95278: Double macro to avoid some compilers think it is recursive. */ + ss << "#define " << uniform.name << "_ " << uniform.name << "\n"; + ss << "#define " << uniform.name << " (" << uniform.name << "_)\n"; } +#endif + ss << "\n"; + return ss.str(); +} + +static std::string main_function_wrapper(std::string &pre_main, std::string &post_main) +{ + std::stringstream ss; + /* Prototype for the original main. */ + ss << "\n"; + ss << "void main_function_();\n"; + /* Wrapper to the main function in order to inject code processing on globals. */ + ss << "void main() {\n"; + ss << pre_main; + ss << " main_function_();\n"; + ss << post_main; + ss << "}\n"; + /* Rename the original main. */ + ss << "#define main main_function_\n"; ss << "\n"; return ss.str(); } @@ -422,10 +515,13 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) const { std::stringstream ss; + std::string post_main = ""; ss << "\n/* Inputs. */\n"; for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) { - if (GLContext::explicit_location_support) { + if (GLContext::explicit_location_support && + /* Fix issue with AMDGPU-PRO + workbench_prepass_mesh_vert.glsl being quantized. */ + GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) == false) { ss << "layout(location = " << attr.index << ") "; } ss << "in " << to_string(attr.type) << " " << attr.name << ";\n"; @@ -434,13 +530,35 @@ std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) con for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { print_interface(ss, "out", *iface); } + if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::LAYER)) { + ss << "out int gpu_Layer;\n"; + } + if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) { + if (!GLContext::native_barycentric_support) { + /* Disabled or unsupported. */ + } + else if (GLEW_AMD_shader_explicit_vertex_parameter) { + /* Need this for stable barycentric. */ + ss << "flat out vec4 gpu_pos_flat;\n"; + ss << "out vec4 gpu_pos;\n"; + + post_main += " gpu_pos = gpu_pos_flat = gl_Position;\n"; + } + } ss << "\n"; + + if (post_main.empty() == false) { + std::string pre_main = ""; + ss << main_function_wrapper(pre_main, post_main); + } return ss.str(); } std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) const { std::stringstream ss; + std::string pre_main = ""; + ss << "\n/* Interfaces. */\n"; const Vector<StageInterfaceInfo *> &in_interfaces = (info.geometry_source_.is_empty()) ? info.vertex_out_interfaces_ : @@ -448,6 +566,32 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c for (const StageInterfaceInfo *iface : in_interfaces) { print_interface(ss, "in", *iface); } + if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) { + if (!GLContext::native_barycentric_support) { + ss << "smooth in vec3 gpu_BaryCoord;\n"; + ss << "noperspective in vec3 gpu_BaryCoordNoPersp;\n"; + } + else if (GLEW_AMD_shader_explicit_vertex_parameter) { + /* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry + * shader workaround if this extension/feature is detected. */ + ss << "\n/* Stable Barycentric Coordinates. */\n"; + ss << "flat in vec4 gpu_pos_flat;\n"; + ss << "__explicitInterpAMD in vec4 gpu_pos;\n"; + /* Globals. */ + ss << "vec3 gpu_BaryCoord;\n"; + ss << "vec3 gpu_BaryCoordNoPersp;\n"; + ss << "\n"; + ss << "vec2 stable_bary_(vec2 in_bary) {\n"; + ss << " vec3 bary = vec3(in_bary, 1.0 - in_bary.x - in_bary.y);\n"; + ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { return bary.zxy; }\n"; + ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { return bary.yzx; }\n"; + ss << " return bary.xyz;\n"; + ss << "}\n"; + + pre_main += " gpu_BaryCoord = stable_bary_(gl_BaryCoordSmoothAMD);\n"; + pre_main += " gpu_BaryCoordNoPersp = stable_bary_(gl_BaryCoordNoPerspAMD);\n"; + } + } ss << "\n/* Outputs. */\n"; for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) { ss << "layout(location = " << output.index; @@ -465,6 +609,11 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c ss << "out " << to_string(output.type) << " " << output.name << ";\n"; } ss << "\n"; + + if (pre_main.empty() == false) { + std::string post_main = ""; + ss << main_function_wrapper(pre_main, post_main); + } return ss.str(); } @@ -492,21 +641,125 @@ std::string GLShader::geometry_layout_declare(const ShaderCreateInfo &info) cons return ss.str(); } +static StageInterfaceInfo *find_interface_by_name(const Vector<StageInterfaceInfo *> &ifaces, + const StringRefNull &name) +{ + for (auto *iface : ifaces) { + if (iface->instance_name == name) { + return iface; + } + } + return nullptr; +} + std::string GLShader::geometry_interface_declare(const ShaderCreateInfo &info) const { std::stringstream ss; + ss << "\n/* Interfaces. */\n"; for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { - print_interface(ss, "in", *iface, "[]"); + bool has_matching_output_iface = find_interface_by_name(info.geometry_out_interfaces_, + iface->instance_name) != nullptr; + const char *suffix = (has_matching_output_iface) ? "_in[]" : "[]"; + print_interface(ss, "in", *iface, suffix); } ss << "\n"; for (const StageInterfaceInfo *iface : info.geometry_out_interfaces_) { - print_interface(ss, "out", *iface); + bool has_matching_input_iface = find_interface_by_name(info.vertex_out_interfaces_, + iface->instance_name) != nullptr; + const char *suffix = (has_matching_input_iface) ? "_out" : ""; + print_interface(ss, "out", *iface, suffix); } ss << "\n"; return ss.str(); } +std::string GLShader::compute_layout_declare(const ShaderCreateInfo &info) const +{ + std::stringstream ss; + ss << "\n/* Compute Layout. */\n"; + ss << "layout(local_size_x = " << info.compute_layout_.local_size_x; + if (info.compute_layout_.local_size_y != -1) { + ss << ", local_size_y = " << info.compute_layout_.local_size_y; + } + if (info.compute_layout_.local_size_z != -1) { + ss << ", local_size_y = " << info.compute_layout_.local_size_z; + } + ss << ") in;\n"; + ss << "\n"; + return ss.str(); +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Passthrough geometry shader emulation + * + * \{ */ + +std::string GLShader::workaround_geometry_shader_source_create( + const shader::ShaderCreateInfo &info) +{ + std::stringstream ss; + + const bool do_layer_workaround = !GLContext::layered_rendering_support && + bool(info.builtins_ & BuiltinBits::LAYER); + const bool do_barycentric_workaround = !GLContext::native_barycentric_support && + bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD); + + shader::ShaderCreateInfo info_modified = info; + info_modified.geometry_out_interfaces_ = info_modified.vertex_out_interfaces_; + /** + * NOTE(@fclem): Assuming we will render TRIANGLES. This will not work with other primitive + * types. In this case, it might not trigger an error on some implementations. + */ + info_modified.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3); + + ss << geometry_layout_declare(info_modified); + ss << geometry_interface_declare(info_modified); + if (do_layer_workaround) { + ss << "in int gpu_Layer[];\n"; + } + if (do_barycentric_workaround) { + ss << "smooth out vec3 gpu_BaryCoord;\n"; + ss << "noperspective out vec3 gpu_BaryCoordNoPersp;\n"; + } + ss << "\n"; + + ss << "void main()\n"; + ss << "{\n"; + if (do_layer_workaround) { + ss << " gl_Layer = gpu_Layer[0];\n"; + } + for (auto i : IndexRange(3)) { + for (auto iface : info_modified.vertex_out_interfaces_) { + for (auto &inout : iface->inouts) { + ss << " " << iface->instance_name << "_out." << inout.name; + ss << " = " << iface->instance_name << "_in[" << i << "]." << inout.name << ";\n"; + } + } + if (do_barycentric_workaround) { + ss << " gpu_BaryCoordNoPersp = gpu_BaryCoord ="; + ss << " vec3(" << int(i == 0) << ", " << int(i == 1) << ", " << int(i == 2) << ");\n"; + } + ss << " gl_Position = gl_in[" << i << "].gl_Position;\n"; + ss << " EmitVertex();\n"; + } + ss << "}\n"; + return ss.str(); +} + +bool GLShader::do_geometry_shader_injection(const shader::ShaderCreateInfo *info) +{ + BuiltinBits builtins = info->builtins_; + if (!GLContext::native_barycentric_support && bool(builtins & BuiltinBits::BARYCENTRIC_COORD)) { + return true; + } + if (!GLContext::layered_rendering_support && bool(builtins & BuiltinBits::LAYER)) { + return true; + } + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -516,7 +769,7 @@ std::string GLShader::geometry_interface_declare(const ShaderCreateInfo &info) c static char *glsl_patch_default_get() { /** Used for shader patching. Init once. */ - static char patch[512] = "\0"; + static char patch[1024] = "\0"; if (patch[0] != '\0') { return patch; } @@ -543,6 +796,7 @@ static char *glsl_patch_default_get() if (GLContext::shader_draw_parameters_support) { STR_CONCAT(patch, slen, "#extension GL_ARB_shader_draw_parameters : enable\n"); STR_CONCAT(patch, slen, "#define GPU_ARB_shader_draw_parameters\n"); + STR_CONCAT(patch, slen, "#define gpu_BaseInstance gl_BaseInstanceARB\n"); } if (GLContext::geometry_shader_invocations) { STR_CONCAT(patch, slen, "#extension GL_ARB_gpu_shader5 : enable\n"); @@ -552,6 +806,31 @@ static char *glsl_patch_default_get() STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n"); STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n"); } + if (!GLEW_VERSION_4_2 && GLEW_ARB_conservative_depth) { + STR_CONCAT(patch, slen, "#extension GL_ARB_conservative_depth : enable\n"); + } + if (GPU_shader_image_load_store_support()) { + STR_CONCAT(patch, slen, "#extension GL_ARB_shader_image_load_store: enable\n"); + STR_CONCAT(patch, slen, "#extension GL_ARB_shading_language_420pack: enable\n"); + } + if (GLContext::layered_rendering_support) { + STR_CONCAT(patch, slen, "#extension GL_AMD_vertex_shader_layer: enable\n"); + STR_CONCAT(patch, slen, "#define gpu_Layer gl_Layer\n"); + } + if (GLContext::native_barycentric_support) { + STR_CONCAT(patch, slen, "#extension GL_AMD_shader_explicit_vertex_parameter: enable\n"); + } + + /* Fallbacks. */ + if (!GLContext::shader_draw_parameters_support) { + STR_CONCAT(patch, slen, "uniform int gpu_BaseInstance;\n"); + } + + /* Vulkan GLSL compat. */ + STR_CONCAT(patch, slen, "#define gpu_InstanceIndex (gl_InstanceID + gpu_BaseInstance)\n"); + + /* Array compat. */ + STR_CONCAT(patch, slen, "#define array(_type) _type[]\n"); /* Derivative sign can change depending on implementation. */ STR_CONCATF(patch, slen, "#define DFDX_SIGN %1.1f\n", GLContext::derivative_signs[0]); @@ -660,6 +939,14 @@ bool GLShader::finalize(const shader::ShaderCreateInfo *info) return false; } + if (info && do_geometry_shader_injection(info)) { + std::string source = workaround_geometry_shader_source_create(*info); + Vector<const char *> sources; + sources.append("version"); + sources.append(source.c_str()); + geometry_shader_from_glsl(sources); + } + glLinkProgram(shader_program_); GLint status; diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index af92c77db54..cc1c93142f8 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -69,6 +69,7 @@ class GLShader : public Shader { std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const override; std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const override; std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const override; + std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const override; /** Should be called before linking. */ void transform_feedback_names_set(Span<const char *> name_list, @@ -93,6 +94,14 @@ class GLShader : public Shader { /** Create, compile and attach the shader stage to the shader program. */ GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources); + /** + * \brief features available on newer implementation such as native barycentric coordinates + * and layered rendering, necessitate a geometry shader to work on older hardware. + */ + std::string workaround_geometry_shader_source_create(const shader::ShaderCreateInfo &info); + + bool do_geometry_shader_injection(const shader::ShaderCreateInfo *info); + MEM_CXX_CLASS_ALLOC_FUNCS("GLShader"); }; diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 2211c2fbb7c..0a31f8dee7f 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -117,7 +117,28 @@ static inline int image_binding(int32_t program, switch (type) { case GL_IMAGE_1D: case GL_IMAGE_2D: - case GL_IMAGE_3D: { + case GL_IMAGE_3D: + case GL_IMAGE_CUBE: + case GL_IMAGE_BUFFER: + case GL_IMAGE_1D_ARRAY: + case GL_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_1D: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_3D: + case GL_INT_IMAGE_CUBE: + case GL_INT_IMAGE_BUFFER: + case GL_INT_IMAGE_1D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_1D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: { /* For now just assign a consecutive index. In the future, we should set it in * the shader using layout(binding = i) and query its value. */ int binding = *image_len; @@ -298,6 +319,7 @@ GLShaderInterface::GLShaderInterface(GLuint program) input->binding = input->location = binding; name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_ssbo_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu; } /* Builtin Uniforms */ @@ -355,15 +377,33 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI } } + size_t workaround_names_size = 0; + Vector<StringRefNull> workaround_uniform_names; + auto check_enabled_uniform = [&](const char *uniform_name) { + if (glGetUniformLocation(program, uniform_name) != -1) { + workaround_uniform_names.append(uniform_name); + workaround_names_size += StringRefNull(uniform_name).size() + 1; + uniform_len_++; + } + }; + + if (!GLContext::shader_draw_parameters_support) { + check_enabled_uniform("gpu_BaseInstance"); + } + BLI_assert_msg(ubo_len_ <= 16, "enabled_ubo_mask_ is uint16_t"); int input_tot_len = attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_; inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__); ShaderInput *input = inputs_; - name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_, "name_buffer"); + name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_ + workaround_names_size, + "name_buffer"); uint32_t name_buffer_offset = 0; + /* Necessary to make #glUniform works. TODO(fclem) Remove. */ + glUseProgram(program); + /* Attributes */ for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) { copy_input_name(input, attr.name, name_buffer_, name_buffer_offset); @@ -382,7 +422,7 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) { copy_input_name(input, res.uniformbuf.name, name_buffer_, name_buffer_offset); if (true || !GLContext::explicit_location_support) { - input->location = glGetUniformBlockIndex(program, res.uniformbuf.name.c_str()); + input->location = glGetUniformBlockIndex(program, name_buffer_ + input->name_offset); glUniformBlockBinding(program, input->location, res.slot); } input->binding = res.slot; @@ -419,10 +459,15 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI } for (const ShaderCreateInfo::PushConst &uni : info.push_constants_) { copy_input_name(input, uni.name, name_buffer_, name_buffer_offset); - /* Until we make use of explicit uniform location. */ - if (true || !GLContext::explicit_location_support) { - input->location = glGetUniformLocation(program, uni.name.c_str()); - } + input->location = glGetUniformLocation(program, name_buffer_ + input->name_offset); + input->binding = -1; + input++; + } + + /* Compatibility uniforms. */ + for (auto &name : workaround_uniform_names) { + copy_input_name(input, name, name_buffer_, name_buffer_offset); + input->location = glGetUniformLocation(program, name_buffer_ + input->name_offset); input->binding = -1; input++; } @@ -432,7 +477,7 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) { copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset); input->location = input->binding = res.slot; - enabled_ubo_mask_ |= (1 << input->binding); + enabled_ssbo_mask_ |= (1 << input->binding); input++; } } @@ -474,7 +519,7 @@ GLShaderInterface::~GLShaderInterface() void GLShaderInterface::ref_add(GLVaoCache *ref) { for (int i = 0; i < refs_.size(); i++) { - if (refs_[i] == NULL) { + if (refs_[i] == nullptr) { refs_[i] = ref; return; } @@ -486,7 +531,7 @@ void GLShaderInterface::ref_remove(GLVaoCache *ref) { for (int i = 0; i < refs_.size(); i++) { if (refs_[i] == ref) { - refs_[i] = NULL; + refs_[i] = nullptr; break; /* cannot have duplicates */ } } diff --git a/source/blender/gpu/opengl/gl_shader_log.cc b/source/blender/gpu/opengl/gl_shader_log.cc index 174cc63ad81..ec13bc44136 100644 --- a/source/blender/gpu/opengl/gl_shader_log.cc +++ b/source/blender/gpu/opengl/gl_shader_log.cc @@ -60,6 +60,15 @@ char *GLLogParser::parse_line(char *log_line, GPULogItem &log_item) log_item.cursor.row = log_item.cursor.column; log_item.cursor.column = -1; } + else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL) && + /* WORKAROUND(@fclem): Both Mesa and AMDGPU-PRO are reported as official. */ + StringRefNull(GPU_platform_version()).find(" Mesa ") == -1) { + /* source:row */ + log_item.cursor.source = log_item.cursor.row; + log_item.cursor.row = log_item.cursor.column; + log_item.cursor.column = -1; + log_item.source_base_row = true; + } else { /* line:char */ } diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index 83ff3ffc9e9..5985f1583f2 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -124,11 +124,20 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits) if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS) { barrier |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; } + if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) { + barrier |= GL_SHADER_STORAGE_BARRIER_BIT; + } if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) { barrier |= GL_TEXTURE_FETCH_BARRIER_BIT; } - if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) { - barrier |= GL_SHADER_STORAGE_BARRIER_BIT; + if (barrier_bits & GPU_BARRIER_TEXTURE_UPDATE) { + barrier |= GL_TEXTURE_UPDATE_BARRIER_BIT; + } + if (barrier_bits & GPU_BARRIER_COMMAND) { + barrier |= GL_COMMAND_BARRIER_BIT; + } + if (barrier_bits & GPU_BARRIER_FRAMEBUFFER) { + barrier |= GL_FRAMEBUFFER_BARRIER_BIT; } if (barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY) { barrier |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT; diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl index b83ea59a692..779bcc59487 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl @@ -4,57 +4,18 @@ #define MID_VERTEX 65 -#ifndef USE_GPU_SHADER_CREATE_INFO - -/* u is position along the curve, defining the tangent space. - * v is "signed" distance (compressed to [0..1] range) from the pos in expand direction */ -in vec2 uv; -in vec2 pos; /* verts position in the curve tangent space */ -in vec2 expand; - -# ifdef USE_INSTANCE -/* Instance attrs. */ -in vec2 P0; -in vec2 P1; -in vec2 P2; -in vec2 P3; -in ivec4 colid_doarrow; -in vec4 start_color; -in vec4 end_color; -in ivec2 domuted; -in float dim_factor; -in float thickness; -in float dash_factor; -in float dash_alpha; - -uniform vec4 colors[6]; - -# else -/* Single curve drawcall, use uniform. */ -uniform vec2 bezierPts[4]; - -uniform vec4 colors[3]; -uniform bool doArrow; -uniform bool doMuted; -uniform float dim_factor; -uniform float thickness; -uniform float dash_factor; -uniform float dash_alpha; - -# endif - -uniform float expandSize; -uniform float arrowSize; -uniform mat4 ModelViewProjectionMatrix; - -out float colorGradient; -out vec4 finalColor; -out float lineU; -flat out float lineLength; -flat out float dashFactor; -flat out float dashAlpha; -flat out int isMainLine; -#endif +/** + * `uv.x` is position along the curve, defining the tangent space. + * `uv.y` is "signed" distance (compressed to [0..1] range) from the pos in expand direction + * `pos` is the verts position in the curve tangent space + */ + +void main(void) +{ + /* Define where along the noodle the gradient will starts and ends. + * Use 0.25 instead of 0.35-0.65, because of a visual shift issue. */ + const float start_gradient_threshold = 0.25; + const float end_gradient_threshold = 0.55; #ifdef USE_INSTANCE # define colStart (colid_doarrow[0] < 3 ? start_color : node_link_data.colors[colid_doarrow[0]]) @@ -62,33 +23,23 @@ flat out int isMainLine; # define colShadow node_link_data.colors[colid_doarrow[2]] # define doArrow (colid_doarrow[3] != 0) # define doMuted (domuted[0] != 0) - #else -# define P0 node_link_data.bezierPts[0].xy -# define P1 node_link_data.bezierPts[1].xy -# define P2 node_link_data.bezierPts[2].xy -# define P3 node_link_data.bezierPts[3].xy -# define cols node_link_data.colors -# define doArrow node_link_data.doArrow -# define doMuted node_link_data.doMuted -# define dim_factor node_link_data.dim_factor -# define thickness node_link_data.thickness -# define dash_factor node_link_data.dash_factor -# define dash_alpha node_link_data.dash_alpha - -# define colShadow node_link_data.colors[0] -# define colStart node_link_data.colors[1] -# define colEnd node_link_data.colors[2] - + vec2 P0 = node_link_data.bezierPts[0].xy; + vec2 P1 = node_link_data.bezierPts[1].xy; + vec2 P2 = node_link_data.bezierPts[2].xy; + vec2 P3 = node_link_data.bezierPts[3].xy; + bool doArrow = node_link_data.doArrow; + bool doMuted = node_link_data.doMuted; + float dim_factor = node_link_data.dim_factor; + float thickness = node_link_data.thickness; + float dash_factor = node_link_data.dash_factor; + float dash_alpha = node_link_data.dash_alpha; + + vec4 colShadow = node_link_data.colors[0]; + vec4 colStart = node_link_data.colors[1]; + vec4 colEnd = node_link_data.colors[2]; #endif -/* Define where along the noodle the gradient will starts and ends. - * Use 0.25 instead of 0.35-0.65, because of a visual shift issue. */ -const float start_gradient_threshold = 0.25; -const float end_gradient_threshold = 0.55; - -void main(void) -{ /* Parameters for the dashed line. */ isMainLine = expand.y != 1.0 ? 0 : 1; dashFactor = dash_factor; diff --git a/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl index 4d887a37807..1ec84598bf1 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl) + #ifndef USE_GPU_SHADER_CREATE_INFO noperspective in vec4 finalColor; out vec4 fragColor; diff --git a/source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl index de555cc5706..f374913a32c 100644 --- a/source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl) + #ifndef USE_GPU_SHADER_CREATE_INFO in vec4 finalColor; out vec4 fragColor; diff --git a/source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl b/source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl index 74341701fb0..7d69cba5017 100644 --- a/source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl @@ -6,13 +6,13 @@ uniform bool srgbTarget = false; #endif -vec4 blender_srgb_to_framebuffer_space(vec4 color) +vec4 blender_srgb_to_framebuffer_space(vec4 col) { if (srgbTarget) { - vec3 c = max(color.rgb, vec3(0.0)); + vec3 c = max(col.rgb, vec3(0.0)); vec3 c1 = c * (1.0 / 12.92); vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4)); - color.rgb = mix(c1, c2, step(vec3(0.04045), c)); + col.rgb = mix(c1, c2, step(vec3(0.04045), c)); } - return color; + return col; } diff --git a/source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl index 1675de3d567..28a716104f1 100644 --- a/source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl) + #ifndef USE_GPU_SHADER_CREATE_INFO flat in vec4 finalColor; out vec4 fragColor; diff --git a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl index 1456bd0c732..c339d3cbabb 100644 --- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl) + #ifndef USE_GPU_SHADER_CREATE_INFO flat in vec4 color_flat; noperspective in vec2 texCoord_interp; diff --git a/source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl index b4a75cc489b..0510848e4d4 100644 --- a/source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl @@ -1,3 +1,4 @@ +#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl) #ifndef USE_GPU_SHADER_CREATE_INFO uniform vec4 color; diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh index bf746eae9b4..b5dce51fc1b 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh @@ -29,11 +29,11 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_area_borders) .vertex_in(0, Type::VEC2, "pos") .vertex_out(smooth_uv_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC4, "rect") - .push_constant(20, Type::VEC4, "color") - .push_constant(24, Type::FLOAT, "scale") - .push_constant(25, Type::INT, "cornerLen") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "rect") + .push_constant(Type::VEC4, "color") + .push_constant(Type::FLOAT, "scale") + .push_constant(Type::INT, "cornerLen") .vertex_source("gpu_shader_2D_area_borders_vert.glsl") .fragment_source("gpu_shader_2D_area_borders_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh index 48c261da8dd..b8dbca276ae 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh @@ -26,10 +26,10 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_checker) .vertex_in(0, Type::VEC2, "pos") .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC4, "color1") - .push_constant(20, Type::VEC4, "color2") - .push_constant(24, Type::INT, "size") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color1") + .push_constant(Type::VEC4, "color2") + .push_constant(Type::INT, "size") .vertex_source("gpu_shader_2D_vert.glsl") .fragment_source("gpu_shader_checker_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh index 51ce7f503df..b52be8b328e 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh @@ -26,11 +26,11 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_diag_stripes) .vertex_in(0, Type::VEC2, "pos") .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC4, "color1") - .push_constant(20, Type::VEC4, "color2") - .push_constant(24, Type::INT, "size1") - .push_constant(28, Type::INT, "size2") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color1") + .push_constant(Type::VEC4, "color2") + .push_constant(Type::INT, "size1") + .push_constant(Type::INT, "size2") .vertex_source("gpu_shader_2D_vert.glsl") .fragment_source("gpu_shader_diag_stripes_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh index bbc5446f16f..699b0a456f9 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh @@ -30,7 +30,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_flat_color) .vertex_in(1, Type::VEC4, "color") .vertex_out(flat_color_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_2D_flat_color_vert.glsl") .fragment_source("gpu_shader_flat_color_frag.glsl") .additional_info("gpu_srgb_to_framebuffer_space") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh index a6cc9076d4a..8ac9f58d936 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh @@ -25,6 +25,6 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_color) .additional_info("gpu_shader_2D_image_common") - .push_constant(16, Type::VEC4, "color") + .push_constant(Type::VEC4, "color") .fragment_source("gpu_shader_image_color_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh index e11d6746446..3e10c0e1651 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh @@ -25,7 +25,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_desaturate_color) .additional_info("gpu_shader_2D_image_common") - .push_constant(16, Type::VEC4, "color") - .push_constant(20, Type::FLOAT, "factor") + .push_constant(Type::VEC4, "color") + .push_constant(Type::FLOAT, "factor") .fragment_source("gpu_shader_image_desaturate_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh index 3d20b63c265..989e38527c0 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh @@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_common) .vertex_in(1, Type::VEC2, "texCoord") .vertex_out(smooth_tex_coord_interp_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .sampler(0, ImageType::FLOAT_2D, "image") .vertex_source("gpu_shader_2D_image_vert.glsl"); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh index c2c0e9fec78..d1a2a8f6ee7 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh @@ -29,9 +29,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_merge) .vertex_in(1, Type::VEC2, "texCoord") .vertex_out(smooth_tex_coord_interp_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::BOOL, "display_transform") - .push_constant(17, Type::BOOL, "overlay") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::BOOL, "display_transform") + .push_constant(Type::BOOL, "overlay") .sampler(0, ImageType::FLOAT_2D, "image_texture") .sampler(1, ImageType::FLOAT_2D, "overlays_texture") .vertex_source("gpu_shader_2D_image_vert.glsl") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh index c1e6c3957d3..d099d95e4b6 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh @@ -29,8 +29,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_stereo_merge) .fragment_out(1, Type::VEC4, "overlayColor") .sampler(0, ImageType::FLOAT_2D, "imageTexture") .sampler(1, ImageType::FLOAT_2D, "overlayTexture") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::INT, "stereoDisplaySettings") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::INT, "stereoDisplaySettings") .vertex_source("gpu_shader_2D_vert.glsl") .fragment_source("gpu_shader_image_overlays_stereo_merge_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh index 4e10b91ef39..9d5fb152561 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh @@ -27,10 +27,10 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_rect_color) .vertex_out(smooth_tex_coord_interp_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC4, "color") - .push_constant(20, Type::VEC4, "rect_icon") - .push_constant(24, Type::VEC4, "rect_geom") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color") + .push_constant(Type::VEC4, "rect_icon") + .push_constant(Type::VEC4, "rect_geom") .sampler(0, ImageType::FLOAT_2D, "image") .vertex_source("gpu_shader_2D_image_rect_vert.glsl") .fragment_source("gpu_shader_image_color_frag.glsl") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh index 3663de0a98f..93950b37509 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh @@ -25,7 +25,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_shuffle_color) .additional_info("gpu_shader_2D_image_common") - .push_constant(16, Type::VEC4, "color") - .push_constant(20, Type::VEC4, "shuffle") + .push_constant(Type::VEC4, "color") + .push_constant(Type::VEC4, "shuffle") .fragment_source("gpu_shader_image_shuffle_color_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh index 371a35386a7..afac24e6241 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh @@ -28,7 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_line_dashed_uniform_color) .vertex_in(0, Type::VEC3, "pos") .vertex_out(flat_color_iface) - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_2D_line_dashed_uniform_color_vert.glsl") .fragment_source("gpu_shader_2D_line_dashed_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh index b15d7ba3ada..15451899d5d 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh @@ -39,7 +39,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_nodelink) .vertex_out(nodelink_iface) .fragment_out(0, Type::VEC4, "fragColor") .uniform_buf(0, "NodeLinkData", "node_link_data", Frequency::PASS) - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_2D_nodelink_vert.glsl") .fragment_source("gpu_shader_2D_nodelink_frag.glsl") .typedef_source("GPU_shader_shared.h") @@ -64,7 +64,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_nodelink_inst) .vertex_out(nodelink_iface) .fragment_out(0, Type::VEC4, "fragColor") .uniform_buf(0, "NodeLinkInstanceData", "node_link_data", Frequency::PASS) - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_2D_nodelink_vert.glsl") .fragment_source("gpu_shader_2D_nodelink_frag.glsl") .typedef_source("GPU_shader_shared.h") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh index d2753af8e9b..2a3d5698ba4 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh @@ -28,9 +28,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_aa) .vertex_in(0, Type::VEC2, "pos") .vertex_out(smooth_radii_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC4, "color") - .push_constant(20, Type::FLOAT, "size") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color") + .push_constant(Type::FLOAT, "size") .vertex_source("gpu_shader_2D_point_uniform_size_aa_vert.glsl") .fragment_source("gpu_shader_point_uniform_color_aa_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh index edc83534573..c7cc61e745b 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh @@ -28,11 +28,11 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_outline_aa .vertex_in(0, Type::VEC2, "pos") .vertex_out(smooth_radii_outline_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(20, Type::VEC4, "color") - .push_constant(24, Type::VEC4, "outlineColor") - .push_constant(28, Type::FLOAT, "size") - .push_constant(29, Type::FLOAT, "outlineWidth") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color") + .push_constant(Type::VEC4, "outlineColor") + .push_constant(Type::FLOAT, "size") + .push_constant(Type::FLOAT, "outlineWidth") .vertex_source("gpu_shader_2D_point_uniform_size_outline_aa_vert.glsl") .fragment_source("gpu_shader_point_uniform_color_outline_aa_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh index 4358e94f91f..38dddb4357e 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh @@ -30,7 +30,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_varying_size_varying_color) .vertex_in(2, Type::VEC4, "color") .vertex_out(smooth_color_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_2D_point_varying_size_varying_color_vert.glsl") .fragment_source("gpu_shader_point_varying_color_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh index 0029e8d2044..128be12a7d9 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh @@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_smooth_color) .vertex_in(1, Type::VEC4, "color") .vertex_out(smooth_color_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_2D_smooth_color_vert.glsl") .fragment_source("gpu_shader_2D_smooth_color_frag.glsl") .additional_info("gpu_srgb_to_framebuffer_space") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh index 7e75b265711..3dab6d36753 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh @@ -26,8 +26,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_uniform_color) .vertex_in(0, Type::VEC2, "pos") .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC4, "color") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color") .vertex_source("gpu_shader_2D_vert.glsl") .fragment_source("gpu_shader_uniform_color_frag.glsl") .additional_info("gpu_srgb_to_framebuffer_space") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh index 3e7e4aeecda..63a4e679215 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh @@ -27,7 +27,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_depth_only) .vertex_in(0, Type::VEC3, "pos") .vertex_out(flat_color_iface) - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_3D_vert.glsl") .fragment_source("gpu_shader_depth_only_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh index 14a6986f478..4628f8bd6c4 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh @@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_flat_color) .vertex_in(1, Type::VEC4, "color") .vertex_out(flat_color_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_3D_flat_color_vert.glsl") .fragment_source("gpu_shader_flat_color_frag.glsl") .additional_info("gpu_srgb_to_framebuffer_space") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh index b829975448c..d47df129501 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh @@ -29,8 +29,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_image_modulate_alpha) .vertex_in(1, Type::VEC2, "texCoord") .vertex_out(smooth_tex_coord_interp_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::FLOAT, "alpha") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::FLOAT, "alpha") .sampler(0, ImageType::FLOAT_2D, "image", Frequency::PASS) .vertex_source("gpu_shader_3D_image_vert.glsl") .fragment_source("gpu_shader_image_modulate_alpha_frag.glsl") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh index d43ea799420..62c05d4677c 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh @@ -28,7 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color) .vertex_in(0, Type::VEC3, "pos") .vertex_out(flat_color_iface) - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_3D_line_dashed_uniform_color_vert.glsl") .fragment_source("gpu_shader_2D_line_dashed_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh index 27357eef8c9..d7b6806acc6 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh @@ -28,7 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_fixed_size_varying_color) .vertex_in(1, Type::VEC4, "color") .vertex_out(smooth_color_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_3D_point_fixed_size_varying_color_vert.glsl") .fragment_source("gpu_shader_point_varying_color_frag.glsl") .do_static_compilation(true); @@ -39,7 +39,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_varying_size_varying_color) .vertex_in(2, Type::FLOAT, "size") .vertex_out(smooth_color_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_3D_point_varying_size_varying_color_vert.glsl") .fragment_source("gpu_shader_point_varying_color_frag.glsl") .do_static_compilation(true); @@ -48,9 +48,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa) .vertex_in(0, Type::VEC3, "pos") .vertex_out(smooth_radii_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC4, "color") - .push_constant(20, Type::FLOAT, "size") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color") + .push_constant(Type::FLOAT, "size") .vertex_source("gpu_shader_3D_point_uniform_size_aa_vert.glsl") .fragment_source("gpu_shader_point_uniform_color_aa_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh index c99d9ed199d..10f6e9a5b83 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh @@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color) .vertex_in(1, Type::VEC4, "color") .vertex_out(smooth_color_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .vertex_source("gpu_shader_3D_smooth_color_vert.glsl") .fragment_source("gpu_shader_3D_smooth_color_frag.glsl") .additional_info("gpu_srgb_to_framebuffer_space") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh index 5b8517310ea..e96ce8842f1 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh @@ -26,8 +26,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_uniform_color) .vertex_in(0, Type::VEC3, "pos") .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC4, "color") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color") .vertex_source("gpu_shader_3D_vert.glsl") .fragment_source("gpu_shader_uniform_color_frag.glsl") .additional_info("gpu_srgb_to_framebuffer_space") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh index c337c399f59..460ef3f5acf 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh @@ -41,8 +41,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke) .uniform_buf(0, "GPencilStrokeData", "gpencil_stroke_data") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::MAT4, "ProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ProjectionMatrix") .vertex_source("gpu_shader_gpencil_stroke_vert.glsl") .geometry_source("gpu_shader_gpencil_stroke_geom.glsl") .fragment_source("gpu_shader_gpencil_stroke_frag.glsl") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh index 98a1fcf5b37..418ec0bef2d 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh @@ -31,7 +31,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_instance_varying_color_varying_size) .vertex_in(3, Type::FLOAT, "size") .vertex_out(flat_color_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ViewProjectionMatrix") + .push_constant(Type::MAT4, "ViewProjectionMatrix") .vertex_source("gpu_shader_instance_variying_size_variying_color_vert.glsl") .fragment_source("gpu_shader_flat_color_frag.glsl") .additional_info("gpu_srgb_to_framebuffer_space") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh index f8cb94e52d0..bbd3b6a9f01 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh @@ -38,9 +38,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_keyframe_shape) .vertex_in(4, Type ::INT, "flags") .vertex_out(keyframe_shape_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::VEC2, "ViewportSize") - .push_constant(24, Type::FLOAT, "outline_scale") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC2, "ViewportSize") + .push_constant(Type::FLOAT, "outline_scale") .vertex_source("gpu_shader_keyframe_shape_vert.glsl") .fragment_source("gpu_shader_keyframe_shape_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh index c3f86ed2b6f..8afa7d79f3f 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh @@ -32,8 +32,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_simple_lighting) .vertex_out(smooth_normal_iface) .fragment_out(0, Type::VEC4, "fragColor") .uniform_buf(0, "SimpleLightingData", "simple_lighting_data", Frequency::PASS) - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::MAT3, "NormalMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT3, "NormalMatrix") .typedef_source("GPU_shader_shared.h") .vertex_source("gpu_shader_3D_normal_vert.glsl") .fragment_source("gpu_shader_simple_lighting_frag.glsl") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_text_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_text_info.hh index 2c17a494d76..9429db66f90 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_text_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_text_info.hh @@ -37,7 +37,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_text) .vertex_in(3, Type ::INT, "offset") .vertex_out(text_iface) .fragment_out(0, Type::VEC4, "fragColor") - .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .sampler(0, ImageType::FLOAT_2D, "glyph", Frequency::PASS) .vertex_source("gpu_shader_text_vert.glsl") .fragment_source("gpu_shader_text_frag.glsl") diff --git a/source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh b/source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh index 3af49b56ab1..e9154bcaeda 100644 --- a/source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh @@ -24,4 +24,5 @@ #include "gpu_shader_create_info.hh" GPU_SHADER_CREATE_INFO(gpu_srgb_to_framebuffer_space) + .push_constant(Type::BOOL, "srgbTarget") .define("blender_srgb_to_framebuffer_space(a) a"); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl index 01a16e194ca..ab6024b073d 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl @@ -18,7 +18,7 @@ void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result) result.radiance = out_Diffuse_0.radiance; - /* TODO(fclem) Try to not use this. */ + /* TODO(@fclem): Try to not use this. */ closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 6ffa6b59572..59f0377869b 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -1,15 +1,4 @@ -float wang_hash_noise(uint s) -{ - s = (s ^ 61u) ^ (s >> 16u); - s *= 9u; - s = s ^ (s >> 4u); - s *= 0x27d4eb2du; - s = s ^ (s >> 15u); - - return fract(float(s) / 4294967296.0); -} - void node_hair_info(float hair_length, out float is_strand, out float intercept, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl index 86191451e5f..cb798047791 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl @@ -215,3 +215,14 @@ float integer_noise(int n) nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; return 0.5 * (float(nn) / 1073741824.0); } + +float wang_hash_noise(uint s) +{ + s = (s ^ 61u) ^ (s >> 16u); + s *= 9u; + s = s ^ (s >> 4u); + s *= 0x27d4eb2du; + s = s ^ (s >> 15u); + + return fract(float(s) / 4294967296.0); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl new file mode 100644 index 00000000000..d717ac97b28 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl @@ -0,0 +1,13 @@ + +void node_point_info(out vec3 position, out float radius, out float random) +{ +#ifdef POINTCLOUD_SHADER + position = pointPosition; + radius = pointRadius; + random = wang_hash_noise(uint(pointID)); +#else + position = vec3(0.0, 0.0, 0.0); + radius = 0.0; + random = 0.0; +#endif +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index bba84c2be52..c97fc090fe2 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -166,7 +166,7 @@ void node_bsdf_principled(vec4 base_color, float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, in_Refraction_3.roughness, in_Refraction_3.ior).x; - /* TODO(fclem) This could be going to a transmission render pass instead. */ + /* TODO(@fclem): This could be going to a transmission render pass instead. */ out_Refraction_3.radiance *= btdf; out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance); out_Refraction_3.radiance *= base_color.rgb; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl index 7cbc7218f5c..8a42a131f43 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl @@ -21,7 +21,7 @@ void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Cl result.radiance = out_Refraction_0.radiance; - /* TODO(fclem) Try to not use this. */ + /* TODO(@fclem): Try to not use this. */ result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N, viewCameraVec(viewPosition)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl index d0c159cdf37..20b634aa801 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl @@ -22,7 +22,7 @@ void node_subsurface_scattering(vec4 color, closure_load_sss_data(scale, out_Diffuse_0.radiance, color.rgb, int(sss_id), result); - /* TODO(fclem) Try to not use this. */ + /* TODO(@fclem): Try to not use this. */ closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 65d7631445d..a557d7dc6d1 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -378,8 +378,9 @@ struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, int quality, - bool overwrite, - struct GSet *file_list); + const bool overwrite, + struct GSet *file_list, + bool build_only_on_bad_performance); /** * Will rebuild all used indices and proxies at once. @@ -431,6 +432,7 @@ bool IMB_anim_can_produce_frames(const struct anim *anim); int ismovie(const char *filepath); int IMB_anim_get_image_width(struct anim *anim); int IMB_anim_get_image_height(struct anim *anim); +bool IMB_get_gop_decode_time(struct anim *anim); /** * diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 6a05b681c88..38dbb9bfc47 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -504,11 +504,6 @@ static ImBuf *avi_fetchibuf(struct anim *anim, int position) #ifdef WITH_FFMPEG -BLI_INLINE bool need_aligned_ffmpeg_buffer(struct anim *anim) -{ - return (anim->x & 31) != 0; -} - static int startffmpeg(struct anim *anim) { int i, video_stream_index; @@ -707,23 +702,20 @@ static int startffmpeg(struct anim *anim) anim->pFrameComplete = false; anim->pFrameDeinterlaced = av_frame_alloc(); anim->pFrameRGB = av_frame_alloc(); + anim->pFrameRGB->format = AV_PIX_FMT_RGBA; + anim->pFrameRGB->width = anim->x; + anim->pFrameRGB->height = anim->y; - if (need_aligned_ffmpeg_buffer(anim)) { - anim->pFrameRGB->format = AV_PIX_FMT_RGBA; - anim->pFrameRGB->width = anim->x; - anim->pFrameRGB->height = anim->y; - - if (av_frame_get_buffer(anim->pFrameRGB, 32) < 0) { - fprintf(stderr, "Could not allocate frame data.\n"); - avcodec_free_context(&anim->pCodecCtx); - avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->cur_packet); - av_frame_free(&anim->pFrameRGB); - av_frame_free(&anim->pFrameDeinterlaced); - av_frame_free(&anim->pFrame); - anim->pCodecCtx = NULL; - return -1; - } + if (av_frame_get_buffer(anim->pFrameRGB, 0) < 0) { + fprintf(stderr, "Could not allocate frame data.\n"); + avcodec_free_context(&anim->pCodecCtx); + avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->cur_packet); + av_frame_free(&anim->pFrameRGB); + av_frame_free(&anim->pFrameDeinterlaced); + av_frame_free(&anim->pFrame); + anim->pCodecCtx = NULL; + return -1; } if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) { @@ -851,92 +843,26 @@ static void ffmpeg_postprocess(struct anim *anim) } } - if (!need_aligned_ffmpeg_buffer(anim)) { - av_image_fill_arrays(anim->pFrameRGB->data, - anim->pFrameRGB->linesize, - (unsigned char *)ibuf->rect, - AV_PIX_FMT_RGBA, - anim->x, - anim->y, - 1); - } - -# if defined(__x86_64__) || defined(_M_X64) - /* Scale and flip image over Y axis in one go, using negative strides. - * This doesn't work with ARM/PowerPC though and may be misusing the API. - * Limit it x86_64 where it appears to work. - * http://trac.ffmpeg.org/ticket/9060 */ - int *dstStride = anim->pFrameRGB->linesize; - uint8_t **dst = anim->pFrameRGB->data; - const int dstStride2[4] = {-dstStride[0], 0, 0, 0}; - uint8_t *dst2[4] = {dst[0] + (anim->y - 1) * dstStride[0], 0, 0, 0}; - - sws_scale(anim->img_convert_ctx, - (const uint8_t *const *)input->data, - input->linesize, - 0, - anim->y, - dst2, - dstStride2); -# else - /* Scale with swscale. */ - int *dstStride = anim->pFrameRGB->linesize; - uint8_t **dst = anim->pFrameRGB->data; - const int dstStride2[4] = {dstStride[0], 0, 0, 0}; - uint8_t *dst2[4] = {dst[0], 0, 0, 0}; - int x, y, h, w; - unsigned char *bottom; - unsigned char *top; - sws_scale(anim->img_convert_ctx, (const uint8_t *const *)input->data, input->linesize, 0, anim->y, - dst2, - dstStride2); - - /* Flip destination image buffer over Y axis. */ - bottom = (unsigned char *)dst[0]; - top = bottom + anim->x * (anim->y - 1) * 4; - - h = (anim->y + 1) / 2; - w = anim->x; - - for (y = 0; y < h; y++) { - unsigned char tmp[4]; - unsigned int *tmp_l = (unsigned int *)tmp; - - for (x = 0; x < w; x++) { - tmp[0] = bottom[0]; - tmp[1] = bottom[1]; - tmp[2] = bottom[2]; - tmp[3] = bottom[3]; - - bottom[0] = top[0]; - bottom[1] = top[1]; - bottom[2] = top[2]; - bottom[3] = top[3]; - - *(unsigned int *)top = *tmp_l; - - bottom += 4; - top += 4; - } - top -= 8 * w; - } -# endif - - if (need_aligned_ffmpeg_buffer(anim)) { - uint8_t *buf_src = anim->pFrameRGB->data[0]; - uint8_t *buf_dst = (uint8_t *)ibuf->rect; - for (int y = 0; y < anim->y; y++) { - memcpy(buf_dst, buf_src, anim->x * 4); - buf_dst += anim->x * 4; - buf_src += anim->pFrameRGB->linesize[0]; - } - } - + anim->pFrameRGB->data, + anim->pFrameRGB->linesize); + + /* Copy the valid bytes from the aligned buffer vertically flipped into ImBuf */ + int aligned_stride = anim->pFrameRGB->linesize[0]; + const uint8_t *const src[4] = { + anim->pFrameRGB->data[0] + (anim->y - 1) * aligned_stride, 0, 0, 0}; + /* NOTE: Negative linesize is used to copy and flip image at once with function + * `av_image_copy_to_buffer`. This could cause issues in future and image may need to be flipped + * explicitly. */ + const int src_linesize[4] = {-anim->pFrameRGB->linesize[0], 0, 0, 0}; + int dst_size = av_image_get_buffer_size( + anim->pFrameRGB->format, anim->pFrameRGB->width, anim->pFrameRGB->height, 1); + av_image_copy_to_buffer( + (uint8_t *)ibuf->rect, dst_size, src, src_linesize, AV_PIX_FMT_RGBA, anim->x, anim->y, 1); if (filter_y) { IMB_filtery(ibuf); } @@ -1067,21 +993,23 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) return false; } -static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search) +static double ffmpeg_steps_per_frame_get(struct anim *anim) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - AVRational frame_rate = v_st->r_frame_rate; AVRational time_base = v_st->time_base; - double steps_per_frame = (double)(frame_rate.den * time_base.den) / - (double)(frame_rate.num * time_base.num); + AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL); + return av_q2d(av_inv_q(av_mul_q(frame_rate, time_base))); + ; +} + +static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search) +{ /* Step back half a frame position to make sure that we get the requested * frame and not the one after it. This is a workaround as ffmpeg will * sometimes not seek to a frame after the requested pts even if * AVSEEK_FLAG_BACKWARD is specified. */ - int64_t pts = pts_to_search - (steps_per_frame / 2); - - return pts; + return pts_to_search - (ffmpeg_steps_per_frame_get(anim) / 2); } /* This gives us an estimate of which pts our requested frame will have. @@ -1100,13 +1028,8 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim, else { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; int64_t start_pts = v_st->start_time; - AVRational frame_rate = v_st->r_frame_rate; - AVRational time_base = v_st->time_base; - - double steps_per_frame = (double)(frame_rate.den * time_base.den) / - (double)(frame_rate.num * time_base.num); - pts_to_search = round(position * steps_per_frame); + pts_to_search = round(position * ffmpeg_steps_per_frame_get(anim)); if (start_pts != AV_NOPTS_VALUE) { pts_to_search += start_pts; @@ -1196,13 +1119,6 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t *requested_pts, int64_t pts_to_search) { - AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - AVRational frame_rate = v_st->r_frame_rate; - AVRational time_base = v_st->time_base; - - double steps_per_frame = (double)(frame_rate.den * time_base.den) / - (double)(frame_rate.num * time_base.num); - int64_t current_pts = *requested_pts; int64_t offset = 0; @@ -1210,7 +1126,7 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, /* Step backward frame by frame until we find the key frame we are looking for. */ while (current_pts != 0) { - current_pts = *requested_pts - (int64_t)round(offset * steps_per_frame); + current_pts = *requested_pts - (int64_t)round(offset * ffmpeg_steps_per_frame_get(anim)); current_pts = MAX2(current_pts, 0); /* Seek to timestamp. */ @@ -1482,20 +1398,6 @@ static void free_anim_ffmpeg(struct anim *anim) av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrame); - - if (!need_aligned_ffmpeg_buffer(anim)) { - /* If there's no need for own aligned buffer it means that FFmpeg's - * frame shares the same buffer as temporary ImBuf. In this case we - * should not free the buffer when freeing the FFmpeg buffer. - */ - av_image_fill_arrays(anim->pFrameRGB->data, - anim->pFrameRGB->linesize, - NULL, - AV_PIX_FMT_RGBA, - anim->x, - anim->y, - 1); - } av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index ff86fbfdd38..f23748e59a2 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -104,7 +104,7 @@ MINLINE void float_to_byte_dither_v4( bool IMB_alpha_affects_rgb(const ImBuf *ibuf) { - return (ibuf->flags & IB_alphamode_channel_packed) == 0; + return ibuf && (ibuf->flags & IB_alphamode_channel_packed) == 0; } void IMB_buffer_byte_from_float(uchar *rect_to, diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 6cd87e29c9d..00e96e7840b 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -37,6 +37,8 @@ # include "BLI_winstuff.h" #endif +#include "PIL_time.h" + #include "IMB_anim.h" #include "IMB_indexer.h" #include "imbuf.h" @@ -170,7 +172,6 @@ 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; } @@ -246,8 +247,10 @@ struct anim_index *IMB_indexer_open(const char *name) uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index) { - if (frame_index < 0) { - frame_index = 0; + /* This is hard coded, because our current timecode files return non zero seek position for index + * 0. Only when seeking to 0 it is guaranteed, that first packet will be read. */ + if (frame_index <= 0) { + return 0; } if (frame_index >= idx->num_entries) { frame_index = idx->num_entries - 1; @@ -815,12 +818,16 @@ typedef struct FFmpegIndexBuilderContext { double pts_time_base; int frameno, frameno_gapless; int start_pts_set; + + bool build_only_on_bad_performance; + bool building_cancelled; } FFmpegIndexBuilderContext; static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, - int quality) + int quality, + bool build_only_on_bad_performance) { FFmpegIndexBuilderContext *context = MEM_callocN(sizeof(FFmpegIndexBuilderContext), "FFmpeg index builder context"); @@ -832,6 +839,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, context->proxy_sizes_in_use = proxy_sizes_in_use; context->num_proxy_sizes = IMB_PROXY_MAX_SLOT; context->num_indexers = IMB_TC_MAX_SLOT; + context->build_only_on_bad_performance = build_only_on_bad_performance; memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx)); memset(context->indexer, 0, sizeof(context->indexer)); @@ -937,15 +945,17 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int { int i; + const bool do_rollback = stop || context->building_cancelled; + for (i = 0; i < context->num_indexers; i++) { if (context->tcs_in_use & tc_types[i]) { - IMB_index_builder_finish(context->indexer[i], stop); + IMB_index_builder_finish(context->indexer[i], do_rollback); } } for (i = 0; i < context->num_proxy_sizes; i++) { if (context->proxy_sizes_in_use & proxy_sizes[i]) { - free_proxy_output_ffmpeg(context->proxy_ctx[i], stop); + free_proxy_output_ffmpeg(context->proxy_ctx[i], do_rollback); } } @@ -1096,6 +1106,111 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, return 1; } +/* Get number of frames, that can be decoded in specified time period. */ +static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *context, + const double time_period) +{ + AVFrame *in_frame = av_frame_alloc(); + AVPacket *packet = av_packet_alloc(); + + const double start = PIL_check_seconds_timer(); + int frames_decoded = 0; + + while (av_read_frame(context->iFormatCtx, packet) >= 0) { + if (packet->stream_index != context->videoStream) { + continue; + } + + int ret = avcodec_send_packet(context->iCodecCtx, packet); + while (ret >= 0) { + ret = avcodec_receive_frame(context->iCodecCtx, in_frame); + + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } + + if (ret < 0) { + fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret)); + break; + } + frames_decoded++; + } + + const double end = PIL_check_seconds_timer(); + + if (end > start + time_period) { + break; + } + } + + avcodec_flush_buffers(context->iCodecCtx); + av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD); + return frames_decoded; +} + +/* Read up to 10k movie packets and return max GOP size detected. + * Number of packets is arbitrary. It should be as large as possible, but processed within + * reasonable time period, so detected GOP size is as close to real as possible. */ +static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *context) +{ + AVPacket *packet = av_packet_alloc(); + + const int packets_max = 10000; + int packet_index = 0; + int max_gop = 0; + int cur_gop = 0; + + while (av_read_frame(context->iFormatCtx, packet) >= 0) { + if (packet->stream_index != context->videoStream) { + continue; + } + packet_index++; + cur_gop++; + + if (packet->flags & AV_PKT_FLAG_KEY) { + max_gop = max_ii(max_gop, cur_gop); + cur_gop = 0; + } + + if (packet_index > packets_max) { + break; + } + } + + av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD); + return max_gop; +} + +/* Assess scrubbing performance of provided file. This function is not meant to be very exact. + * It compares number number of frames decoded in reasonable time with largest detected GOP size. + * Because seeking happens in single GOP, it means, that maximum seek time can be detected this + * way. + * Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or + * equal. + */ +static bool indexer_need_to_build_proxy(FFmpegIndexBuilderContext *context) +{ + if (!context->build_only_on_bad_performance) { + return true; + } + + /* Make sure, that file is not cold read. */ + indexer_performance_get_decode_rate(context, 0.1); + /* Get decode rate per 100ms. This is arbitrary, but seems to be good baseline cadence of + * seeking. */ + const int decode_rate = indexer_performance_get_decode_rate(context, 0.1); + const int max_gop_size = indexer_performance_get_max_gop_size(context); + + if (max_gop_size <= 10 || max_gop_size < decode_rate) { + printf("Skipping proxy building for %s: Decoding performance is already good.\n", + context->iFormatCtx->url); + context->building_cancelled = true; + return false; + } + + return true; +} + #endif /* ---------------------------------------------------------------------- @@ -1275,7 +1390,8 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_sizes_in_use, int quality, const bool overwrite, - GSet *file_list) + GSet *file_list, + bool build_only_on_bad_performance) { IndexBuildContext *context = NULL; IMB_Proxy_Size proxy_sizes_to_build = proxy_sizes_in_use; @@ -1329,9 +1445,13 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, switch (anim->curtype) { #ifdef WITH_FFMPEG case ANIM_FFMPEG: - context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); + context = index_ffmpeg_create_context( + anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance); break; +#else + UNUSED_VARS(build_only_on_bad_performance); #endif + #ifdef WITH_AVI default: context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); @@ -1359,7 +1479,9 @@ void IMB_anim_index_rebuild(struct IndexBuildContext *context, switch (context->anim_type) { #ifdef WITH_FFMPEG case ANIM_FFMPEG: - index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress); + if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) { + index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress); + } break; #endif #ifdef WITH_AVI diff --git a/source/blender/io/collada/CMakeLists.txt b/source/blender/io/collada/CMakeLists.txt index e1645083116..9ce3389257d 100644 --- a/source/blender/io/collada/CMakeLists.txt +++ b/source/blender/io/collada/CMakeLists.txt @@ -135,10 +135,6 @@ if(WITH_BUILDINFO) add_definitions(-DWITH_BUILDINFO) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(CMAKE_COMPILER_IS_GNUCXX) # COLLADAFWArray.h gives error with gcc 4.5 string(APPEND CMAKE_CXX_FLAGS " -fpermissive") diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index 12015bf1698..980f33fffa1 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC intern/usd_writer_camera.cc intern/usd_writer_hair.cc intern/usd_writer_light.cc + intern/usd_writer_material.cc intern/usd_writer_mesh.cc intern/usd_writer_metaball.cc intern/usd_writer_transform.cc @@ -89,6 +90,7 @@ set(SRC intern/usd_writer_camera.h intern/usd_writer_hair.h intern/usd_writer_light.h + intern/usd_writer_material.h intern/usd_writer_mesh.h intern/usd_writer_metaball.h intern/usd_writer_transform.h diff --git a/source/blender/io/usd/intern/usd_reader_camera.cc b/source/blender/io/usd/intern/usd_reader_camera.cc index 2732ed5770d..1d001e19140 100644 --- a/source/blender/io/usd/intern/usd_reader_camera.cc +++ b/source/blender/io/usd/intern/usd_reader_camera.cc @@ -72,7 +72,7 @@ void USDCameraReader::read_object_data(Main *bmain, const double motionSampleTim cam_prim.GetHorizontalApertureAttr().Get(&horAp, motionSampleTime); bcam->lens = val.Get<float>(); - /* TODO(makowalski) */ + /* TODO(@makowalski): support sensor size. */ #if 0 bcam->sensor_x = 0.0f; bcam->sensor_y = 0.0f; diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index 2b5326eb4c1..a358c563c88 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -18,11 +18,15 @@ */ #include "usd_writer_abstract.h" #include "usd_hierarchy_iterator.h" +#include "usd_writer_material.h" #include <pxr/base/tf/stringUtils.h> +#include "BKE_customdata.h" #include "BLI_assert.h" +#include "DNA_mesh_types.h" + /* TfToken objects are not cheap to construct, so we do it once. */ namespace usdtokens { /* Materials */ @@ -34,6 +38,19 @@ static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal); static const pxr::TfToken surface("surface", pxr::TfToken::Immortal); } // namespace usdtokens +static std::string get_mesh_active_uvlayer_name(const Object *ob) +{ + if (!ob || ob->type != OB_MESH || !ob->data) { + return ""; + } + + const Mesh *me = static_cast<Mesh *>(ob->data); + + const char *name = CustomData_get_active_layer_name(&me->ldata, CD_MLOOPUV); + + return name ? name : ""; +} + namespace blender::io::usd { USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_context) @@ -78,7 +95,8 @@ const pxr::SdfPath &USDAbstractWriter::usd_path() const return usd_export_context_.usd_path; } -pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material) +pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(const HierarchyContext &context, + Material *material) { static pxr::SdfPath material_library_path("/_materials"); pxr::UsdStageRefPtr stage = usd_export_context_.stage; @@ -92,17 +110,14 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material) } usd_material = pxr::UsdShadeMaterial::Define(stage, usd_path); - /* Construct the shader. */ - pxr::SdfPath shader_path = usd_path.AppendChild(usdtokens::preview_shader); - pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(stage, shader_path); - shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface)); - shader.CreateInput(usdtokens::diffuse_color, pxr::SdfValueTypeNames->Color3f) - .Set(pxr::GfVec3f(material->r, material->g, material->b)); - shader.CreateInput(usdtokens::roughness, pxr::SdfValueTypeNames->Float).Set(material->roughness); - shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic); - - /* Connect the shader and the material together. */ - usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface); + if (material->use_nodes && this->usd_export_context_.export_params.generate_preview_surface) { + std::string active_uv = get_mesh_active_uvlayer_name(context.object); + create_usd_preview_surface_material( + this->usd_export_context_, material, usd_material, active_uv); + } + else { + create_usd_viewport_material(this->usd_export_context_, material, usd_material); + } return usd_material; } diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index dd81dd47c83..c67aa824263 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -69,7 +69,7 @@ class USDAbstractWriter : public AbstractHierarchyWriter { virtual void do_write(HierarchyContext &context) = 0; pxr::UsdTimeCode get_export_time_code() const; - pxr::UsdShadeMaterial ensure_usd_material(Material *material); + pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material); void write_visibility(const HierarchyContext &context, const pxr::UsdTimeCode timecode, diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc new file mode 100644 index 00000000000..5ce04339503 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -0,0 +1,767 @@ +/* + * 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 "usd_writer_material.h" + +#include "usd.h" +#include "usd_exporter_context.h" + +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_node.h" + +#include "BLI_fileops.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "DNA_material_types.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" + +#include <pxr/base/tf/stringUtils.h> +#include <pxr/pxr.h> +#include <pxr/usd/usdGeom/scope.h> + +#include <iostream> + +/* TfToken objects are not cheap to construct, so we do it once. */ +namespace usdtokens { +// Materials +static const pxr::TfToken clearcoat("clearcoat", pxr::TfToken::Immortal); +static const pxr::TfToken clearcoatRoughness("clearcoatRoughness", pxr::TfToken::Immortal); +static const pxr::TfToken diffuse_color("diffuseColor", pxr::TfToken::Immortal); +static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal); +static const pxr::TfToken preview_shader("previewShader", pxr::TfToken::Immortal); +static const pxr::TfToken preview_surface("UsdPreviewSurface", pxr::TfToken::Immortal); +static const pxr::TfToken uv_texture("UsdUVTexture", pxr::TfToken::Immortal); +static const pxr::TfToken primvar_float2("UsdPrimvarReader_float2", pxr::TfToken::Immortal); +static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal); +static const pxr::TfToken specular("specular", pxr::TfToken::Immortal); +static const pxr::TfToken opacity("opacity", pxr::TfToken::Immortal); +static const pxr::TfToken surface("surface", pxr::TfToken::Immortal); +static const pxr::TfToken perspective("perspective", pxr::TfToken::Immortal); +static const pxr::TfToken orthographic("orthographic", pxr::TfToken::Immortal); +static const pxr::TfToken rgb("rgb", pxr::TfToken::Immortal); +static const pxr::TfToken r("r", pxr::TfToken::Immortal); +static const pxr::TfToken g("g", pxr::TfToken::Immortal); +static const pxr::TfToken b("b", pxr::TfToken::Immortal); +static const pxr::TfToken st("st", pxr::TfToken::Immortal); +static const pxr::TfToken result("result", pxr::TfToken::Immortal); +static const pxr::TfToken varname("varname", pxr::TfToken::Immortal); +static const pxr::TfToken out("out", pxr::TfToken::Immortal); +static const pxr::TfToken normal("normal", pxr::TfToken::Immortal); +static const pxr::TfToken ior("ior", pxr::TfToken::Immortal); +static const pxr::TfToken file("file", pxr::TfToken::Immortal); +static const pxr::TfToken preview("preview", pxr::TfToken::Immortal); +static const pxr::TfToken raw("raw", pxr::TfToken::Immortal); +static const pxr::TfToken sRGB("sRGB", pxr::TfToken::Immortal); +static const pxr::TfToken sourceColorSpace("sourceColorSpace", pxr::TfToken::Immortal); +static const pxr::TfToken Shader("Shader", pxr::TfToken::Immortal); +} // namespace usdtokens + +/* Cycles specific tokens. */ +namespace cyclestokens { +static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal); +} // namespace cyclestokens + +namespace blender::io::usd { + +/* Preview surface input specification. */ +struct InputSpec { + pxr::TfToken input_name; + pxr::SdfValueTypeName input_type; + pxr::TfToken source_name; + /* Whether a default value should be set + * if the node socket has not input. Usually + * false for the Normal input. */ + bool set_default_value; +}; + +/* Map Blender socket names to USD Preview Surface InputSpec structs. */ +typedef std::map<std::string, InputSpec> InputSpecMap; + +/* Static function forward declarations. */ +static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &usd_export_context, + pxr::UsdShadeMaterial &material, + const char *name, + int type); +static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &usd_export_context, + pxr::UsdShadeMaterial &material, + bNode *node); +static void create_uvmap_shader(const USDExporterContext &usd_export_context, + bNode *tex_node, + pxr::UsdShadeMaterial &usd_material, + pxr::UsdShadeShader &usd_tex_shader, + const pxr::TfToken &default_uv); +static void export_texture(bNode *node, + const pxr::UsdStageRefPtr stage, + const bool allow_overwrite = false); +static bNode *find_bsdf_node(Material *material); +static void get_absolute_path(Image *ima, char *r_path); +static std::string get_tex_image_asset_path(bNode *node, + const pxr::UsdStageRefPtr stage, + const USDExportParams &export_params); +static InputSpecMap &preview_surface_input_map(); +static bNode *traverse_channel(bNodeSocket *input, short target_type); + +template<typename T1, typename T2> +void create_input(pxr::UsdShadeShader &shader, const InputSpec &spec, const void *value); + +void create_usd_preview_surface_material(const USDExporterContext &usd_export_context, + Material *material, + pxr::UsdShadeMaterial &usd_material, + const std::string &default_uv) +{ + if (!material) { + return; + } + + /* Define a 'preview' scope beneath the material which will contain the preview shaders. */ + pxr::UsdGeomScope::Define(usd_export_context.stage, + usd_material.GetPath().AppendChild(usdtokens::preview)); + + /* Default map when creating UV primvar reader shaders. */ + pxr::TfToken default_uv_sampler = default_uv.empty() ? cyclestokens::UVMap : + pxr::TfToken(default_uv); + + /* We only handle the first instance of either principled or + * diffuse bsdf nodes in the material's node tree, because + * USD Preview Surface has no concept of layering materials. */ + bNode *node = find_bsdf_node(material); + if (!node) { + return; + } + + pxr::UsdShadeShader preview_surface = create_usd_preview_shader( + usd_export_context, usd_material, node); + + const InputSpecMap &input_map = preview_surface_input_map(); + + /* Set the preview surface inputs. */ + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + + /* Check if this socket is mapped to a USD preview shader input. */ + const InputSpecMap::const_iterator it = input_map.find(sock->name); + + if (it == input_map.end()) { + continue; + } + + pxr::UsdShadeShader created_shader; + + bNode *input_node = traverse_channel(sock, SH_NODE_TEX_IMAGE); + + const InputSpec &input_spec = it->second; + + if (input_node) { + /* Create connection. */ + created_shader = create_usd_preview_shader(usd_export_context, usd_material, input_node); + + preview_surface.CreateInput(input_spec.input_name, input_spec.input_type) + .ConnectToSource(created_shader, input_spec.source_name); + } + else if (input_spec.set_default_value) { + /* Set hardcoded value. */ + switch (sock->type) { + case SOCK_FLOAT: { + create_input<bNodeSocketValueFloat, float>( + preview_surface, input_spec, sock->default_value); + } break; + case SOCK_VECTOR: { + create_input<bNodeSocketValueVector, pxr::GfVec3f>( + preview_surface, input_spec, sock->default_value); + } break; + case SOCK_RGBA: { + create_input<bNodeSocketValueRGBA, pxr::GfVec3f>( + preview_surface, input_spec, sock->default_value); + } break; + default: + break; + } + } + + /* If any input texture node has been found, export the texture, if necessary, + * and look for a connected uv node. */ + if (!(created_shader && input_node && input_node->type == SH_NODE_TEX_IMAGE)) { + continue; + } + + if (usd_export_context.export_params.export_textures) { + export_texture(input_node, + usd_export_context.stage, + usd_export_context.export_params.overwrite_textures); + } + + create_uvmap_shader( + usd_export_context, input_node, usd_material, created_shader, default_uv_sampler); + } +} + +void create_usd_viewport_material(const USDExporterContext &usd_export_context, + Material *material, + pxr::UsdShadeMaterial &usd_material) +{ + /* Construct the shader. */ + pxr::SdfPath shader_path = usd_material.GetPath().AppendChild(usdtokens::preview_shader); + pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(usd_export_context.stage, shader_path); + + shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface)); + shader.CreateInput(usdtokens::diffuse_color, pxr::SdfValueTypeNames->Color3f) + .Set(pxr::GfVec3f(material->r, material->g, material->b)); + shader.CreateInput(usdtokens::roughness, pxr::SdfValueTypeNames->Float).Set(material->roughness); + shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic); + + /* Connect the shader and the material together. */ + usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface); +} + +/* Return USD Preview Surface input map singleton. */ +static InputSpecMap &preview_surface_input_map() +{ + static InputSpecMap input_map = { + {"Base Color", + {usdtokens::diffuse_color, pxr::SdfValueTypeNames->Float3, usdtokens::rgb, true}}, + {"Color", {usdtokens::diffuse_color, pxr::SdfValueTypeNames->Float3, usdtokens::rgb, true}}, + {"Roughness", {usdtokens::roughness, pxr::SdfValueTypeNames->Float, usdtokens::r, true}}, + {"Metallic", {usdtokens::metallic, pxr::SdfValueTypeNames->Float, usdtokens::r, true}}, + {"Specular", {usdtokens::specular, pxr::SdfValueTypeNames->Float, usdtokens::r, true}}, + {"Alpha", {usdtokens::opacity, pxr::SdfValueTypeNames->Float, usdtokens::r, true}}, + {"IOR", {usdtokens::ior, pxr::SdfValueTypeNames->Float, usdtokens::r, true}}, + /* Note that for the Normal input set_default_value is false. */ + {"Normal", {usdtokens::normal, pxr::SdfValueTypeNames->Float3, usdtokens::rgb, false}}, + {"Clearcoat", {usdtokens::clearcoat, pxr::SdfValueTypeNames->Float, usdtokens::r, true}}, + {"Clearcoat Roughness", + {usdtokens::clearcoatRoughness, pxr::SdfValueTypeNames->Float, usdtokens::r, true}}, + }; + + return input_map; +} + +/* Create an input on the given shader with name and type + * provided by the InputSpec and assign the given value to the + * input. Parameters T1 and T2 indicate the Blender and USD + * value types, respectively. */ +template<typename T1, typename T2> +void create_input(pxr::UsdShadeShader &shader, const InputSpec &spec, const void *value) +{ + const T1 *cast_value = static_cast<const T1 *>(value); + shader.CreateInput(spec.input_name, spec.input_type).Set(T2(cast_value->value)); +} + +/* Find the UVMAP node input to the given texture image node and convert it + * to a USD primvar reader shader. If no UVMAP node is found, create a primvar + * reader for the given default uv set. The primvar reader will be attached to + * the 'st' input of the given USD texture shader. */ +static void create_uvmap_shader(const USDExporterContext &usd_export_context, + bNode *tex_node, + pxr::UsdShadeMaterial &usd_material, + pxr::UsdShadeShader &usd_tex_shader, + const pxr::TfToken &default_uv) +{ + bool found_uv_node = false; + + /* Find UV input to the texture node. */ + LISTBASE_FOREACH (bNodeSocket *, tex_node_sock, &tex_node->inputs) { + + if (!tex_node_sock->link || !STREQ(tex_node_sock->name, "Vector")) { + continue; + } + + bNode *uv_node = traverse_channel(tex_node_sock, SH_NODE_UVMAP); + if (uv_node == nullptr) { + continue; + } + + pxr::UsdShadeShader uv_shader = create_usd_preview_shader( + usd_export_context, usd_material, uv_node); + + if (!uv_shader.GetPrim().IsValid()) { + continue; + } + + found_uv_node = true; + + if (NodeShaderUVMap *shader_uv_map = static_cast<NodeShaderUVMap *>(uv_node->storage)) { + /* We need to make valid here because actual uv primvar has been. */ + std::string uv_set = pxr::TfMakeValidIdentifier(shader_uv_map->uv_map); + + uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token) + .Set(pxr::TfToken(uv_set)); + usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2) + .ConnectToSource(uv_shader, usdtokens::result); + } + else { + uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token).Set(default_uv); + usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2) + .ConnectToSource(uv_shader, usdtokens::result); + } + } + + if (!found_uv_node) { + /* No UVMAP node was linked to the texture node. However, we generate + * a primvar reader node that specifies the UV set to sample, as some + * DCCs require this. */ + + pxr::UsdShadeShader uv_shader = create_usd_preview_shader( + usd_export_context, usd_material, "uvmap", SH_NODE_TEX_COORD); + + if (uv_shader.GetPrim().IsValid()) { + uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token).Set(default_uv); + usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2) + .ConnectToSource(uv_shader, usdtokens::result); + } + } +} + +/* Generate a file name for an in-memory image that doesn't have a + * filepath already defined. */ +static std::string get_in_memory_texture_filename(Image *ima) +{ + bool is_dirty = BKE_image_is_dirty(ima); + bool is_generated = ima->source == IMA_SRC_GENERATED; + bool is_packed = BKE_image_has_packedfile(ima); + if (!(is_generated || is_dirty || is_packed)) { + return ""; + } + + /* Determine the correct file extension from the image format. */ + ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr); + if (!imbuf) { + return ""; + } + + ImageFormatData imageFormat; + BKE_imbuf_to_image_format(&imageFormat, imbuf); + + char file_name[FILE_MAX]; + /* Use the image name for the file name. */ + strcpy(file_name, ima->id.name + 2); + + BKE_image_path_ensure_ext_from_imformat(file_name, &imageFormat); + + return file_name; +} + +static void export_in_memory_texture(Image *ima, + const std::string &export_dir, + const bool allow_overwrite) +{ + char image_abs_path[FILE_MAX]; + + char file_name[FILE_MAX]; + if (strlen(ima->filepath) > 0) { + get_absolute_path(ima, image_abs_path); + BLI_split_file_part(image_abs_path, file_name, FILE_MAX); + } + else { + /* Use the image name for the file name. */ + strcpy(file_name, ima->id.name + 2); + } + + ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr); + if (!imbuf) { + return; + } + + ImageFormatData imageFormat; + BKE_imbuf_to_image_format(&imageFormat, imbuf); + + /* This image in its current state only exists in Blender memory. + * So we have to export it. The export will keep the image state intact, + * so the exported file will not be associated with the image. */ + + BKE_image_path_ensure_ext_from_imformat(file_name, &imageFormat); + + char export_path[FILE_MAX]; + BLI_path_join(export_path, FILE_MAX, export_dir.c_str(), file_name, nullptr); + + if (!allow_overwrite && BLI_exists(export_path)) { + return; + } + + if ((BLI_path_cmp_normalized(export_path, image_abs_path) == 0) && BLI_exists(image_abs_path)) { + /* As a precaution, don't overwrite the original path. */ + return; + } + + std::cout << "Exporting in-memory texture to " << export_path << std::endl; + + if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) { + WM_reportf(RPT_WARNING, "USD export: couldn't export in-memory texture to %s", export_path); + } +} + +/* Get the absolute filepath of the given image. Assumes + * r_path result array is of length FILE_MAX. */ +static void get_absolute_path(Image *ima, char *r_path) +{ + /* Make absolute source path. */ + BLI_strncpy(r_path, ima->filepath, FILE_MAX); + BLI_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); + BLI_path_normalize(nullptr, r_path); +} + +static pxr::TfToken get_node_tex_image_color_space(bNode *node) +{ + if (!node->id) { + return pxr::TfToken(); + } + + Image *ima = reinterpret_cast<Image *>(node->id); + + if (strcmp(ima->colorspace_settings.name, "Raw") == 0) { + return usdtokens::raw; + } + if (strcmp(ima->colorspace_settings.name, "Non-Color") == 0) { + return usdtokens::raw; + } + if (strcmp(ima->colorspace_settings.name, "sRGB") == 0) { + return usdtokens::sRGB; + } + + return pxr::TfToken(); +} + +/* Search the upstream nodes connected to the given socket and return the first occurrence + * of the node of the given type. Return null if no node of this type was found. */ +static bNode *traverse_channel(bNodeSocket *input, const short target_type) +{ + if (!input->link) { + return nullptr; + } + + bNode *linked_node = input->link->fromnode; + if (linked_node->type == target_type) { + /* Return match. */ + return linked_node; + } + + /* Recursively traverse the linked node's sockets. */ + LISTBASE_FOREACH (bNodeSocket *, sock, &linked_node->inputs) { + if (bNode *found_node = traverse_channel(sock, target_type)) { + return found_node; + } + } + + return nullptr; +} + +/* Returns the first occurrence of a principled BSDF or a diffuse BSDF node found in the given + * material's node tree. Returns null if no instance of either type was found.*/ +static bNode *find_bsdf_node(Material *material) +{ + LISTBASE_FOREACH (bNode *, node, &material->nodetree->nodes) { + if (node->type == SH_NODE_BSDF_PRINCIPLED || node->type == SH_NODE_BSDF_DIFFUSE) { + return node; + } + } + + return nullptr; +} + +/* Creates a USD Preview Surface shader based on the given cycles node name and type. */ +static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &usd_export_context, + pxr::UsdShadeMaterial &material, + const char *name, + const int type) +{ + pxr::SdfPath shader_path = material.GetPath() + .AppendChild(usdtokens::preview) + .AppendChild(pxr::TfToken(pxr::TfMakeValidIdentifier(name))); + pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(usd_export_context.stage, shader_path); + + switch (type) { + case SH_NODE_TEX_IMAGE: { + shader.CreateIdAttr(pxr::VtValue(usdtokens::uv_texture)); + break; + } + case SH_NODE_TEX_COORD: + case SH_NODE_UVMAP: { + shader.CreateIdAttr(pxr::VtValue(usdtokens::primvar_float2)); + break; + } + case SH_NODE_BSDF_DIFFUSE: + case SH_NODE_BSDF_PRINCIPLED: { + shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface)); + material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface); + break; + } + + default: + break; + } + + return shader; +} + +/* Creates a USD Preview Surface shader based on the given cycles shading node. */ +static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &usd_export_context, + pxr::UsdShadeMaterial &material, + bNode *node) +{ + pxr::UsdShadeShader shader = create_usd_preview_shader( + usd_export_context, material, node->name, node->type); + + if (node->type != SH_NODE_TEX_IMAGE) { + return shader; + } + + /* For texture image nodes we set the image path and color space. */ + std::string imagePath = get_tex_image_asset_path( + node, usd_export_context.stage, usd_export_context.export_params); + if (!imagePath.empty()) { + shader.CreateInput(usdtokens::file, pxr::SdfValueTypeNames->Asset) + .Set(pxr::SdfAssetPath(imagePath)); + } + + pxr::TfToken colorSpace = get_node_tex_image_color_space(node); + if (!colorSpace.IsEmpty()) { + shader.CreateInput(usdtokens::sourceColorSpace, pxr::SdfValueTypeNames->Token).Set(colorSpace); + } + + return shader; +} + +static std::string get_tex_image_asset_path(Image *ima) +{ + char filepath[FILE_MAX]; + get_absolute_path(ima, filepath); + + return std::string(filepath); +} + +/* Gets an asset path for the given texture image node. The resulting path + * may be absolute, relative to the USD file, or in a 'textures' directory + * in the same directory as the USD file, depending on the export parameters. + * The filename is typically the image filepath but might also be automatically + * generated based on the image name for in-memory textures when exporting textures. + * This function may return an empty string if the image does not have a filepath + * assigned and no asset path could be determined. */ +static std::string get_tex_image_asset_path(bNode *node, + const pxr::UsdStageRefPtr stage, + const USDExportParams &export_params) +{ + Image *ima = reinterpret_cast<Image *>(node->id); + if (!ima) { + return ""; + } + + std::string path; + + if (strlen(ima->filepath) > 0) { + /* Get absolute path. */ + path = get_tex_image_asset_path(ima); + } + else if (export_params.export_textures) { + /* Image has no filepath, but since we are exporting textures, + * check if this is an in-memory texture for which we can + * generate a file name. */ + path = get_in_memory_texture_filename(ima); + } + + if (path.empty()) { + return path; + } + + if (export_params.export_textures) { + /* The texture is exported to a 'textures' directory next to the + * USD root layer. */ + + char exp_path[FILE_MAX]; + char file_path[FILE_MAX]; + BLI_split_file_part(path.c_str(), file_path, FILE_MAX); + + if (export_params.relative_texture_paths) { + BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path, nullptr); + } + else { + /* Create absolute path in the textures directory. */ + pxr::SdfLayerHandle layer = stage->GetRootLayer(); + std::string stage_path = layer->GetRealPath(); + if (stage_path.empty()) { + return path; + } + + char dir_path[FILE_MAX]; + BLI_split_dir_part(stage_path.c_str(), dir_path, FILE_MAX); + BLI_path_join(exp_path, FILE_MAX, dir_path, "textures", file_path, nullptr); + } + return exp_path; + } + + if (export_params.relative_texture_paths) { + /* Get the path relative to the USD. */ + pxr::SdfLayerHandle layer = stage->GetRootLayer(); + std::string stage_path = layer->GetRealPath(); + if (stage_path.empty()) { + return path; + } + + char rel_path[FILE_MAX]; + strcpy(rel_path, path.c_str()); + + BLI_path_rel(rel_path, stage_path.c_str()); + + /* BLI_path_rel adds '//' as a prefix to the path, if + * generating the relative path was successful. */ + if (rel_path[0] != '/' || rel_path[1] != '/') { + /* No relative path generated. */ + return path; + } + + return rel_path + 2; + } + + return path; +} + +/* If the given image is tiled, copy the image tiles to the given + * destination directory. */ +static void copy_tiled_textures(Image *ima, + const std::string &dest_dir, + const bool allow_overwrite) +{ + char src_path[FILE_MAX]; + get_absolute_path(ima, src_path); + + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern = BKE_image_get_tile_strformat(src_path, &tile_format); + + /* Only <UDIM> tile formats are supported by USD right now. */ + if (tile_format != UDIM_TILE_FORMAT_UDIM) { + std::cout << "WARNING: unsupported tile format for `" << src_path << "`" << std::endl; + MEM_SAFE_FREE(udim_pattern); + return; + } + + /* Copy all tiles. */ + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + char src_tile_path[FILE_MAX]; + BKE_image_set_filepath_from_tile_number( + src_tile_path, udim_pattern, tile_format, tile->tile_number); + + char dest_filename[FILE_MAXFILE]; + BLI_split_file_part(src_tile_path, dest_filename, sizeof(dest_filename)); + + char dest_tile_path[FILE_MAX]; + BLI_path_join(dest_tile_path, FILE_MAX, dest_dir.c_str(), dest_filename, nullptr); + + if (!allow_overwrite && BLI_exists(dest_tile_path)) { + continue; + } + + if (BLI_path_cmp_normalized(src_tile_path, dest_tile_path) == 0) { + /* Source and destination paths are the same, don't copy. */ + continue; + } + + std::cout << "Copying texture tile from " << src_tile_path << " to " << dest_tile_path + << std::endl; + + /* Copy the file. */ + if (BLI_copy(src_tile_path, dest_tile_path) != 0) { + WM_reportf(RPT_WARNING, + "USD export: couldn't copy texture tile from %s to %s", + src_tile_path, + dest_tile_path); + } + } + MEM_SAFE_FREE(udim_pattern); +} + +/* Copy the given image to the destination directory. */ +static void copy_single_file(Image *ima, const std::string &dest_dir, const bool allow_overwrite) +{ + char source_path[FILE_MAX]; + get_absolute_path(ima, source_path); + + char file_name[FILE_MAX]; + BLI_split_file_part(source_path, file_name, FILE_MAX); + + char dest_path[FILE_MAX]; + BLI_path_join(dest_path, FILE_MAX, dest_dir.c_str(), file_name, nullptr); + + if (!allow_overwrite && BLI_exists(dest_path)) { + return; + } + + if (BLI_path_cmp_normalized(source_path, dest_path) == 0) { + /* Source and destination paths are the same, don't copy. */ + return; + } + + std::cout << "Copying texture from " << source_path << " to " << dest_path << std::endl; + + /* Copy the file. */ + if (BLI_copy(source_path, dest_path) != 0) { + WM_reportf( + RPT_WARNING, "USD export: couldn't copy texture from %s to %s", source_path, dest_path); + } +} + +/* Export the given texture node's image to a 'textures' directory + * next to given stage's root layer USD. + * Based on ImagesExporter::export_UV_Image() */ +static void export_texture(bNode *node, + const pxr::UsdStageRefPtr stage, + const bool allow_overwrite) +{ + if (node->type != SH_NODE_TEX_IMAGE && node->type != SH_NODE_TEX_ENVIRONMENT) { + return; + } + + Image *ima = reinterpret_cast<Image *>(node->id); + if (!ima) { + return; + } + + pxr::SdfLayerHandle layer = stage->GetRootLayer(); + std::string stage_path = layer->GetRealPath(); + if (stage_path.empty()) { + return; + } + + char usd_dir_path[FILE_MAX]; + BLI_split_dir_part(stage_path.c_str(), usd_dir_path, FILE_MAX); + + char tex_dir_path[FILE_MAX]; + BLI_path_join(tex_dir_path, FILE_MAX, usd_dir_path, "textures", SEP_STR, nullptr); + + BLI_dir_create_recursive(tex_dir_path); + + const bool is_dirty = BKE_image_is_dirty(ima); + const bool is_generated = ima->source == IMA_SRC_GENERATED; + const bool is_packed = BKE_image_has_packedfile(ima); + + std::string dest_dir(tex_dir_path); + + if (is_generated || is_dirty || is_packed) { + export_in_memory_texture(ima, dest_dir, allow_overwrite); + } + else if (ima->source == IMA_SRC_TILED) { + copy_tiled_textures(ima, dest_dir, allow_overwrite); + } + else { + copy_single_file(ima, dest_dir, allow_overwrite); + } +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_material.h b/source/blender/io/usd/intern/usd_writer_material.h new file mode 100644 index 00000000000..435ac41c4bf --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_material.h @@ -0,0 +1,57 @@ +/* + * 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 <pxr/pxr.h> +#include <pxr/usd/usd/prim.h> +#include <pxr/usd/usd/stage.h> +#include <pxr/usd/usdShade/material.h> + +#include <string> + +struct bNode; +struct bNodeTree; +struct Material; +struct USDExportParams; + +namespace blender::io::usd { + +struct USDExporterContext; + +/** + * Entry point to create an approximate USD Preview Surface network from a Cycles node graph. + * Due to the limited nodes in the USD Preview Surface specification, only the following nodes + * are supported: + * - UVMap + * - Texture Coordinate + * - Image Texture + * - Principled BSDF + * More may be added in the future. + * + * \param default_uv: used as the default UV set name sampled by the `primvar` + * reader shaders generated for image texture nodes that don't have an attached UVMap node. + */ +void create_usd_preview_surface_material(const USDExporterContext &usd_export_context, + Material *material, + pxr::UsdShadeMaterial &usd_material, + const std::string &default_uv = ""); + +/* Entry point to create USD Shade Material network from Blender viewport display settings. */ +void create_usd_viewport_material(const USDExporterContext &usd_export_context, + Material *material, + pxr::UsdShadeMaterial &usd_material); + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index b061a2ff795..1d3a5f5280d 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -361,7 +361,7 @@ void USDGenericMeshWriter::assign_materials(const HierarchyContext &context, continue; } - pxr::UsdShadeMaterial usd_material = ensure_usd_material(material); + pxr::UsdShadeMaterial usd_material = ensure_usd_material(context, material); material_binding_api.Bind(usd_material); /* USD seems to support neither per-material nor per-face-group double-sidedness, so we just @@ -395,7 +395,7 @@ void USDGenericMeshWriter::assign_materials(const HierarchyContext &context, continue; } - pxr::UsdShadeMaterial usd_material = ensure_usd_material(material); + pxr::UsdShadeMaterial usd_material = ensure_usd_material(context, material); pxr::TfToken material_name = usd_material.GetPath().GetNameToken(); pxr::UsdGeomSubset usd_face_subset = material_binding_api.CreateMaterialBindSubset( diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 16bd826ecdd..505f917ede5 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -41,6 +41,10 @@ struct USDExportParams { bool visible_objects_only; bool use_instancing; enum eEvaluationMode evaluation_mode; + bool generate_preview_surface; + bool export_textures; + bool overwrite_textures; + bool relative_texture_paths; }; struct USDImportParams { diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt index 296dd70b5a2..0b1be7946cf 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -56,6 +56,12 @@ set(LIB bf_blenkernel ) +if(WITH_TBB) + add_definitions(-DWITH_TBB) + list(APPEND INC_SYS ${TBB_INCLUDE_DIRS}) + list(APPEND LIB ${TBB_LIBRARIES}) +endif() + blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index d31353c4a76..87f87e37a7e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -24,6 +24,7 @@ #include "BKE_blender_version.h" #include "BLI_path_util.h" +#include "BLI_task.hh" #include "obj_export_mesh.hh" #include "obj_export_mtl.hh" @@ -52,68 +53,75 @@ const char *DEFORM_GROUP_DISABLED = "off"; * So an empty material name is written. */ const char *MATERIAL_GROUP_DISABLED = ""; -void OBJWriter::write_vert_uv_normal_indices(Span<int> vert_indices, +void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> uv_indices, Span<int> normal_indices) const { BLI_assert(vert_indices.size() == uv_indices.size() && vert_indices.size() == normal_indices.size()); - file_handler_->write<eOBJSyntaxElement::poly_element_begin>(); + fh.write<eOBJSyntaxElement::poly_element_begin>(); for (int j = 0; j < vert_indices.size(); j++) { - file_handler_->write<eOBJSyntaxElement::vertex_uv_normal_indices>( - vert_indices[j] + index_offsets_.vertex_offset + 1, - uv_indices[j] + index_offsets_.uv_vertex_offset + 1, - normal_indices[j] + index_offsets_.normal_offset + 1); + fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>( + vert_indices[j] + offsets.vertex_offset + 1, + uv_indices[j] + offsets.uv_vertex_offset + 1, + normal_indices[j] + offsets.normal_offset + 1); } - file_handler_->write<eOBJSyntaxElement::poly_element_end>(); + fh.write<eOBJSyntaxElement::poly_element_end>(); } -void OBJWriter::write_vert_normal_indices(Span<int> vert_indices, +void OBJWriter::write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> /*uv_indices*/, Span<int> normal_indices) const { BLI_assert(vert_indices.size() == normal_indices.size()); - file_handler_->write<eOBJSyntaxElement::poly_element_begin>(); + fh.write<eOBJSyntaxElement::poly_element_begin>(); for (int j = 0; j < vert_indices.size(); j++) { - file_handler_->write<eOBJSyntaxElement::vertex_normal_indices>( - vert_indices[j] + index_offsets_.vertex_offset + 1, - normal_indices[j] + index_offsets_.normal_offset + 1); + fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + offsets.vertex_offset + 1, + normal_indices[j] + offsets.normal_offset + + 1); } - file_handler_->write<eOBJSyntaxElement::poly_element_end>(); + fh.write<eOBJSyntaxElement::poly_element_end>(); } -void OBJWriter::write_vert_uv_indices(Span<int> vert_indices, +void OBJWriter::write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> uv_indices, Span<int> /*normal_indices*/) const { BLI_assert(vert_indices.size() == uv_indices.size()); - file_handler_->write<eOBJSyntaxElement::poly_element_begin>(); + fh.write<eOBJSyntaxElement::poly_element_begin>(); for (int j = 0; j < vert_indices.size(); j++) { - file_handler_->write<eOBJSyntaxElement::vertex_uv_indices>( - vert_indices[j] + index_offsets_.vertex_offset + 1, - uv_indices[j] + index_offsets_.uv_vertex_offset + 1); + fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + offsets.vertex_offset + 1, + uv_indices[j] + offsets.uv_vertex_offset + 1); } - file_handler_->write<eOBJSyntaxElement::poly_element_end>(); + fh.write<eOBJSyntaxElement::poly_element_end>(); } -void OBJWriter::write_vert_indices(Span<int> vert_indices, +void OBJWriter::write_vert_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> /*uv_indices*/, Span<int> /*normal_indices*/) const { - file_handler_->write<eOBJSyntaxElement::poly_element_begin>(); + fh.write<eOBJSyntaxElement::poly_element_begin>(); for (const int vert_index : vert_indices) { - file_handler_->write<eOBJSyntaxElement::vertex_indices>(vert_index + - index_offsets_.vertex_offset + 1); + fh.write<eOBJSyntaxElement::vertex_indices>(vert_index + offsets.vertex_offset + 1); } - file_handler_->write<eOBJSyntaxElement::poly_element_end>(); + fh.write<eOBJSyntaxElement::poly_element_end>(); } void OBJWriter::write_header() const { using namespace std::string_literals; - file_handler_->write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() + - "\n"); - file_handler_->write<eOBJSyntaxElement::string>("# www.blender.org\n"); + FormatHandler<eFileType::OBJ> fh; + fh.write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() + "\n"); + fh.write<eOBJSyntaxElement::string>("# www.blender.org\n"); + fh.write_to_file(outfile_); } void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const @@ -122,10 +130,13 @@ void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const char mtl_file_name[FILE_MAXFILE]; char mtl_dir_name[FILE_MAXDIR]; BLI_split_dirfile(mtl_filepath.data(), mtl_dir_name, mtl_file_name, FILE_MAXDIR, FILE_MAXFILE); - file_handler_->write<eOBJSyntaxElement::mtllib>(mtl_file_name); + FormatHandler<eFileType::OBJ> fh; + fh.write<eOBJSyntaxElement::mtllib>(mtl_file_name); + fh.write_to_file(outfile_); } -void OBJWriter::write_object_group(const OBJMesh &obj_mesh_data) const +void OBJWriter::write_object_group(FormatHandler<eFileType::OBJ> &fh, + const OBJMesh &obj_mesh_data) const { /* "o object_name" is not mandatory. A valid .OBJ file may contain neither * "o name" nor "g group_name". */ @@ -138,125 +149,104 @@ void OBJWriter::write_object_group(const OBJMesh &obj_mesh_data) const const char *object_material_name = obj_mesh_data.get_object_material_name(0); if (export_params_.export_materials && export_params_.export_material_groups && object_material_name) { - file_handler_->write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name + - "_" + object_material_name); - return; + fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name + "_" + + object_material_name); + } + else { + fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name); } - file_handler_->write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name); } -void OBJWriter::write_object_name(const OBJMesh &obj_mesh_data) const +void OBJWriter::write_object_name(FormatHandler<eFileType::OBJ> &fh, + const OBJMesh &obj_mesh_data) const { const char *object_name = obj_mesh_data.get_object_name(); if (export_params_.export_object_groups) { - write_object_group(obj_mesh_data); + write_object_group(fh, obj_mesh_data); return; } - file_handler_->write<eOBJSyntaxElement::object_name>(object_name); + fh.write<eOBJSyntaxElement::object_name>(object_name); } -void OBJWriter::write_vertex_coords(const OBJMesh &obj_mesh_data) const +/* Split up large meshes into multi-threaded jobs; each job processes + * this amount of items. */ +static const int chunk_size = 32768; +static int calc_chunk_count(int count) { - const int tot_vertices = obj_mesh_data.tot_vertices(); - for (int i = 0; i < tot_vertices; i++) { - float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); - file_handler_->write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]); - } + return (count + chunk_size - 1) / chunk_size; } -void OBJWriter::write_uv_coords(OBJMesh &r_obj_mesh_data) const +/* Write /tot_count/ items to OBJ file output. Each item is written + * by a /function/ that should be independent from other items. + * If the amount of items is large enough (> chunk_size), then writing + * will be done in parallel, into temporary FormatHandler buffers that + * will be written into the final /fh/ buffer at the end. + */ +template<typename Function> +void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh, + int tot_count, + const Function &function) { - Vector<std::array<float, 2>> uv_coords; - /* UV indices are calculated and stored in an OBJMesh member here. */ - r_obj_mesh_data.store_uv_coords_and_indices(uv_coords); - - for (const std::array<float, 2> &uv_vertex : uv_coords) { - file_handler_->write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]); + if (tot_count <= 0) { + return; } -} - -void OBJWriter::write_poly_normals(OBJMesh &obj_mesh_data) -{ - obj_mesh_data.ensure_mesh_normals(); - Vector<float3> normals; - obj_mesh_data.store_normal_coords_and_indices(normals); - for (const float3 &normal : normals) { - file_handler_->write<eOBJSyntaxElement::normal>(normal[0], normal[1], normal[2]); + /* If we have just one chunk, process it directly into the output + * buffer - avoids all the job scheduling and temporary vector allocation + * overhead. */ + const int chunk_count = calc_chunk_count(tot_count); + if (chunk_count == 1) { + for (int i = 0; i < tot_count; i++) { + function(fh, i); + } + return; + } + /* Give each chunk its own temporary output buffer, and process them in parallel. */ + std::vector<FormatHandler<eFileType::OBJ>> buffers(chunk_count); + blender::threading::parallel_for(IndexRange(chunk_count), 1, [&](IndexRange range) { + for (const int r : range) { + int i_start = r * chunk_size; + int i_end = std::min(i_start + chunk_size, tot_count); + auto &buf = buffers[r]; + for (int i = i_start; i < i_end; i++) { + function(buf, i); + } + } + }); + /* Emit all temporary output buffers into the destination buffer. */ + for (auto &buf : buffers) { + fh.append_from(buf); } } -int OBJWriter::write_smooth_group(const OBJMesh &obj_mesh_data, - const int poly_index, - const int last_poly_smooth_group) const +void OBJWriter::write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, + const OBJMesh &obj_mesh_data) const { - int current_group = SMOOTH_GROUP_DISABLED; - if (!export_params_.export_smooth_groups && obj_mesh_data.is_ith_poly_smooth(poly_index)) { - /* Smooth group calculation is disabled, but polygon is smooth-shaded. */ - current_group = SMOOTH_GROUP_DEFAULT; - } - else if (obj_mesh_data.is_ith_poly_smooth(poly_index)) { - /* Smooth group calc is enabled and polygon is smooth–shaded, so find the group. */ - current_group = obj_mesh_data.ith_smooth_group(poly_index); - } - - if (current_group == last_poly_smooth_group) { - /* Group has already been written, even if it is "s 0". */ - return current_group; - } - file_handler_->write<eOBJSyntaxElement::smooth_group>(current_group); - return current_group; + const int tot_count = obj_mesh_data.tot_vertices(); + obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { + float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); + buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]); + }); } -int16_t OBJWriter::write_poly_material(const OBJMesh &obj_mesh_data, - const int poly_index, - const int16_t last_poly_mat_nr, - std::function<const char *(int)> matname_fn) const +void OBJWriter::write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &r_obj_mesh_data) const { - if (!export_params_.export_materials || obj_mesh_data.tot_materials() <= 0) { - return last_poly_mat_nr; - } - const int16_t current_mat_nr = obj_mesh_data.ith_poly_matnr(poly_index); - /* Whenever a polygon with a new material is encountered, write its material - * and/or group, otherwise pass. */ - if (last_poly_mat_nr == current_mat_nr) { - return current_mat_nr; - } - if (current_mat_nr == NOT_FOUND) { - file_handler_->write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED); - return current_mat_nr; - } - if (export_params_.export_object_groups) { - write_object_group(obj_mesh_data); - } - const char *mat_name = matname_fn(current_mat_nr); - if (!mat_name) { - mat_name = MATERIAL_GROUP_DISABLED; - } - file_handler_->write<eOBJSyntaxElement::poly_usemtl>(mat_name); - - return current_mat_nr; + const Vector<float2> &uv_coords = r_obj_mesh_data.get_uv_coords(); + const int tot_count = uv_coords.size(); + obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { + const float2 &uv_vertex = uv_coords[i]; + buf.write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]); + }); } -int16_t OBJWriter::write_vertex_group(const OBJMesh &obj_mesh_data, - const int poly_index, - const int16_t last_poly_vertex_group) const +void OBJWriter::write_poly_normals(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data) { - if (!export_params_.export_vertex_groups) { - return last_poly_vertex_group; - } - const int16_t current_group = obj_mesh_data.get_poly_deform_group_index(poly_index); - - if (current_group == last_poly_vertex_group) { - /* No vertex group found in this polygon, just like in the last iteration. */ - return current_group; - } - if (current_group == NOT_FOUND) { - file_handler_->write<eOBJSyntaxElement::object_group>(DEFORM_GROUP_DISABLED); - return current_group; - } - file_handler_->write<eOBJSyntaxElement::object_group>( - obj_mesh_data.get_poly_deform_group_name(current_group)); - return current_group; + /* Poly normals should be calculated earlier via store_normal_coords_and_indices. */ + const Vector<float3> &normal_coords = obj_mesh_data.get_normal_coords(); + const int tot_count = normal_coords.size(); + obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { + const float3 &normal = normal_coords[i]; + buf.write<eOBJSyntaxElement::normal>(normal[0], normal[1], normal[2]); + }); } OBJWriter::func_vert_uv_normal_indices OBJWriter::get_poly_element_writer( @@ -278,32 +268,85 @@ OBJWriter::func_vert_uv_normal_indices OBJWriter::get_poly_element_writer( return &OBJWriter::write_vert_indices; } -void OBJWriter::write_poly_elements(const OBJMesh &obj_mesh_data, - std::function<const char *(int)> matname_fn) +static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams ¶ms, int poly_idx) { - int last_poly_smooth_group = NEGATIVE_INIT; - int16_t last_poly_vertex_group = NEGATIVE_INIT; - int16_t last_poly_mat_nr = NEGATIVE_INIT; + if (poly_idx < 0) { + return NEGATIVE_INIT; + } + int group = SMOOTH_GROUP_DISABLED; + if (mesh.is_ith_poly_smooth(poly_idx)) { + group = !params.export_smooth_groups ? SMOOTH_GROUP_DEFAULT : mesh.ith_smooth_group(poly_idx); + } + return group; +} +void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + const OBJMesh &obj_mesh_data, + std::function<const char *(int)> matname_fn) +{ const func_vert_uv_normal_indices poly_element_writer = get_poly_element_writer( obj_mesh_data.tot_uv_vertices()); const int tot_polygons = obj_mesh_data.tot_polygons(); - for (int i = 0; i < tot_polygons; i++) { + obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int i) { Vector<int> poly_vertex_indices = obj_mesh_data.calc_poly_vertex_indices(i); Span<int> poly_uv_indices = obj_mesh_data.calc_poly_uv_indices(i); Vector<int> poly_normal_indices = obj_mesh_data.calc_poly_normal_indices(i); - last_poly_smooth_group = write_smooth_group(obj_mesh_data, i, last_poly_smooth_group); - last_poly_vertex_group = write_vertex_group(obj_mesh_data, i, last_poly_vertex_group); - last_poly_mat_nr = write_poly_material(obj_mesh_data, i, last_poly_mat_nr, matname_fn); - (this->*poly_element_writer)(poly_vertex_indices, poly_uv_indices, poly_normal_indices); - } + /* Write smoothing group if different from previous. */ + { + const int prev_group = get_smooth_group(obj_mesh_data, export_params_, i - 1); + const int group = get_smooth_group(obj_mesh_data, export_params_, i); + if (group != prev_group) { + buf.write<eOBJSyntaxElement::smooth_group>(group); + } + } + + /* Write vertex group if different from previous. */ + if (export_params_.export_vertex_groups) { + const int16_t prev_group = i == 0 ? NEGATIVE_INIT : + obj_mesh_data.get_poly_deform_group_index(i - 1); + const int16_t group = obj_mesh_data.get_poly_deform_group_index(i); + if (group != prev_group) { + buf.write<eOBJSyntaxElement::object_group>( + group == NOT_FOUND ? DEFORM_GROUP_DISABLED : + obj_mesh_data.get_poly_deform_group_name(group)); + } + } + + /* Write material name and material group if different from previous. */ + if (export_params_.export_materials && obj_mesh_data.tot_materials() > 0) { + const int16_t prev_mat = i == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(i - 1); + const int16_t mat = obj_mesh_data.ith_poly_matnr(i); + if (mat != prev_mat) { + if (mat == NOT_FOUND) { + buf.write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED); + } + else { + if (export_params_.export_object_groups) { + write_object_group(buf, obj_mesh_data); + } + const char *mat_name = matname_fn(mat); + if (!mat_name) { + mat_name = MATERIAL_GROUP_DISABLED; + } + buf.write<eOBJSyntaxElement::poly_usemtl>(mat_name); + } + } + } + + /* Write polygon elements. */ + (this->*poly_element_writer)( + buf, offsets, poly_vertex_indices, poly_uv_indices, poly_normal_indices); + }); } -void OBJWriter::write_edges_indices(const OBJMesh &obj_mesh_data) const +void OBJWriter::write_edges_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + const OBJMesh &obj_mesh_data) const { - obj_mesh_data.ensure_mesh_edges(); + /* Note: ensure_mesh_edges should be called before. */ const int tot_edges = obj_mesh_data.tot_edges(); for (int edge_index = 0; edge_index < tot_edges; edge_index++) { const std::optional<std::array<int, 2>> vertex_indices = @@ -311,13 +354,13 @@ void OBJWriter::write_edges_indices(const OBJMesh &obj_mesh_data) const if (!vertex_indices) { continue; } - file_handler_->write<eOBJSyntaxElement::edge>( - (*vertex_indices)[0] + index_offsets_.vertex_offset + 1, - (*vertex_indices)[1] + index_offsets_.vertex_offset + 1); + fh.write<eOBJSyntaxElement::edge>((*vertex_indices)[0] + offsets.vertex_offset + 1, + (*vertex_indices)[1] + offsets.vertex_offset + 1); } } -void OBJWriter::write_nurbs_curve(const OBJCurve &obj_nurbs_data) const +void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh, + const OBJCurve &obj_nurbs_data) const { const int total_splines = obj_nurbs_data.total_splines(); for (int spline_idx = 0; spline_idx < total_splines; spline_idx++) { @@ -325,15 +368,15 @@ void OBJWriter::write_nurbs_curve(const OBJCurve &obj_nurbs_data) const for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) { const float3 vertex_coords = obj_nurbs_data.vertex_coordinates( spline_idx, vertex_idx, export_params_.scaling_factor); - file_handler_->write<eOBJSyntaxElement::vertex_coords>( + fh.write<eOBJSyntaxElement::vertex_coords>( vertex_coords[0], vertex_coords[1], vertex_coords[2]); } const char *nurbs_name = obj_nurbs_data.get_curve_name(); const int nurbs_degree = obj_nurbs_data.get_nurbs_degree(spline_idx); - file_handler_->write<eOBJSyntaxElement::object_group>(nurbs_name); - file_handler_->write<eOBJSyntaxElement::cstype>(); - file_handler_->write<eOBJSyntaxElement::nurbs_degree>(nurbs_degree); + fh.write<eOBJSyntaxElement::object_group>(nurbs_name); + fh.write<eOBJSyntaxElement::cstype>(); + fh.write<eOBJSyntaxElement::nurbs_degree>(nurbs_degree); /** * The numbers written here are indices into the vertex coordinates written * earlier, relative to the line that is going to be written. @@ -342,36 +385,42 @@ void OBJWriter::write_nurbs_curve(const OBJCurve &obj_nurbs_data) const * 0.0 1.0 -1 -2 -3 -4 -1 -2 -3 for a cyclic curve with 4 vertices. */ const int total_control_points = obj_nurbs_data.total_spline_control_points(spline_idx); - file_handler_->write<eOBJSyntaxElement::curve_element_begin>(); + fh.write<eOBJSyntaxElement::curve_element_begin>(); for (int i = 0; i < total_control_points; i++) { /* "+1" to keep indices one-based, even if they're negative: i.e., -1 refers to the * last vertex coordinate, -2 second last. */ - file_handler_->write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1)); + fh.write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1)); } - file_handler_->write<eOBJSyntaxElement::curve_element_end>(); + fh.write<eOBJSyntaxElement::curve_element_end>(); /** * In `parm u 0 0.1 ..` line:, (total control points + 2) equidistant numbers in the - * parameter range are inserted. + * parameter range are inserted. However for curves with endpoint flag, + * first degree+1 numbers are zeroes, and last degree+1 numbers are ones */ - file_handler_->write<eOBJSyntaxElement::nurbs_parameter_begin>(); + + const short flagsu = obj_nurbs_data.get_nurbs_flagu(spline_idx); + const bool cyclic = flagsu & CU_NURB_CYCLIC; + const bool endpoint = !cyclic && (flagsu & CU_NURB_ENDPOINT); + fh.write<eOBJSyntaxElement::nurbs_parameter_begin>(); for (int i = 1; i <= total_control_points + 2; i++) { - file_handler_->write<eOBJSyntaxElement::nurbs_parameters>(1.0f * i / - (total_control_points + 2 + 1)); + float parm = 1.0f * i / (total_control_points + 2 + 1); + if (endpoint) { + if (i <= nurbs_degree) { + parm = 0; + } + else if (i > total_control_points + 2 - nurbs_degree) { + parm = 1; + } + } + fh.write<eOBJSyntaxElement::nurbs_parameters>(parm); } - file_handler_->write<eOBJSyntaxElement::nurbs_parameter_end>(); + fh.write<eOBJSyntaxElement::nurbs_parameter_end>(); - file_handler_->write<eOBJSyntaxElement::nurbs_group_end>(); + fh.write<eOBJSyntaxElement::nurbs_group_end>(); } } -void OBJWriter::update_index_offsets(const OBJMesh &obj_mesh_data) -{ - index_offsets_.vertex_offset += obj_mesh_data.tot_vertices(); - index_offsets_.uv_vertex_offset += obj_mesh_data.tot_uv_vertices(); - index_offsets_.normal_offset += obj_mesh_data.tot_normal_indices(); -} - /* -------------------------------------------------------------------- */ /** \name .MTL writers. * \{ */ @@ -394,18 +443,31 @@ MTLWriter::MTLWriter(const char *obj_filepath) noexcept(false) if (!ok) { throw std::system_error(ENAMETOOLONG, std::system_category(), ""); } - file_handler_ = std::make_unique<FormattedFileHandler<eFileType::MTL>>(mtl_filepath_); + outfile_ = BLI_fopen(mtl_filepath_.c_str(), "wb"); + if (!outfile_) { + throw std::system_error(errno, std::system_category(), "Cannot open file " + mtl_filepath_); + } +} +MTLWriter::~MTLWriter() +{ + if (outfile_) { + fmt_handler_.write_to_file(outfile_); + if (std::fclose(outfile_)) { + std::cerr << "Error: could not close the file '" << mtl_filepath_ + << "' properly, it may be corrupted." << std::endl; + } + } } -void MTLWriter::write_header(const char *blen_filepath) const +void MTLWriter::write_header(const char *blen_filepath) { using namespace std::string_literals; const char *blen_basename = (blen_filepath && blen_filepath[0] != '\0') ? BLI_path_basename(blen_filepath) : "None"; - file_handler_->write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() + - " MTL File: '" + blen_basename + "'\n"); - file_handler_->write<eMTLSyntaxElement::string>("# www.blender.org\n"); + fmt_handler_.write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() + + " MTL File: '" + blen_basename + "'\n"); + fmt_handler_.write<eMTLSyntaxElement::string>("# www.blender.org\n"); } StringRefNull MTLWriter::mtl_file_path() const @@ -415,18 +477,18 @@ StringRefNull MTLWriter::mtl_file_path() const void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material) { - file_handler_->write<eMTLSyntaxElement::Ns>(mtl_material.Ns); - file_handler_->write<eMTLSyntaxElement::Ka>( + fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl_material.Ns); + fmt_handler_.write<eMTLSyntaxElement::Ka>( mtl_material.Ka.x, mtl_material.Ka.y, mtl_material.Ka.z); - file_handler_->write<eMTLSyntaxElement::Kd>( + fmt_handler_.write<eMTLSyntaxElement::Kd>( mtl_material.Kd.x, mtl_material.Kd.y, mtl_material.Kd.z); - file_handler_->write<eMTLSyntaxElement::Ks>( + fmt_handler_.write<eMTLSyntaxElement::Ks>( mtl_material.Ks.x, mtl_material.Ks.y, mtl_material.Ks.z); - file_handler_->write<eMTLSyntaxElement::Ke>( + fmt_handler_.write<eMTLSyntaxElement::Ke>( mtl_material.Ke.x, mtl_material.Ke.y, mtl_material.Ke.z); - file_handler_->write<eMTLSyntaxElement::Ni>(mtl_material.Ni); - file_handler_->write<eMTLSyntaxElement::d>(mtl_material.d); - file_handler_->write<eMTLSyntaxElement::illum>(mtl_material.illum); + fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl_material.Ni); + fmt_handler_.write<eMTLSyntaxElement::d>(mtl_material.d); + fmt_handler_.write<eMTLSyntaxElement::illum>(mtl_material.illum); } void MTLWriter::write_texture_map( @@ -449,8 +511,8 @@ void MTLWriter::write_texture_map( #define SYNTAX_DISPATCH(eMTLSyntaxElement) \ if (texture_map.key == eMTLSyntaxElement) { \ - file_handler_->write<eMTLSyntaxElement>(translation + scale + map_bump_strength, \ - texture_map.value.image_path); \ + fmt_handler_.write<eMTLSyntaxElement>(translation + scale + map_bump_strength, \ + texture_map.value.image_path); \ return; \ } @@ -474,8 +536,8 @@ void MTLWriter::write_materials() mtlmaterials_.end(), [](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; }); for (const MTLMaterial &mtlmat : mtlmaterials_) { - file_handler_->write<eMTLSyntaxElement::string>("\n"); - file_handler_->write<eMTLSyntaxElement::newmtl>(mtlmat.name); + fmt_handler_.write<eMTLSyntaxElement::string>("\n"); + fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name); write_bsdf_properties(mtlmat); for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map : mtlmat.texture_maps.items()) { diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 7385d9fabe2..c88955e5090 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -49,14 +49,29 @@ struct IndexOffsets { class OBJWriter : NonMovable, NonCopyable { private: const OBJExportParams &export_params_; - std::unique_ptr<FormattedFileHandler<eFileType::OBJ>> file_handler_ = nullptr; - IndexOffsets index_offsets_{0, 0, 0}; + std::string outfile_path_; + FILE *outfile_; public: OBJWriter(const char *filepath, const OBJExportParams &export_params) noexcept(false) - : export_params_(export_params) + : export_params_(export_params), outfile_path_(filepath), outfile_(nullptr) { - file_handler_ = std::make_unique<FormattedFileHandler<eFileType::OBJ>>(filepath); + outfile_ = BLI_fopen(filepath, "wb"); + if (!outfile_) { + throw std::system_error(errno, std::system_category(), "Cannot open file " + outfile_path_); + } + } + ~OBJWriter() + { + if (outfile_ && std::fclose(outfile_)) { + std::cerr << "Error: could not close the file '" << outfile_path_ + << "' properly, it may be corrupted." << std::endl; + } + } + + FILE *get_outfile() const + { + return outfile_; } void write_header() const; @@ -64,11 +79,11 @@ class OBJWriter : NonMovable, NonCopyable { /** * Write object's name or group. */ - void write_object_name(const OBJMesh &obj_mesh_data) const; + void write_object_name(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const; /** * Write an object's group with mesh and/or material name appended conditionally. */ - void write_object_group(const OBJMesh &obj_mesh_data) const; + void write_object_group(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const; /** * Write file name of Material Library in .OBJ file. */ @@ -76,38 +91,17 @@ class OBJWriter : NonMovable, NonCopyable { /** * Write vertex coordinates for all vertices as "v x y z". */ - void write_vertex_coords(const OBJMesh &obj_mesh_data) const; + void write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const; /** * Write UV vertex coordinates for all vertices as `vt u v`. * \note UV indices are stored here, but written with polygons later. */ - void write_uv_coords(OBJMesh &obj_mesh_data) const; + void write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data) const; /** * Write loop normals for smooth-shaded polygons, and polygon normals otherwise, as "vn x y z". * \note Normal indices ares stored here, but written with polygons later. */ - void write_poly_normals(OBJMesh &obj_mesh_data); - /** - * Write smooth group if polygon at the given index is shaded smooth else "s 0" - */ - int write_smooth_group(const OBJMesh &obj_mesh_data, - int poly_index, - int last_poly_smooth_group) const; - /** - * Write material name and material group of a polygon in the .OBJ file. - * \return #mat_nr of the polygon at the given index. - * \note It doesn't write to the material library. - */ - int16_t write_poly_material(const OBJMesh &obj_mesh_data, - int poly_index, - int16_t last_poly_mat_nr, - std::function<const char *(int)> matname_fn) const; - /** - * Write the name of the deform group of a polygon. - */ - int16_t write_vertex_group(const OBJMesh &obj_mesh_data, - int poly_index, - int16_t last_poly_vertex_group) const; + void write_poly_normals(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data); /** * Write polygon elements with at least vertex indices, and conditionally with UV vertex * indices and polygon normal indices. Also write groups: smooth, vertex, material. @@ -115,25 +109,25 @@ class OBJWriter : NonMovable, NonCopyable { * name used in the .obj file. * \note UV indices were stored while writing UV vertices. */ - void write_poly_elements(const OBJMesh &obj_mesh_data, + void write_poly_elements(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + const OBJMesh &obj_mesh_data, std::function<const char *(int)> matname_fn); /** * Write loose edges of a mesh as "l v1 v2". */ - void write_edges_indices(const OBJMesh &obj_mesh_data) const; + void write_edges_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + const OBJMesh &obj_mesh_data) const; /** * Write a NURBS curve to the .OBJ file in parameter form. */ - void write_nurbs_curve(const OBJCurve &obj_nurbs_data) const; - - /** - * When there are multiple objects in a frame, the indices of previous objects' coordinates or - * normals add up. - */ - void update_index_offsets(const OBJMesh &obj_mesh_data); + void write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh, const OBJCurve &obj_nurbs_data) const; private: - using func_vert_uv_normal_indices = void (OBJWriter::*)(Span<int> vert_indices, + using func_vert_uv_normal_indices = void (OBJWriter::*)(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> uv_indices, Span<int> normal_indices) const; /** @@ -144,25 +138,33 @@ class OBJWriter : NonMovable, NonCopyable { /** * Write one line of polygon indices as "f v1/vt1/vn1 v2/vt2/vn2 ...". */ - void write_vert_uv_normal_indices(Span<int> vert_indices, + void write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> uv_indices, Span<int> normal_indices) const; /** * Write one line of polygon indices as "f v1//vn1 v2//vn2 ...". */ - void write_vert_normal_indices(Span<int> vert_indices, + void write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> /*uv_indices*/, Span<int> normal_indices) const; /** * Write one line of polygon indices as "f v1/vt1 v2/vt2 ...". */ - void write_vert_uv_indices(Span<int> vert_indices, + void write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> uv_indices, Span<int> /*normal_indices*/) const; /** * Write one line of polygon indices as "f v1 v2 ...". */ - void write_vert_indices(Span<int> vert_indices, + void write_vert_indices(FormatHandler<eFileType::OBJ> &fh, + const IndexOffsets &offsets, + Span<int> vert_indices, Span<int> /*uv_indices*/, Span<int> /*normal_indices*/) const; }; @@ -172,7 +174,8 @@ class OBJWriter : NonMovable, NonCopyable { */ class MTLWriter : NonMovable, NonCopyable { private: - std::unique_ptr<FormattedFileHandler<eFileType::MTL>> file_handler_ = nullptr; + FormatHandler<eFileType::MTL> fmt_handler_; + FILE *outfile_; std::string mtl_filepath_; Vector<MTLMaterial> mtlmaterials_; /* Map from a Material* to an index into mtlmaterials_. */ @@ -183,8 +186,9 @@ class MTLWriter : NonMovable, NonCopyable { * Create the .MTL file. */ MTLWriter(const char *obj_filepath) noexcept(false); + ~MTLWriter(); - void write_header(const char *blen_filepath) const; + void write_header(const char *blen_filepath); /** * Write all of the material specifications to the MTL file. * For consistency of output from run to run (useful for testing), diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh index 1bbefaee75f..7d8427a8980 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -24,6 +24,7 @@ #include <string> #include <system_error> #include <type_traits> +#include <vector> #include "BLI_compiler_attrs.h" #include "BLI_fileops.h" @@ -124,6 +125,14 @@ constexpr bool is_type_integral = (... && std::is_integral_v<std::decay_t<T>>); template<typename... T> constexpr bool is_type_string_related = (... && std::is_constructible_v<std::string, T>); +/* GCC (at least 9.3) while compiling the obj_exporter_tests.cc with optimizations on, + * results in "obj_export_io.hh:205:18: warning: ‘%s’ directive output truncated writing 34 bytes + * into a region of size 6" and similar warnings. Yes the output is truncated, and that is covered + * as an edge case by tests on purpose. */ +#if defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-truncation" +#endif template<typename... T> constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key) { @@ -168,7 +177,7 @@ constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key return {"curv 0.0 1.0", 0, is_type_string_related<T...>}; } case eOBJSyntaxElement::nurbs_parameter_begin: { - return {"parm 0.0", 0, is_type_string_related<T...>}; + return {"parm u 0.0", 0, is_type_string_related<T...>}; } case eOBJSyntaxElement::nurbs_parameters: { return {" %f", 1, is_type_float<T...>}; @@ -264,31 +273,52 @@ constexpr FormattingSyntax syntax_elem_to_formatting(const eMTLSyntaxElement key } } } +#if defined __GNUC__ +# pragma GCC diagnostic pop +#endif /** - * File format and syntax agnostic file writer. + * File format and syntax agnostic file buffer writer. + * All writes are done into an internal chunked memory buffer + * (list of default 64 kilobyte blocks). + * Call write_fo_file once in a while to write the memory buffer(s) + * into the given file. */ -template<eFileType filetype> class FormattedFileHandler : NonCopyable, NonMovable { +template<eFileType filetype, + size_t buffer_chunk_size = 64 * 1024, + size_t write_local_buffer_size = 1024> +class FormatHandler : NonCopyable, NonMovable { private: - std::FILE *outfile_ = nullptr; - std::string outfile_path_; + typedef std::vector<char> VectorChar; + std::vector<VectorChar> blocks_; public: - FormattedFileHandler(std::string outfile_path) noexcept(false) - : outfile_path_(std::move(outfile_path)) + /* Write contents to the buffer(s) into a file, and clear the buffers. */ + void write_to_file(FILE *f) { - outfile_ = BLI_fopen(outfile_path_.c_str(), "w"); - if (!outfile_) { - throw std::system_error(errno, std::system_category(), "Cannot open file " + outfile_path_); - } + for (const auto &b : blocks_) + fwrite(b.data(), 1, b.size(), f); + blocks_.clear(); } - ~FormattedFileHandler() + std::string get_as_string() const { - if (outfile_ && std::fclose(outfile_)) { - std::cerr << "Error: could not close the file '" << outfile_path_ - << "' properly, it may be corrupted." << std::endl; - } + std::string s; + for (const auto &b : blocks_) + s.append(b.data(), b.size()); + return s; + } + size_t get_block_count() const + { + return blocks_.size(); + } + + void append_from(FormatHandler<filetype, buffer_chunk_size, write_local_buffer_size> &v) + { + blocks_.insert(blocks_.end(), + std::make_move_iterator(v.blocks_.begin()), + std::make_move_iterator(v.blocks_.end())); + v.blocks_.clear(); } /** @@ -298,7 +328,7 @@ template<eFileType filetype> class FormattedFileHandler : NonCopyable, NonMovabl * `eFileType::MTL`. */ template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T> - constexpr void write(T &&...args) const + constexpr void write(T &&...args) { /* Get format syntax, number of arguments expected and whether types of given arguments are * valid. @@ -339,13 +369,47 @@ template<eFileType filetype> class FormattedFileHandler : NonCopyable, NonMovabl } } - template<typename... T> constexpr void write_impl(const char *fmt, T &&...args) const + /* Ensure the last block contains at least this amount of free space. + * If not, add a new block with max of block size & the amount of space needed. */ + void ensure_space(size_t at_least) + { + if (blocks_.empty() || (blocks_.back().capacity() - blocks_.back().size() < at_least)) { + VectorChar &b = blocks_.emplace_back(VectorChar()); + b.reserve(std::max(at_least, buffer_chunk_size)); + } + } + + template<typename... T> constexpr void write_impl(const char *fmt, T &&...args) { if constexpr (sizeof...(T) == 0) { - std::fputs(fmt, outfile_); + /* No arguments: just emit the format string. */ + size_t len = strlen(fmt); + ensure_space(len); + VectorChar &bb = blocks_.back(); + bb.insert(bb.end(), fmt, fmt + len); } else { - std::fprintf(outfile_, fmt, convert_to_primitive(std::forward<T>(args))...); + /* Format into a local buffer. */ + char buf[write_local_buffer_size]; + int needed = std::snprintf( + buf, write_local_buffer_size, fmt, convert_to_primitive(std::forward<T>(args))...); + if (needed < 0) + throw std::system_error( + errno, std::system_category(), "Failed to format obj export string into a buffer"); + ensure_space(needed + 1); /* Ensure space for zero terminator. */ + VectorChar &bb = blocks_.back(); + if (needed < write_local_buffer_size) { + /* String formatted successfully into the local buffer, copy it. */ + bb.insert(bb.end(), buf, buf + needed); + } + else { + /* Would need more space than the local buffer: insert said space and format again into + * that. */ + size_t bbEnd = bb.size(); + bb.insert(bb.end(), needed, ' '); + std::snprintf( + bb.data() + bbEnd, needed + 1, fmt, convert_to_primitive(std::forward<T>(args))...); + } } } }; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index c1b12ddd217..468631cdd82 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -69,16 +69,28 @@ OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Obj */ OBJMesh::~OBJMesh() { - free_mesh_if_needed(); - if (poly_smooth_groups_) { - MEM_freeN(poly_smooth_groups_); - } + clear(); } void OBJMesh::free_mesh_if_needed() { if (mesh_eval_needs_free_ && export_mesh_eval_) { BKE_id_free(nullptr, export_mesh_eval_); + export_mesh_eval_ = nullptr; + mesh_eval_needs_free_ = false; + } +} + +void OBJMesh::clear() +{ + free_mesh_if_needed(); + uv_indices_.clear_and_make_inline(); + uv_coords_.clear_and_make_inline(); + loop_to_normal_index_.clear_and_make_inline(); + normal_coords_.clear_and_make_inline(); + if (poly_smooth_groups_) { + MEM_freeN(poly_smooth_groups_); + poly_smooth_groups_ = nullptr; } } @@ -256,7 +268,7 @@ Vector<int> OBJMesh::calc_poly_vertex_indices(const int poly_index) const return r_poly_vertex_indices; } -void OBJMesh::store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coords) +void OBJMesh::store_uv_coords_and_indices() { const MPoly *mpoly = export_mesh_eval_->mpoly; const MLoop *mloop = export_mesh_eval_->mloop; @@ -276,7 +288,7 @@ void OBJMesh::store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coo uv_indices_.resize(totpoly); /* At least total vertices of a mesh will be present in its texture map. So * reserve minimum space early. */ - r_uv_coords.reserve(totvert); + uv_coords_.reserve(totvert); tot_uv_vertices_ = 0; for (int vertex_index = 0; vertex_index < totvert; vertex_index++) { @@ -288,11 +300,10 @@ void OBJMesh::store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coo const int vertices_in_poly = mpoly[uv_vert->poly_index].totloop; /* Store UV vertex coordinates. */ - r_uv_coords.resize(tot_uv_vertices_); + uv_coords_.resize(tot_uv_vertices_); const int loopstart = mpoly[uv_vert->poly_index].loopstart; Span<float> vert_uv_coords(mloopuv[loopstart + uv_vert->loop_of_poly_index].uv, 2); - r_uv_coords[tot_uv_vertices_ - 1][0] = vert_uv_coords[0]; - r_uv_coords[tot_uv_vertices_ - 1][1] = vert_uv_coords[1]; + uv_coords_[tot_uv_vertices_ - 1] = float2(vert_uv_coords[0], vert_uv_coords[1]); /* Store UV vertex indices. */ uv_indices_[uv_vert->poly_index].resize(vertices_in_poly); @@ -340,7 +351,7 @@ static float3 round_float3_to_n_digits(const float3 &v, int round_digits) return ans; } -void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords) +void OBJMesh::store_normal_coords_and_indices() { /* We'll round normal components to 4 digits. * This will cover up some minor differences @@ -358,7 +369,7 @@ void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords) *lnors)[3] = (const float(*)[3])(CustomData_get_layer(&export_mesh_eval_->ldata, CD_NORMAL)); for (int poly_index = 0; poly_index < export_mesh_eval_->totpoly; ++poly_index) { const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index]; - bool need_per_loop_normals = is_ith_poly_smooth(poly_index); + bool need_per_loop_normals = lnors != nullptr || (mpoly.flag & ME_SMOOTH); if (need_per_loop_normals) { for (int loop_of_poly = 0; loop_of_poly < mpoly.totloop; ++loop_of_poly) { float3 loop_normal; @@ -371,7 +382,7 @@ void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords) if (loop_norm_index == -1) { loop_norm_index = cur_normal_index++; normal_to_index.add(rounded_loop_normal, loop_norm_index); - r_normal_coords.append(rounded_loop_normal); + normal_coords_.append(rounded_loop_normal); } loop_to_normal_index_[loop_index] = loop_norm_index; } @@ -383,7 +394,7 @@ void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords) if (poly_norm_index == -1) { poly_norm_index = cur_normal_index++; normal_to_index.add(rounded_poly_normal, poly_norm_index); - r_normal_coords.append(rounded_poly_normal); + normal_coords_.append(rounded_poly_normal); } for (int i = 0; i < mpoly.totloop; ++i) { int loop_index = mpoly.loopstart + i; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index f3ace140006..4cfbffdcebc 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -82,11 +82,19 @@ class OBJMesh : NonCopyable { * Per-polygon-per-vertex UV vertex indices. */ Vector<Vector<int>> uv_indices_; + /* + * UV vertices. + */ + Vector<float2> uv_coords_; /** * Per-loop normal index. */ Vector<int> loop_to_normal_index_; /* + * Normal coords. + */ + Vector<float3> normal_coords_; + /* * Total number of normal indices (maximum entry, plus 1, in * the loop_to_norm_index_ vector). */ @@ -108,6 +116,9 @@ class OBJMesh : NonCopyable { OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Object *mesh_object); ~OBJMesh(); + /* Clear various arrays to release potentially large memory allocations. */ + void clear(); + int tot_vertices() const; int tot_polygons() const; int tot_uv_vertices() const; @@ -165,10 +176,14 @@ class OBJMesh : NonCopyable { Vector<int> calc_poly_vertex_indices(int poly_index) const; /** * Calculate UV vertex coordinates of an Object. - * - * \note Also store the UV vertex indices in the member variable. + * Stores the coordinates and UV vertex indices in the member variables. */ - void store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coords); + void store_uv_coords_and_indices(); + /* Get UV coordinates computed by store_uv_coords_and_indices. */ + const Vector<float2> &get_uv_coords() const + { + return uv_coords_; + } Span<int> calc_poly_uv_indices(int poly_index) const; /** * Calculate polygon normal of a polygon at given index. @@ -177,10 +192,15 @@ class OBJMesh : NonCopyable { */ float3 calc_poly_normal(int poly_index) const; /** - * Find the unique normals of the mesh and return them in \a r_normal_coords. - * Store the indices into that vector with for each loop in this #OBJMesh. + * Find the unique normals of the mesh and stores them in a member variable. + * Also stores the indices into that vector with for each loop. */ - void store_normal_coords_and_indices(Vector<float3> &r_normal_coords); + void store_normal_coords_and_indices(); + /* Get normals calculate by store_normal_coords_and_indices. */ + const Vector<float3> &get_normal_coords() const + { + return normal_coords_; + } /** * Calculate a polygon's polygon/loop normal indices. * \param poly_index Index of the polygon to calculate indices for. diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc index 48136dad5f7..3637a3f3c5f 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc @@ -193,8 +193,8 @@ static void store_bsdf_properties(const nodes::NodeRef *bsdf_node, copy_property_from_node(SOCK_FLOAT, bnode, "Roughness", {&roughness, 1}); } /* Empirical approximation. Importer should use the inverse of this method. */ - float spec_exponent = (1.0f - roughness) * 30; - spec_exponent *= spec_exponent; + float spec_exponent = (1.0f - roughness); + spec_exponent *= spec_exponent * 1000.0f; float specular = material->spec; if (bnode) { diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc index ec690115115..8a6d3b4b93a 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc @@ -102,4 +102,10 @@ int OBJCurve::get_nurbs_degree(const int spline_index) const return nurb->orderu - 1; } +short OBJCurve::get_nurbs_flagu(const int spline_index) const +{ + const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index)); + return nurb->flagu; +} + } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh index 0c71c3cc09d..d831afec0a0 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh @@ -61,6 +61,10 @@ class OBJCurve : NonCopyable { * Get the degree of the NURBS spline at the given index. */ int get_nurbs_degree(int spline_index) const; + /** + * Get the U flags (CU_NURB_*) of the NURBS spline at the given index. + */ + short get_nurbs_flagu(int spline_index) const; private: /** diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 0c753ccdcac..187f50277f1 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -25,6 +25,7 @@ #include "BKE_scene.h" #include "BLI_path_util.h" +#include "BLI_task.hh" #include "BLI_vector.hh" #include "DEG_depsgraph_query.h" @@ -155,44 +156,97 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me MTLWriter *mtl_writer, const OBJExportParams &export_params) { + /* Parallelization is over meshes/objects, which means + * we have to have the output text buffer for each object, + * and write them all into the file at the end. */ + size_t count = exportable_as_mesh.size(); + std::vector<FormatHandler<eFileType::OBJ>> buffers(count); + + /* Serial: gather material indices, ensure normals & edges. */ + Vector<Vector<int>> mtlindices; if (mtl_writer) { obj_writer.write_mtllib_name(mtl_writer->mtl_file_path()); + mtlindices.reserve(count); } - - /* Smooth groups and UV vertex indices may make huge memory allocations, so they should be freed - * right after they're written, instead of waiting for #blender::Vector to clean them up after - * all the objects are exported. */ for (auto &obj_mesh : exportable_as_mesh) { - obj_writer.write_object_name(*obj_mesh); - obj_writer.write_vertex_coords(*obj_mesh); - Vector<int> obj_mtlindices; + OBJMesh &obj = *obj_mesh; + if (mtl_writer) { + mtlindices.append(mtl_writer->add_materials(obj)); + } + if (export_params.export_normals) { + obj.ensure_mesh_normals(); + } + obj.ensure_mesh_edges(); + } - if (obj_mesh->tot_polygons() > 0) { - if (export_params.export_smooth_groups) { - obj_mesh->calc_smooth_groups(export_params.smooth_groups_bitflags); - } + /* Parallel over meshes: store normal coords & indices, uv coords and indices. */ + blender::threading::parallel_for(IndexRange(count), 1, [&](IndexRange range) { + for (const int i : range) { + OBJMesh &obj = *exportable_as_mesh[i]; if (export_params.export_normals) { - obj_writer.write_poly_normals(*obj_mesh); + obj.store_normal_coords_and_indices(); } if (export_params.export_uv) { - obj_writer.write_uv_coords(*obj_mesh); - } - if (mtl_writer) { - obj_mtlindices = mtl_writer->add_materials(*obj_mesh); + obj.store_uv_coords_and_indices(); } - /* This function takes a 0-indexed slot index for the obj_mesh object and - * returns the material name that we are using in the .obj file for it. */ - std::function<const char *(int)> matname_fn = [&](int s) -> const char * { - if (!mtl_writer || s < 0 || s >= obj_mtlindices.size()) { - return nullptr; + } + }); + + /* Serial: calculate index offsets; these are sequentially added + * over all meshes, and requite normal/uv indices to be calculated. */ + Vector<IndexOffsets> index_offsets; + index_offsets.reserve(count); + IndexOffsets offsets{0, 0, 0}; + for (auto &obj_mesh : exportable_as_mesh) { + OBJMesh &obj = *obj_mesh; + index_offsets.append(offsets); + offsets.vertex_offset += obj.tot_vertices(); + offsets.uv_vertex_offset += obj.tot_uv_vertices(); + offsets.normal_offset += obj.tot_normal_indices(); + } + + /* Parallel over meshes: main result writing. */ + blender::threading::parallel_for(IndexRange(count), 1, [&](IndexRange range) { + for (const int i : range) { + OBJMesh &obj = *exportable_as_mesh[i]; + auto &fh = buffers[i]; + + obj_writer.write_object_name(fh, obj); + obj_writer.write_vertex_coords(fh, obj); + + if (obj.tot_polygons() > 0) { + if (export_params.export_smooth_groups) { + obj.calc_smooth_groups(export_params.smooth_groups_bitflags); + } + if (export_params.export_normals) { + obj_writer.write_poly_normals(fh, obj); } - return mtl_writer->mtlmaterial_name(obj_mtlindices[s]); - }; - obj_writer.write_poly_elements(*obj_mesh, matname_fn); + if (export_params.export_uv) { + obj_writer.write_uv_coords(fh, obj); + } + /* This function takes a 0-indexed slot index for the obj_mesh object and + * returns the material name that we are using in the .obj file for it. */ + const auto *obj_mtlindices = mtlindices.is_empty() ? nullptr : &mtlindices[i]; + std::function<const char *(int)> matname_fn = [&](int s) -> const char * { + if (!obj_mtlindices || s < 0 || s >= obj_mtlindices->size()) { + return nullptr; + } + return mtl_writer->mtlmaterial_name((*obj_mtlindices)[s]); + }; + obj_writer.write_poly_elements(fh, index_offsets[i], obj, matname_fn); + } + obj_writer.write_edges_indices(fh, index_offsets[i], obj); + + /* Nothing will need this object's data after this point, release + * various arrays here. */ + obj.clear(); } - obj_writer.write_edges_indices(*obj_mesh); + }); - obj_writer.update_index_offsets(*obj_mesh); + /* Write all the object text buffers into the output file. */ + FILE *f = obj_writer.get_outfile(); + for (auto &b : buffers) { + b.write_to_file(f); } } @@ -202,11 +256,13 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me static void write_nurbs_curve_objects(const Vector<std::unique_ptr<OBJCurve>> &exportable_as_nurbs, const OBJWriter &obj_writer) { + FormatHandler<eFileType::OBJ> fh; /* #OBJCurve doesn't have any dynamically allocated memory, so it's fine * to wait for #blender::Vector to clean the objects up. */ for (const std::unique_ptr<OBJCurve> &obj_curve : exportable_as_nurbs) { - obj_writer.write_nurbs_curve(*obj_curve); + obj_writer.write_nurbs_curve(fh, *obj_curve); } + fh.write_to_file(obj_writer.get_outfile()); } void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, const char *filepath) diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index 89e1de49511..58329f63650 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -193,7 +193,7 @@ static std::string read_temp_file_in_string(const std::string &file_path) std::string res; size_t buffer_len; void *buffer = BLI_file_read_text_as_mem(file_path.c_str(), 0, &buffer_len); - if (buffer != NULL) { + if (buffer != nullptr) { res.assign((const char *)buffer, buffer_len); MEM_freeN(buffer); } @@ -238,6 +238,38 @@ TEST(obj_exporter_writer, mtllib) BLI_delete(out_file_path.c_str(), false, false); } +TEST(obj_exporter_writer, format_handler_buffer_chunking) +{ + /* Use a tiny buffer chunk size, so that the test below ends up creating several blocks. */ + FormatHandler<eFileType::OBJ, 16, 8> h; + h.write<eOBJSyntaxElement::object_name>("abc"); + h.write<eOBJSyntaxElement::object_name>("abcd"); + h.write<eOBJSyntaxElement::object_name>("abcde"); + h.write<eOBJSyntaxElement::object_name>("abcdef"); + h.write<eOBJSyntaxElement::object_name>("012345678901234567890123456789abcd"); + h.write<eOBJSyntaxElement::object_name>("123"); + h.write<eOBJSyntaxElement::curve_element_begin>(); + h.write<eOBJSyntaxElement::new_line>(); + h.write<eOBJSyntaxElement::nurbs_parameter_begin>(); + h.write<eOBJSyntaxElement::new_line>(); + + size_t got_blocks = h.get_block_count(); + ASSERT_EQ(got_blocks, 7); + + std::string got_string = h.get_as_string(); + using namespace std::string_literals; + const char *expected = R"(o abc +o abcd +o abcde +o abcdef +o 012345678901234567890123456789abcd +o 123 +curv 0.0 1.0 +parm u 0.0 +)"; + ASSERT_EQ(got_string, expected); +} + /* Return true if string #a and string #b are equal after their first newline. */ static bool strings_equal_after_first_lines(const std::string &a, const std::string &b) { @@ -313,8 +345,14 @@ class obj_exporter_regression_test : public obj_exporter_test { std::string output_mtl_str = read_temp_file_in_string(out_mtl_file_path); std::string golden_mtl_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_mtl; std::string golden_mtl_str = read_temp_file_in_string(golden_mtl_file_path); - ASSERT_TRUE(strings_equal_after_first_lines(output_mtl_str, golden_mtl_str)); - BLI_delete(out_mtl_file_path.c_str(), false, false); + are_equal = strings_equal_after_first_lines(output_mtl_str, golden_mtl_str); + if (save_failing_test_output && !are_equal) { + printf("failing test output in %s\n", out_mtl_file_path.c_str()); + } + ASSERT_TRUE(are_equal); + if (!save_failing_test_output || are_equal) { + BLI_delete(out_mtl_file_path.c_str(), false, false); + } } } }; @@ -378,6 +416,19 @@ TEST_F(obj_exporter_regression_test, nurbs_as_nurbs) "io_tests/blend_geometry/nurbs.blend", "io_tests/obj/nurbs.obj", "", _export.params); } +TEST_F(obj_exporter_regression_test, nurbs_curves_as_nurbs) +{ + OBJExportParamsDefault _export; + _export.params.forward_axis = OBJ_AXIS_Y_FORWARD; + _export.params.up_axis = OBJ_AXIS_Z_UP; + _export.params.export_materials = false; + _export.params.export_curves_as_nurbs = true; + compare_obj_export_to_golden("io_tests/blend_geometry/nurbs_curves.blend", + "io_tests/obj/nurbs_curves.obj", + "", + _export.params); +} + TEST_F(obj_exporter_regression_test, nurbs_as_mesh) { OBJExportParamsDefault _export; @@ -402,6 +453,18 @@ TEST_F(obj_exporter_regression_test, cube_all_data_triangulated) _export.params); } +TEST_F(obj_exporter_regression_test, cube_normal_edit) +{ + OBJExportParamsDefault _export; + _export.params.forward_axis = OBJ_AXIS_Y_FORWARD; + _export.params.up_axis = OBJ_AXIS_Z_UP; + _export.params.export_materials = false; + compare_obj_export_to_golden("io_tests/blend_geometry/cube_normal_edit.blend", + "io_tests/obj/cube_normal_edit.obj", + "", + _export.params); +} + TEST_F(obj_exporter_regression_test, suzanne_all_data) { OBJExportParamsDefault _export; diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 060b55ffe5c..4f479abe2b0 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -534,12 +534,14 @@ typedef struct PreviewImage { #define ID_IS_LINKED(_id) (((const ID *)(_id))->lib != NULL) -/* Note that this is a fairly high-level check, should be used at user interaction level, not in +/* Note that these are fairly high-level checks, should be used at user interaction level, not in * BKE_library_override typically (especially due to the check on LIB_TAG_EXTERN). */ -#define ID_IS_OVERRIDABLE_LIBRARY(_id) \ - (ID_IS_LINKED(_id) && !ID_MISSING(_id) && (((const ID *)(_id))->tag & LIB_TAG_EXTERN) != 0 && \ +#define ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(_id) \ + (ID_IS_LINKED(_id) && !ID_MISSING(_id) && \ (BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0 && \ !ELEM(GS(((ID *)(_id))->name), ID_SCE)) +#define ID_IS_OVERRIDABLE_LIBRARY(_id) \ + (ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY((_id)) && (((const ID *)(_id))->tag & LIB_TAG_EXTERN) != 0) /* NOTE: The three checks below do not take into account whether given ID is linked or not (when * chaining overrides over several libraries). User must ensure the ID is not linked itself @@ -798,7 +800,9 @@ typedef enum IDRecalcFlag { * Use this tag with a scene ID which owns the sequences. */ ID_RECALC_SEQUENCER_STRIPS = (1 << 14), - ID_RECALC_AUDIO_SEEK = (1 << 15), + /* Runs on frame-change (used for seeking audio too). */ + ID_RECALC_FRAME_CHANGE = (1 << 15), + ID_RECALC_AUDIO_FPS = (1 << 16), ID_RECALC_AUDIO_VOLUME = (1 << 17), ID_RECALC_AUDIO_MUTE = (1 << 18), @@ -886,7 +890,7 @@ typedef enum IDRecalcFlag { #define FILTER_ID_CF (1ULL << 28) #define FILTER_ID_WS (1ULL << 29) #define FILTER_ID_LP (1ULL << 31) -#define FILTER_ID_HA (1ULL << 32) +#define FILTER_ID_CV (1ULL << 32) #define FILTER_ID_PT (1ULL << 33) #define FILTER_ID_VO (1ULL << 34) #define FILTER_ID_SIM (1ULL << 35) @@ -897,7 +901,7 @@ typedef enum IDRecalcFlag { FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | \ FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | \ FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | FILTER_ID_WS | \ - FILTER_ID_LP | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM) + FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM) /** * This enum defines the index assigned to each type of IDs in the array returned by @@ -980,7 +984,7 @@ enum { INDEX_ID_ME, INDEX_ID_CU, INDEX_ID_MB, - INDEX_ID_HA, + INDEX_ID_CV, INDEX_ID_PT, INDEX_ID_VO, INDEX_ID_LT, diff --git a/source/blender/makesdna/DNA_ID_enums.h b/source/blender/makesdna/DNA_ID_enums.h index 45faf9e7f57..839c1e8933f 100644 --- a/source/blender/makesdna/DNA_ID_enums.h +++ b/source/blender/makesdna/DNA_ID_enums.h @@ -90,7 +90,7 @@ typedef enum ID_Type { ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */ ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */ - ID_HA = MAKE_ID2('H', 'A'), /* Hair */ + ID_CV = MAKE_ID2('C', 'V'), /* Curves */ ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */ ID_VO = MAKE_ID2('V', 'O'), /* Volume */ ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */ diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 82b20483902..1ca724b7108 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -478,9 +478,6 @@ typedef struct bPose { short flag; char _pad[2]; - /** Proxy layer: copy from armature, gets synced. */ - unsigned int proxy_layer; - char _pad1[4]; /** Local action time of this pose. */ float ctime; @@ -503,8 +500,6 @@ typedef struct bPose { /** Settings for visualization of bone animation. */ bAnimVizSettings avs; - /** Proxy active bone name, MAXBONENAME. */ - char proxy_act_bone[64]; } bPose; /* Pose->flag */ diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index 566ffd19669..1005b5186aa 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -77,7 +77,7 @@ typedef struct Bone { /** dist, weight: for non-deformgroup deforms. */ float dist, weight; /** - * The width for block bones. + * The width for block bones. The final X/Z bone widths are double these values. * * \note keep in this order for transform code which stores a pointer to `xwidth`, * accessing length and `zwidth` as offsets. diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 68e50c5b29d..2f41389f2c6 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -522,7 +522,7 @@ typedef enum eBrushUVSculptTool { SCULPT_TOOL_SLIDE_RELAX, \ SCULPT_TOOL_MASK) == 0) -/* ImagePaintSettings.tool */ +/** #ImagePaintSettings.tool */ typedef enum eBrushImagePaintTool { PAINT_TOOL_DRAW = 0, PAINT_TOOL_SOFTEN = 1, @@ -534,7 +534,7 @@ typedef enum eBrushImagePaintTool { /* The enums here should be kept in sync with the weight paint tool. * This is because #smooth_brush_toggle_on and #smooth_brush_toggle_off - * assumes that the Blur blursh has the same enum value. */ + * assumes that the blur brush has the same enum value. */ typedef enum eBrushVertexPaintTool { VPAINT_TOOL_DRAW = 0, VPAINT_TOOL_BLUR = 1, diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index d587bd8082b..53b1a60e53e 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -714,8 +714,6 @@ typedef enum eBConstraint_Flags { CONSTRAINT_SPACEONCE = (1 << 6), /* influence ipo is on constraint itself, not in action channel */ CONSTRAINT_OWN_IPO = (1 << 7), - /* indicates that constraint was added locally (i.e. didn't come from the proxy-lib) */ - CONSTRAINT_PROXY_LOCAL = (1 << 8), /* indicates that constraint is temporarily disabled (only used in GE) */ CONSTRAINT_OFF = (1 << 9), /* use bbone curve shape when calculating headtail values (also used by dependency graph!) */ diff --git a/source/blender/makesdna/DNA_hair_defaults.h b/source/blender/makesdna/DNA_curves_defaults.h index 095e4fdf583..66c7a1bd71b 100644 --- a/source/blender/makesdna/DNA_hair_defaults.h +++ b/source/blender/makesdna/DNA_curves_defaults.h @@ -24,10 +24,10 @@ /* clang-format off */ /* -------------------------------------------------------------------- */ -/** \name Hair Struct +/** \name Curves Struct * \{ */ -#define _DNA_DEFAULT_Hair \ +#define _DNA_DEFAULT_Curves \ { \ .flag = 0, \ } diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h new file mode 100644 index 00000000000..c7f31557e48 --- /dev/null +++ b/source/blender/makesdna/DNA_curves_types.h @@ -0,0 +1,108 @@ +/* + * 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 DNA + */ + +#pragma once + +#include "DNA_ID.h" +#include "DNA_customdata_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A reusable data structure for geometry consisting of many curves. All control point data is + * stored contiguously for better efficiency. Data for each curve is stored as a slice of the + * main #point_data array. + * + * The data structure is meant to be embedded in other data-blocks to allow reusing + * curve-processing algorithms for multiple Blender data-block types. + */ +typedef struct CurvesGeometry { + /** + * A runtime pointer to the "position" attribute data. + * \note This data is owned by #point_data. + */ + float (*position)[3]; + /** + * A runtime pointer to the "radius" attribute data. + * \note This data is owned by #point_data. + */ + float *radius; + + /** + * The start index of each curve in the point data. The size of each curve can be calculated by + * subtracting the offset from the next offset. That is valid even for the last curve because + * this array is allocated with a length one larger than the number of splines. + * + * \note This is *not* stored in #CustomData because its size is one larger than #curve_data. + */ + int *offsets; + + /** + * All attributes stored on control points (#ATTR_DOMAIN_POINT). + */ + CustomData point_data; + + /** + * All attributes stored on curves (#ATTR_DOMAIN_CURVE). + */ + CustomData curve_data; + + /** + * The total number of control points in all curves. + */ + int point_size; + /** + * The number of curves in the data-block. + */ + int curve_size; +} CurvesGeometry; + +typedef struct Curves { + ID id; + /* Animation data (must be immediately after id). */ + struct AnimData *adt; + + CurvesGeometry geometry; + + int flag; + int attributes_active_index; + + /* Materials. */ + struct Material **mat; + short totcol; + short _pad2[3]; + + /* Draw Cache. */ + void *batch_cache; +} Curves; + +/* Curves.flag */ +enum { + HA_DS_EXPAND = (1 << 0), +}; + +/* Only one material supported currently. */ +#define CURVES_MATERIAL_NR 1 + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 629a5e88de7..9c34d78c944 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -162,8 +162,8 @@ typedef enum CustomDataType { /* CD_LOCATION = 43, */ /* UNUSED */ /* CD_RADIUS = 44, */ /* UNUSED */ - CD_HAIRCURVE = 45, - CD_HAIRMAPPING = 46, + CD_PROP_INT8 = 45, + /* CD_HAIRMAPPING = 46, */ /* UNUSED, can be reused. */ CD_PROP_COLOR = 47, CD_PROP_FLOAT3 = 48, @@ -223,6 +223,7 @@ typedef enum CustomDataType { #define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3) #define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2) #define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL) +#define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8) #define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH) @@ -235,7 +236,8 @@ typedef enum CustomDataType { /* All generic attributes. */ #define CD_MASK_PROP_ALL \ (CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | \ - CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL) + CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL | \ + CD_MASK_PROP_INT8) typedef struct CustomData_MeshMasks { uint64_t vmask; diff --git a/source/blender/makesdna/DNA_hair_types.h b/source/blender/makesdna/DNA_hair_types.h deleted file mode 100644 index 2e819b32033..00000000000 --- a/source/blender/makesdna/DNA_hair_types.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup DNA - */ - -#pragma once - -#include "DNA_ID.h" -#include "DNA_customdata_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct HairCurve { - /* Index of first point of hair curve. */ - int firstpoint; - /* Number of points in hair curve, must be 2 or higher. */ - int numpoints; -} HairCurve; - -/* Hair attachment to a mesh. - * TODO: attach to tessellated triangles or polygons? - * TODO: what type of interpolation to use for uv? */ -typedef struct HairMapping { - float uv[2]; - int poly; -} HairMapping; - -typedef struct Hair { - ID id; - struct AnimData *adt; /* animation data (must be immediately after id) */ - - int flag; - int _pad1[1]; - - /* Geometry */ - float (*co)[3]; - float *radius; - struct HairCurve *curves; - struct HairMaping *mapping; - int totpoint; - int totcurve; - - /* Custom Data */ - struct CustomData pdata; - struct CustomData cdata; - int attributes_active_index; - int _pad3; - - /* Material */ - struct Material **mat; - short totcol; - short _pad2[3]; - - /* Draw Cache */ - void *batch_cache; -} Hair; - -/* Hair.flag */ -enum { - HA_DS_EXPAND = (1 << 0), -}; - -/* Only one material supported currently. */ -#define HAIR_MATERIAL_NR 1 - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 64c8fd3e3a9..7a789227128 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -142,10 +142,20 @@ typedef enum eImageTextureResolution { IMA_TEXTURE_RESOLUTION_LEN } eImageTextureResolution; +/* Defined in BKE_image.h. */ +struct PartialUpdateRegister; +struct PartialUpdateUser; + typedef struct Image_Runtime { /* Mutex used to guarantee thread-safe access to the cached ImBuf of the corresponding image ID. */ void *cache_mutex; + + /** \brief Register containing partial updates. */ + struct PartialUpdateRegister *partial_update_register; + /** \brief Partial update user for GPUTextures stored inside the Image. */ + struct PartialUpdateUser *partial_update_user; + } Image_Runtime; typedef struct Image { @@ -171,8 +181,6 @@ typedef struct Image { int lastframe; /* GPU texture flag. */ - /* Contains `ImagePartialRefresh`. */ - ListBase gpu_refresh_areas; int gpuframenr; short gpuflag; short gpu_pass; @@ -247,15 +255,13 @@ enum { enum { /** GPU texture needs to be refreshed. */ IMA_GPU_REFRESH = (1 << 0), - /** GPU texture needs to be partially refreshed. */ - IMA_GPU_PARTIAL_REFRESH = (1 << 1), /** All mipmap levels in OpenGL texture set? */ - IMA_GPU_MIPMAP_COMPLETE = (1 << 2), + IMA_GPU_MIPMAP_COMPLETE = (1 << 1), /* Reuse the max resolution textures as they fit in the limited scale. */ - IMA_GPU_REUSE_MAX_RESOLUTION = (1 << 3), + IMA_GPU_REUSE_MAX_RESOLUTION = (1 << 2), /* Has any limited scale textures been allocated. * Adds additional checks to reuse max resolution images when they fit inside limited scale. */ - IMA_GPU_HAS_LIMITED_SCALE_TEXTURES = (1 << 4), + IMA_GPU_HAS_LIMITED_SCALE_TEXTURES = (1 << 3), }; /* Image.source, where the image comes from */ diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 22c523901c0..f2493bd5b85 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -48,7 +48,6 @@ typedef struct MVert { /** #MVert.flag */ enum { /* SELECT = (1 << 0), */ - ME_VERT_TMP_TAG = (1 << 2), ME_HIDE = (1 << 4), ME_VERT_FACEDOT = (1 << 5), /* ME_VERT_MERGED = (1 << 6), */ @@ -266,6 +265,9 @@ typedef struct MStringProperty { typedef struct MBoolProperty { uint8_t b; } MBoolProperty; +typedef struct MInt8Property { + int8_t i; +} MInt8Property; /** \} */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 1d0796bda8b..8e38d52a4d7 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -128,15 +128,11 @@ typedef struct ModifierData { char *error; - /** Pointer to a #ModifierData in the original domain. */ - struct ModifierData *orig_modifier_data; - /** Runtime field which contains unique identifier of the modifier. */ SessionUUID session_uuid; /** Runtime field which contains runtime data which is specific to a modifier type. */ void *runtime; - void *_pad1; } ModifierData; typedef enum { @@ -1012,7 +1008,8 @@ typedef struct MeshDeformModifierData { float *bindcos; /* runtime */ - void (*bindfunc)(struct MeshDeformModifierData *mmd, + void (*bindfunc)(struct Object *object, + struct MeshDeformModifierData *mmd, struct Mesh *cagemesh, float *vertexcos, int totvert, diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 13f332f83fd..fd77e8b9f1d 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -514,8 +514,10 @@ typedef struct bNodeTree { /** Information about how inputs and outputs of the node group interact with fields. */ FieldInferencingInterfaceHandle *field_inferencing_interface; - /** Set init on fileread. */ - int type, init; + int type; + + char _pad1[4]; + /** * Sockets in groups have unique identifiers, adding new sockets always * will increase this counter. @@ -599,9 +601,6 @@ typedef struct bNodeTree { #define NTREE_TEXTURE 2 #define NTREE_GEOMETRY 3 -/** #NodeTree.init, flag */ -#define NTREE_TYPE_INIT 1 - /** #NodeTree.flag */ #define NTREE_DS_EXPAND (1 << 0) /* for animation editors */ #define NTREE_COM_OPENCL (1 << 1) /* use opencl */ @@ -1616,7 +1615,8 @@ typedef struct NodeGeometryStringToCurves { uint8_t align_x; /* GeometryNodeStringToCurvesAlignYMode */ uint8_t align_y; - char _pad[1]; + /* GeometryNodeStringToCurvesPivotMode */ + uint8_t pivot_mode; } NodeGeometryStringToCurves; typedef struct NodeGeometryDeleteGeometry { @@ -1848,7 +1848,6 @@ enum { /* math node clamp */ #define SHD_MATH_CLAMP 1 -/** Math node operations. */ typedef enum NodeMathOperation { NODE_MATH_ADD = 0, NODE_MATH_SUBTRACT = 1, @@ -1892,7 +1891,6 @@ typedef enum NodeMathOperation { NODE_MATH_SMOOTH_MAX = 39, } NodeMathOperation; -/** Vector Math node operations. */ typedef enum NodeVectorMathOperation { NODE_VECTOR_MATH_ADD = 0, NODE_VECTOR_MATH_SUBTRACT = 1, @@ -1926,14 +1924,20 @@ typedef enum NodeVectorMathOperation { NODE_VECTOR_MATH_MULTIPLY_ADD = 26, } NodeVectorMathOperation; -/** Boolean math node operations. */ -enum { +typedef enum NodeBooleanMathOperation { NODE_BOOLEAN_MATH_AND = 0, NODE_BOOLEAN_MATH_OR = 1, NODE_BOOLEAN_MATH_NOT = 2, -}; -/** Float compare node operations. */ + NODE_BOOLEAN_MATH_NAND = 3, + NODE_BOOLEAN_MATH_NOR = 4, + NODE_BOOLEAN_MATH_XNOR = 5, + NODE_BOOLEAN_MATH_XOR = 6, + + NODE_BOOLEAN_MATH_IMPLY = 7, + NODE_BOOLEAN_MATH_NIMPLY = 8, +} NodeBooleanMathOperation; + typedef enum NodeCompareMode { NODE_COMPARE_MODE_ELEMENT = 0, NODE_COMPARE_MODE_LENGTH = 1, @@ -1951,10 +1955,8 @@ typedef enum NodeCompareOperation { NODE_COMPARE_NOT_EQUAL = 5, NODE_COMPARE_COLOR_BRIGHTER = 6, NODE_COMPARE_COLOR_DARKER = 7, - } NodeCompareOperation; -/** Float to Int node operations. */ typedef enum FloatToIntRoundingMode { FN_NODE_FLOAT_TO_INT_ROUND = 0, FN_NODE_FLOAT_TO_INT_FLOOR = 1, @@ -2342,6 +2344,16 @@ typedef enum GeometryNodeStringToCurvesAlignYMode { GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM = 4, } GeometryNodeStringToCurvesAlignYMode; +typedef enum GeometryNodeStringToCurvesPivotMode { + GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_MIDPOINT = 0, + GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_LEFT = 1, + GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_CENTER = 2, + GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_RIGHT = 3, + GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT = 4, + GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_CENTER = 5, + GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_RIGHT = 6, +} GeometryNodeStringToCurvesPivotMode; + typedef enum GeometryNodeDeleteGeometryMode { GEO_NODE_DELETE_GEOMETRY_MODE_ALL = 0, GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE = 1, diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 5cce7ec5f67..ca8696d1326 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -181,6 +181,12 @@ typedef struct Object_Runtime { */ struct Mesh *mesh_deform_eval; + /* Evaluated mesh cage in edit mode. */ + struct Mesh *editmesh_eval_cage; + + /** Cached cage bounding box of `editmesh_eval_cage` for selection. */ + struct BoundBox *editmesh_bb_cage; + /** * Original grease pencil bGPdata pointer, before object->data was changed to point * to gpd_eval. @@ -210,6 +216,12 @@ typedef struct Object_Runtime { unsigned short local_collections_bits; short _pad2[3]; + + float (*crazyspace_deform_imats)[3][3]; + float (*crazyspace_deform_cos)[3]; + int crazyspace_num_verts; + + int _pad3[3]; } Object_Runtime; typedef struct ObjectLineArt { @@ -251,9 +263,10 @@ typedef struct Object { /** String describing subobject info, MAX_ID_NAME-2. */ char parsubstr[64]; struct Object *parent, *track; - /* If `ob->proxy` (or proxy_group), this object is proxy for object `ob->proxy`. */ - /* proxy_from is set in target back to the proxy. */ - struct Object *proxy, *proxy_group, *proxy_from; + /* Proxy pointer are deprecated, only kept for conversion to liboverrides. */ + struct Object *proxy DNA_DEPRECATED; + struct Object *proxy_group DNA_DEPRECATED; + struct Object *proxy_from DNA_DEPRECATED; /** Old animation system, deprecated for 2.5. */ struct Ipo *ipo DNA_DEPRECATED; /* struct Path *path; */ @@ -492,7 +505,7 @@ enum { /** Grease Pencil object used in 3D view but not used for annotation in 2D. */ OB_GPENCIL = 26, - OB_HAIR = 27, + OB_CURVES = 27, OB_POINTCLOUD = 28, @@ -507,7 +520,15 @@ enum { (((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) >= OB_GPENCIL && (_type) <= OB_VOLUME)) /** Does the object have some render-able geometry (unlike empties, cameras, etc.). */ #define OB_TYPE_IS_GEOMETRY(_type) \ - (ELEM(_type, OB_MESH, OB_SURF, OB_FONT, OB_MBALL, OB_GPENCIL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) + (ELEM(_type, \ + OB_MESH, \ + OB_SURF, \ + OB_FONT, \ + OB_MBALL, \ + OB_GPENCIL, \ + OB_CURVES, \ + OB_POINTCLOUD, \ + OB_VOLUME)) #define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) @@ -529,7 +550,7 @@ enum { ID_LT, \ ID_GD, \ ID_AR, \ - ID_HA, \ + ID_CV, \ ID_PT, \ ID_VO)) @@ -544,7 +565,7 @@ enum { case ID_LT: \ case ID_GD: \ case ID_AR: \ - case ID_HA: \ + case ID_CV: \ case ID_PT: \ case ID_VO diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h index a50d0524998..71a5c0eb2e5 100644 --- a/source/blender/makesdna/DNA_outliner_types.h +++ b/source/blender/makesdna/DNA_outliner_types.h @@ -101,7 +101,7 @@ typedef enum eTreeStoreElemType { TSE_DRIVER_BASE = 16, /* NO ID */ /* TSE_DRIVER = 17, */ /* UNUSED */ - TSE_PROXY = 18, + /* TSE_PROXY = 18, */ /* UNUSED */ TSE_R_LAYER_BASE = 19, TSE_R_LAYER = 20, /* TSE_R_PASS = 21, */ /* UNUSED */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 5fe67a34dae..70f0dc3f5de 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -80,6 +80,7 @@ typedef struct StripTransform { float rotation; /** 0-1 range, use SEQ_image_transform_origin_offset_pixelspace_get to convert to pixel space. */ float origin[2]; + int filter; } StripTransform; typedef struct StripColorBalance { @@ -788,6 +789,12 @@ typedef enum SequenceColorTag { SEQUENCE_COLOR_TOT, } SequenceColorTag; +/* Sequence->StripTransform->filter */ +enum { + SEQ_TRANSFORM_FILTER_NEAREST = 0, + SEQ_TRANSFORM_FILTER_BILINEAR = 1, +}; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 15bb1ef920d..d1b015485c9 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -651,7 +651,6 @@ typedef struct UserDef_Experimental { /* Debug options, always available. */ char use_undo_legacy; char no_override_auto_resync; - char no_proxy_to_override_conversion; char use_cycles_debug; char use_geometry_nodes_legacy; char show_asset_debug_info; @@ -659,14 +658,14 @@ typedef struct UserDef_Experimental { char SANITIZE_AFTER_HERE; /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ - char use_new_hair_type; + char use_new_curves_type; char use_new_point_cloud_type; char use_full_frame_compositor; char use_sculpt_vertex_colors; char use_sculpt_tools_tilt; char use_extended_asset_browser; char use_override_templates; - char _pad[1]; + char _pad[2]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; @@ -1253,7 +1252,7 @@ typedef enum eDupli_ID_Flags { USER_DUP_PSYS = (1 << 11), USER_DUP_LIGHTPROBE = (1 << 12), USER_DUP_GPENCIL = (1 << 13), - USER_DUP_HAIR = (1 << 14), + USER_DUP_CURVES = (1 << 14), USER_DUP_POINTCLOUD = (1 << 15), USER_DUP_VOLUME = (1 << 16), USER_DUP_LATTICE = (1 << 17), diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt index a3c54e91780..af30fa5cc9e 100644 --- a/source/blender/makesdna/intern/CMakeLists.txt +++ b/source/blender/makesdna/intern/CMakeLists.txt @@ -142,7 +142,7 @@ set(SRC ../DNA_defaults.h ../DNA_fluid_defaults.h ../DNA_gpencil_modifier_defaults.h - ../DNA_hair_defaults.h + ../DNA_curves_defaults.h ../DNA_image_defaults.h ../DNA_lattice_defaults.h ../DNA_light_defaults.h diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 5bc5de7a20b..fd23c5c618f 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -94,9 +94,9 @@ #include "DNA_cloth_types.h" #include "DNA_collection_types.h" #include "DNA_curve_types.h" +#include "DNA_curves_types.h" #include "DNA_fluid_types.h" #include "DNA_gpencil_modifier_types.h" -#include "DNA_hair_types.h" #include "DNA_image_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" @@ -127,9 +127,9 @@ #include "DNA_camera_defaults.h" #include "DNA_collection_defaults.h" #include "DNA_curve_defaults.h" +#include "DNA_curves_defaults.h" #include "DNA_fluid_defaults.h" #include "DNA_gpencil_modifier_defaults.h" -#include "DNA_hair_defaults.h" #include "DNA_image_defaults.h" #include "DNA_lattice_defaults.h" #include "DNA_light_defaults.h" @@ -184,8 +184,8 @@ SDNA_DEFAULT_DECL_STRUCT(FluidEffectorSettings); /* DNA_image_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(Image); -/* DNA_hair_defaults.h */ -SDNA_DEFAULT_DECL_STRUCT(Hair); +/* DNA_curves_defaults.h */ +SDNA_DEFAULT_DECL_STRUCT(Curves); /* DNA_lattice_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(Lattice); @@ -392,8 +392,8 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { /* DNA_image_defaults.h */ SDNA_DEFAULT_DECL(Image), - /* DNA_hair_defaults.h */ - SDNA_DEFAULT_DECL(Hair), + /* DNA_curves_defaults.h */ + SDNA_DEFAULT_DECL(Curves), /* DNA_lattice_defaults.h */ SDNA_DEFAULT_DECL(Lattice), diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 114c0b40407..b61f5315020 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -134,7 +134,7 @@ static const char *includefiles[] = { "DNA_lightprobe_types.h", "DNA_curveprofile_types.h", "DNA_xr_types.h", - "DNA_hair_types.h", + "DNA_curves_types.h", "DNA_pointcloud_types.h", "DNA_volume_types.h", "DNA_simulation_types.h", @@ -1555,8 +1555,18 @@ int main(int argc, char **argv) base_directory = BASE_HEADER; } + /* NOTE: #init_structDNA() in dna_genfile.c expects `sdna->data` is 4-bytes aligned. + * `DNAstr[]` buffer written by `makesdna` is used for this data, so make `DNAstr` forcefully + * 4-bytes aligned. */ +#ifdef __GNUC__ +# define FORCE_ALIGN_4 " __attribute__((aligned(4))) " +#else +# define FORCE_ALIGN_4 " " +#endif fprintf(file_dna, "extern const unsigned char DNAstr[];\n"); - fprintf(file_dna, "const unsigned char DNAstr[] = {\n"); + fprintf(file_dna, "const unsigned char" FORCE_ALIGN_4 "DNAstr[] = {\n"); +#undef FORCE_ALIGN_4 + if (make_structDNA(base_directory, file_dna, file_dna_offsets, file_dna_verify)) { /* error */ fclose(file_dna); @@ -1634,7 +1644,7 @@ int main(int argc, char **argv) #include "DNA_freestyle_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" -#include "DNA_hair_types.h" +#include "DNA_curves_types.h" #include "DNA_image_types.h" #include "DNA_ipo_types.h" #include "DNA_key_types.h" diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 1ade964854d..47afa0f9a13 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -104,6 +104,8 @@ extern StructRNA RNA_BuildGpencilModifier; extern StructRNA RNA_BuildModifier; extern StructRNA RNA_ByteColorAttribute; extern StructRNA RNA_ByteColorAttributeValue; +extern StructRNA RNA_ByteIntAttribute; +extern StructRNA RNA_ByteIntAttributeValue; extern StructRNA RNA_CacheFile; extern StructRNA RNA_CacheFileLayer; extern StructRNA RNA_Camera; @@ -143,6 +145,7 @@ extern StructRNA RNA_CompositorNodeCombHSVA; extern StructRNA RNA_CompositorNodeCombRGBA; extern StructRNA RNA_CompositorNodeCombYCCA; extern StructRNA RNA_CompositorNodeCombYUVA; +extern StructRNA RNA_CompositorNodeCombineXYZ; extern StructRNA RNA_CompositorNodeComposite; extern StructRNA RNA_CompositorNodeCornerPin; extern StructRNA RNA_CompositorNodeCrop; @@ -187,6 +190,7 @@ extern StructRNA RNA_CompositorNodeRLayers; extern StructRNA RNA_CompositorNodeRotate; extern StructRNA RNA_CompositorNodeScale; extern StructRNA RNA_CompositorNodeSceneTime; +extern StructRNA RNA_CompositorNodeSeparateXYZ; extern StructRNA RNA_CompositorNodeSepHSVA; extern StructRNA RNA_CompositorNodeSepRGBA; extern StructRNA RNA_CompositorNodeSepYCCA; @@ -304,7 +308,7 @@ extern StructRNA RNA_GizmoProperties; extern StructRNA RNA_GlowSequence; extern StructRNA RNA_GpencilModifier; extern StructRNA RNA_GreasePencil; -extern StructRNA RNA_Hair; +extern StructRNA RNA_Curves; extern StructRNA RNA_Header; extern StructRNA RNA_Histogram; extern StructRNA RNA_HookGpencilModifier; @@ -1512,10 +1516,21 @@ void RNA_collection_clear(PointerRNA *ptr, const char *name); /** * Check if the #IDproperty exists, for operators. + * + * \param use_ghost: Internally an #IDProperty may exist, + * without the RNA considering it to be "set", see #IDP_FLAG_GHOST. + * This is used for operators, where executing an operator that has run previously + * will re-use the last value (unless #PROP_SKIP_SAVE property is set). + * In this case, the presence of the an existing value shouldn't prevent it being initialized + * from the context. Even though the this value will be returned if it's requested, + * it's not considered to be set (as it would if the menu item or key-map defined it's value). + * Set `use_ghost` to true for default behavior, otherwise false to check if there is a value + * exists internally and would be returned on request. */ bool RNA_property_is_set_ex(PointerRNA *ptr, PropertyRNA *prop, bool use_ghost); bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop); void RNA_property_unset(PointerRNA *ptr, PropertyRNA *prop); +/** See #RNA_property_is_set_ex documentation. */ bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool use_ghost); bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier); bool RNA_property_is_idprop(const PropertyRNA *prop); diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index c5581c01921..e37eb9f7188 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -322,7 +322,7 @@ typedef enum PropertyFlag { * FREE FLAGS: 2, 3, 4, 5, 6, 7, 8, 9, 12 and above. */ typedef enum PropertyOverrideFlag { - /** Means the property can be overridden by a local 'proxy' of some linked datablock. */ + /** Means that the property can be overridden by a local override of some linked datablock. */ PROPOVERRIDE_OVERRIDABLE_LIBRARY = (1 << 0), /** diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index ab141f01b13..71ada76fb76 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -70,6 +70,7 @@ set(DEFSRC rna_packedfile.c rna_palette.c rna_particle.c + rna_pointcloud.c rna_pose.c rna_render.c rna_rigidbody.c @@ -100,11 +101,9 @@ set(DEFSRC if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) - add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_HAIR_NODES) + add_definitions(-DWITH_NEW_CURVES_TYPE) list(APPEND DEFSRC - rna_hair.c - rna_pointcloud.c + rna_curves.c rna_simulation.c ) endif() diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index a6732ca1760..0fadbda5a18 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4376,8 +4376,8 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint}, {"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve}, {"rna_gpencil.c", NULL, RNA_def_gpencil}, -#ifdef WITH_HAIR_NODES - {"rna_hair.c", NULL, RNA_def_hair}, +#ifdef WITH_NEW_CURVES_TYPE + {"rna_curves.c", NULL, RNA_def_curves}, #endif {"rna_image.c", "rna_image_api.c", RNA_def_image}, {"rna_key.c", NULL, RNA_def_key}, @@ -4401,9 +4401,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_packedfile.c", NULL, RNA_def_packedfile}, {"rna_palette.c", NULL, RNA_def_palette}, {"rna_particle.c", NULL, RNA_def_particle}, -#ifdef WITH_POINT_CLOUD {"rna_pointcloud.c", NULL, RNA_def_pointcloud}, -#endif {"rna_pose.c", "rna_pose_api.c", RNA_def_pose}, {"rna_curveprofile.c", NULL, RNA_def_profile}, {"rna_lightprobe.c", NULL, RNA_def_lightprobe}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b92123f445b..9bb78cb483d 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -76,7 +76,7 @@ const EnumPropertyItem rna_enum_id_type_items[] = { {ID_SPK, "SPEAKER", ICON_SPEAKER, "Speaker", ""}, {ID_TXT, "TEXT", ICON_TEXT, "Text", ""}, {ID_TE, "TEXTURE", ICON_TEXTURE_DATA, "Texture", ""}, - {ID_HA, "HAIR", ICON_HAIR_DATA, "Hair", ""}, + {ID_CV, "CURVES", ICON_CURVES_DATA, "Hair Curves", ""}, {ID_PT, "POINTCLOUD", ICON_POINTCLOUD_DATA, "Point Cloud", ""}, {ID_VO, "VOLUME", ICON_VOLUME_DATA, "Volume", ""}, {ID_WM, "WINDOWMANAGER", ICON_WINDOW, "Window Manager", ""}, @@ -151,7 +151,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { ICON_OUTLINER_COLLECTION, "Collections", "Show Collection data-blocks"}, - {FILTER_ID_HA, "filter_hair", ICON_HAIR_DATA, "Hairs", "Show/hide Hair data-blocks"}, + {FILTER_ID_CV, "filter_hair", ICON_CURVES_DATA, "Hairs", "Show/hide Hair data-blocks"}, {FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"}, {FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"}, {FILTER_ID_LP, @@ -385,9 +385,9 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_FreestyleLineStyle) { return ID_LS; } -# ifdef WITH_HAIR_NODES - if (base_type == &RNA_Hair) { - return ID_HA; +# ifdef WITH_NEW_CURVES_TYPE + if (base_type == &RNA_Curves) { + return ID_CV; } # endif if (base_type == &RNA_Lattice) { @@ -423,11 +423,9 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_PaintCurve) { return ID_PC; } -# ifdef WITH_POINT_CLOUD if (base_type == &RNA_PointCloud) { return ID_PT; } -# endif if (base_type == &RNA_LightProbe) { return ID_LP; } @@ -494,9 +492,9 @@ StructRNA *ID_code_to_RNA_type(short idcode) return &RNA_GreasePencil; case ID_GR: return &RNA_Collection; - case ID_HA: -# ifdef WITH_HAIR_NODES - return &RNA_Hair; + case ID_CV: +# ifdef WITH_NEW_CURVES_TYPE + return &RNA_Curves; # else return &RNA_ID; # endif @@ -533,11 +531,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_PC: return &RNA_PaintCurve; case ID_PT: -# ifdef WITH_POINT_CLOUD return &RNA_PointCloud; -# else - return &RNA_ID; -# endif case ID_LP: return &RNA_LightProbe; case ID_SCE: @@ -710,6 +704,7 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) } WM_main_add_notifier(NC_ID | NA_ADDED, NULL); + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); return local_id; } @@ -724,9 +719,11 @@ static ID *rna_ID_override_hierarchy_create( BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); ID *id_root_override = NULL; - BKE_lib_override_library_create(bmain, scene, view_layer, id, id_reference, &id_root_override); + BKE_lib_override_library_create( + bmain, scene, view_layer, NULL, id, id_reference, &id_root_override); WM_main_add_notifier(NC_ID | NA_ADDED, NULL); + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); return id_root_override; } @@ -747,6 +744,8 @@ static void rna_ID_override_template_create(ID *id, ReportList *reports) return; } BKE_lib_override_library_template_create(id); + + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); } static void rna_ID_override_library_operations_update(ID *id, @@ -760,6 +759,8 @@ static void rna_ID_override_library_operations_update(ID *id, } BKE_lib_override_library_operations_create(bmain, id); + + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); } static void rna_ID_override_library_reset(ID *id, @@ -779,6 +780,8 @@ static void rna_ID_override_library_reset(ID *id, else { BKE_lib_override_library_id_reset(bmain, id); } + + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); } static void rna_ID_override_library_destroy(ID *id, @@ -799,6 +802,8 @@ static void rna_ID_override_library_destroy(ID *id, BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE); BKE_id_delete(bmain, id); } + + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); } static IDOverrideLibraryProperty *rna_ID_override_library_properties_add( @@ -812,6 +817,7 @@ static IDOverrideLibraryProperty *rna_ID_override_library_properties_add( BKE_report(reports, RPT_DEBUG, "No new override property created, property already exists"); } + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); return result; } @@ -825,6 +831,8 @@ static void rna_ID_override_library_properties_remove(IDOverrideLibrary *overrid } BKE_lib_override_library_property_delete(override_library, override_property); + + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); } static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_operations_add( @@ -851,6 +859,8 @@ static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_oper if (!created) { BKE_report(reports, RPT_DEBUG, "No new override operation created, operation already exists"); } + + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); return result; } @@ -865,6 +875,8 @@ static void rna_ID_override_library_property_operations_remove( } BKE_lib_override_library_property_operation_delete(override_property, override_operation); + + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); } static void rna_ID_update_tag(ID *id, Main *bmain, ReportList *reports, int flag) @@ -937,9 +949,9 @@ static void rna_ID_user_remap(ID *id, Main *bmain, ID *new_id) } } -static struct ID *rna_ID_make_local(struct ID *self, Main *bmain, bool clear_proxy) +static struct ID *rna_ID_make_local(struct ID *self, Main *bmain, bool UNUSED(clear_proxy)) { - BKE_lib_id_make_local(bmain, self, clear_proxy ? 0 : LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BKE_lib_id_make_local(bmain, self, 0); ID *ret_id = self->newid ? self->newid : self; BKE_id_newptr_and_tag_clear(self); @@ -1753,6 +1765,7 @@ static void rna_def_ID_override_library_property(BlenderRNA *brna) "IDOverrideLibraryPropertyOperation", "Operations", "List of overriding operations for a property"); + RNA_def_property_update(prop, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); rna_def_ID_override_library_property_operations(brna, prop); rna_def_ID_override_library_property_operation(brna); @@ -1805,8 +1818,9 @@ static void rna_def_ID_override_library(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "ID Library Override", "Struct gathering all data needed by overridden linked IDs"); - RNA_def_pointer( + prop = RNA_def_pointer( srna, "reference", "ID", "Reference ID", "Linked ID used as reference by this override"); + RNA_def_property_update(prop, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); prop = RNA_def_boolean(srna, "is_in_hierarchy", @@ -1814,6 +1828,7 @@ static void rna_def_ID_override_library(BlenderRNA *brna) "Is In Hierarchy", "Whether this library override is defined as part of a library " "hierarchy, or as a single, isolated and autonomous override"); + RNA_def_property_update(prop, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY); prop = RNA_def_collection(srna, @@ -1821,6 +1836,7 @@ static void rna_def_ID_override_library(BlenderRNA *brna) "IDOverrideLibraryProperty", "Properties", "List of overridden properties"); + RNA_def_property_update(prop, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); rna_def_ID_override_library_properties(brna, prop); /* Update function. */ @@ -2073,13 +2089,7 @@ static void rna_def_ID(BlenderRNA *brna) "Make this datablock local, return local one " "(may be a copy of the original, in case it is also indirectly used)"); RNA_def_function_flag(func, FUNC_USE_MAIN); - parm = RNA_def_boolean( - func, - "clear_proxy", - true, - "", - "Whether to clear proxies (the default behavior, " - "note that if object has to be duplicated to be made local, proxies are always cleared)"); + parm = RNA_def_boolean(func, "clear_proxy", true, "", "Deprecated, has no effect"); parm = RNA_def_pointer(func, "id", "ID", "", "This ID, or the new ID if it was copied"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index 6b134977c5a..f4236a860ab 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -625,11 +625,11 @@ static void rna_def_dopesheet(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_FILE, 0); RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); - prop = RNA_def_property(srna, "show_hairs", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "show_hair_curves", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag2", ADS_FILTER_NOHAIR); RNA_def_property_ui_text( prop, "Display Hair", "Include visualization of hair related animation data"); - RNA_def_property_ui_icon(prop, ICON_OUTLINER_OB_HAIR, 0); + RNA_def_property_ui_icon(prop, ICON_OUTLINER_OB_CURVES, 0); RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); prop = RNA_def_property(srna, "show_pointclouds", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 28e50e80f32..b22d3654431 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -151,18 +151,9 @@ static void rna_Armature_edit_bone_remove(bArmature *arm, RNA_POINTER_INVALIDATE(ebone_ptr); } -static void rna_Armature_update_layers(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Armature_update_layers(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { bArmature *arm = (bArmature *)ptr->owner_id; - Object *ob; - - /* proxy lib exception, store it here so we can restore layers on file - * load, since it would otherwise get lost due to being linked data */ - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->data == arm && ob->pose) { - ob->pose->proxy_layer = arm->layer; - } - } DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); WM_main_add_notifier(NC_GEOM | ND_DATA, arm); diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index e79cbc838d4..e80c8559020 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -237,7 +237,7 @@ static void rna_AssetMetaData_catalog_id_set(PointerRNA *ptr, const char *value) } if (!BLI_uuid_parse_string(&new_uuid, value)) { - // TODO(Sybren): raise ValueError exception once that's possible from an RNA setter. + /* TODO(@sybren): raise ValueError exception once that's possible from an RNA setter. */ printf("UUID %s not formatted correctly, ignoring new value\n", value); return; } diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 35da353a043..dc0d00aaa77 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -26,8 +26,8 @@ #include "rna_internal.h" +#include "DNA_curves_types.h" #include "DNA_customdata_types.h" -#include "DNA_hair_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" @@ -46,6 +46,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"}, + {CD_PROP_INT8, "INT8", 0, "8-Bit Integer", "Smaller integer with a range from -128 to 127"}, {0, NULL, 0, NULL, NULL}, }; @@ -59,6 +60,7 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"}, + {CD_PROP_INT8, "INT8", 0, "8-Bit Integer", "Smaller integer with a range from -128 to 127"}, {0, NULL, 0, NULL, NULL}, }; @@ -133,6 +135,8 @@ static StructRNA *srna_by_custom_data_layer_type(const CustomDataType type) return &RNA_BoolAttribute; case CD_PROP_FLOAT2: return &RNA_Float2Attribute; + case CD_PROP_INT8: + return &RNA_ByteIntAttribute; default: return NULL; } @@ -184,7 +188,7 @@ const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id, if (id_type == ID_PT && !ELEM(domain_item->value, ATTR_DOMAIN_POINT)) { continue; } - if (id_type == ID_HA && !ELEM(domain_item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + if (id_type == ID_CV && !ELEM(domain_item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { continue; } if (id_type == ID_ME && ELEM(domain_item->value, ATTR_DOMAIN_CURVE)) { @@ -253,6 +257,9 @@ static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRN case CD_PROP_FLOAT2: struct_size = sizeof(float[2]); break; + case CD_PROP_INT8: + struct_size = sizeof(int8_t); + break; default: struct_size = 0; length = 0; @@ -294,6 +301,28 @@ static void rna_ByteColorAttributeValue_color_set(PointerRNA *ptr, const float * linearrgb_to_srgb_uchar4(&mlcol->r, values); } +/* Int8 Attribute. */ + +static int rna_ByteIntAttributeValue_get(PointerRNA *ptr) +{ + int8_t *value = (int8_t *)ptr->data; + return (int)(*value); +} + +static void rna_ByteIntAttributeValue_set(PointerRNA *ptr, const int new_value) +{ + int8_t *value = (int8_t *)ptr->data; + if (new_value > INT8_MAX) { + *value = INT8_MAX; + } + else if (new_value < INT8_MIN) { + *value = INT8_MIN; + } + else { + *value = (int8_t)new_value; + } +} + /* Attribute Group */ static PointerRNA rna_AttributeGroup_new( @@ -648,6 +677,36 @@ static void rna_def_attribute_bool(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "b", 0x01); } +static void rna_def_attribute_int8(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ByteIntAttribute", "Attribute"); + RNA_def_struct_sdna(srna, "CustomDataLayer"); + RNA_def_struct_ui_text(srna, "8-bit Int Attribute", "8-bit int geometry attribute"); + + prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "ByteIntAttributeValue"); + RNA_def_property_collection_funcs(prop, + "rna_Attribute_data_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_Attribute_data_length", + NULL, + NULL, + NULL); + + srna = RNA_def_struct(brna, "ByteIntAttributeValue", NULL); + RNA_def_struct_sdna(srna, "MInt8Property"); + RNA_def_struct_ui_text( + srna, "8-bit Integer Attribute Value", "8-bit value in geometry attribute"); + prop = RNA_def_property(srna, "value", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs( + prop, "rna_ByteIntAttributeValue_get", "rna_ByteIntAttributeValue_set", NULL); +} + static void rna_def_attribute_float2(BlenderRNA *brna) { StructRNA *srna; @@ -723,6 +782,7 @@ static void rna_def_attribute(BlenderRNA *brna) rna_def_attribute_string(brna); rna_def_attribute_bool(brna); rna_def_attribute_float2(brna); + rna_def_attribute_int8(brna); } /* Mesh/PointCloud/Hair.attributes */ diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 0c993660f39..e3a06c44eee 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -3543,14 +3543,6 @@ void RNA_def_constraint(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_ACTIVE); RNA_def_property_ui_text(prop, "Active", "Constraint is the one being edited"); - prop = RNA_def_property(srna, "is_proxy_local", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_PROXY_LOCAL); - RNA_def_property_ui_text( - prop, - "Proxy Local", - "Constraint was added in this proxy instance (i.e. did not belong to source Armature)"); - /* values */ prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "enforce"); diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c new file mode 100644 index 00000000000..faa067000bb --- /dev/null +++ b/source/blender/makesrna/intern/rna_curves.c @@ -0,0 +1,296 @@ +/* + * 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 RNA + */ + +#include <stdlib.h> + +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#include "DNA_curves_types.h" + +#include "BLI_math_base.h" +#include "BLI_string.h" + +#ifdef RNA_RUNTIME + +# include "BLI_math_vector.h" + +# include "BKE_attribute.h" +# include "BKE_curves.h" + +# include "DEG_depsgraph.h" + +# include "WM_api.h" +# include "WM_types.h" + +static Curves *rna_curves(PointerRNA *ptr) +{ + return (Curves *)ptr->owner_id; +} + +static int rna_Curves_curve_offset_data_length(PointerRNA *ptr) +{ + const Curves *curves = rna_curves(ptr); + return curves->geometry.curve_size + 1; +} + +static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + const Curves *curves = rna_curves(ptr); + rna_iterator_array_begin(iter, + (void *)curves->geometry.offsets, + sizeof(int), + curves->geometry.curve_size + 1, + false, + NULL); +} + +static int rna_CurvePoint_index_get(PointerRNA *ptr) +{ + const Curves *curves = rna_curves(ptr); + const float(*co)[3] = ptr->data; + return (int)(co - curves->geometry.position); +} + +static void rna_CurvePoint_location_get(PointerRNA *ptr, float value[3]) +{ + copy_v3_v3(value, (const float *)ptr->data); +} + +static void rna_CurvePoint_location_set(PointerRNA *ptr, const float value[3]) +{ + copy_v3_v3((float *)ptr->data, value); +} + +static float rna_CurvePoint_radius_get(PointerRNA *ptr) +{ + const Curves *curves = rna_curves(ptr); + if (curves->geometry.radius == NULL) { + return 0.0f; + } + const float(*co)[3] = ptr->data; + return curves->geometry.radius[co - curves->geometry.position]; +} + +static void rna_CurvePoint_radius_set(PointerRNA *ptr, float value) +{ + const Curves *curves = rna_curves(ptr); + if (curves->geometry.radius == NULL) { + return; + } + const float(*co)[3] = ptr->data; + curves->geometry.radius[co - curves->geometry.position] = value; +} + +static char *rna_CurvePoint_path(PointerRNA *ptr) +{ + return BLI_sprintfN("points[%d]", rna_CurvePoint_index_get(ptr)); +} + +static int rna_CurveSlice_index_get(PointerRNA *ptr) +{ + Curves *curves = rna_curves(ptr); + return (int)((int *)ptr->data - curves->geometry.offsets); +} + +static char *rna_CurveSlice_path(PointerRNA *ptr) +{ + return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get(ptr)); +} + +static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Curves *curves = rna_curves(ptr); + const int *offset_ptr = (int *)ptr->data; + const int offset = *offset_ptr; + const int size = *(offset_ptr + 1) - offset; + float(*co)[3] = curves->geometry.position + *offset_ptr; + rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL); +} + +static int rna_CurveSlice_first_point_index_get(PointerRNA *ptr) +{ + const int *offset_ptr = (int *)ptr->data; + return *offset_ptr; +} + +static int rna_CurveSlice_points_length_get(PointerRNA *ptr) +{ + const int *offset_ptr = (int *)ptr->data; + const int offset = *offset_ptr; + return *(offset_ptr + 1) - offset; +} + +static void rna_Curves_update_data(struct Main *UNUSED(bmain), + struct 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); + } +} + +#else + +static void rna_def_curves_point(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "CurvePoint", NULL); + RNA_def_struct_ui_text(srna, "Curve Point", "Curve curve control point"); + RNA_def_struct_path_func(srna, "rna_CurvePoint_path"); + + prop = RNA_def_property(srna, "position", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_float_funcs( + prop, "rna_CurvePoint_location_get", "rna_CurvePoint_location_set", NULL); + RNA_def_property_ui_text(prop, "Position", ""); + RNA_def_property_update(prop, 0, "rna_Curves_update_data"); + + prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_funcs( + prop, "rna_CurvePoint_radius_get", "rna_CurvePoint_radius_set", NULL); + RNA_def_property_ui_text(prop, "Radius", ""); + RNA_def_property_update(prop, 0, "rna_Curves_update_data"); + + prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_int_funcs(prop, "rna_CurvePoint_index_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Index", "Index of this points"); +} + +static void rna_def_curves_curve(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "CurveSlice", NULL); + RNA_def_struct_ui_text(srna, "Curve Slice", "A single curve from a curves data-block"); + RNA_def_struct_path_func(srna, "rna_CurveSlice_path"); + + prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "CurvePoint"); + RNA_def_property_ui_text(prop, "Points", "Control points of the curve"); + RNA_def_property_collection_funcs(prop, + "rna_CurveSlice_points_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_CurveSlice_points_length_get", + NULL, + NULL, + NULL); + + prop = RNA_def_property(srna, "first_point_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_int_funcs(prop, "rna_CurveSlice_first_point_index_get", NULL, NULL); + RNA_def_property_ui_text( + prop, "First Point Index", "The index of this curve's first control point"); + + prop = RNA_def_property(srna, "points_length", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_int_funcs(prop, "rna_CurveSlice_points_length_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Number of Points", "Number of control points in the curve"); + + prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_int_funcs(prop, "rna_CurveSlice_index_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Index", "Index of this curve"); +} + +static void rna_def_curves(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Curves", "ID"); + RNA_def_struct_ui_text(srna, "Hair Curves", "Hair data-block for hair curves"); + RNA_def_struct_ui_icon(srna, ICON_CURVES_DATA); + + /* Point and Curve RNA API helpers. */ + + prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", "geometry.curve_size"); + RNA_def_property_struct_type(prop, "CurveSlice"); + RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); + + /* TODO: better solution for (*co)[3] parsing issue. */ + + RNA_define_verify_sdna(0); + prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_struct_type(prop, "CurvePoint"); + RNA_def_property_ui_text(prop, "Points", "Control points of all curves"); + RNA_define_verify_sdna(1); + + /* Direct access to built-in attributes. */ + + RNA_define_verify_sdna(0); + prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); + RNA_def_property_update(prop, 0, "rna_Curves_update_data"); + RNA_define_verify_sdna(1); + + prop = RNA_def_property(srna, "curve_offset_data", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", NULL); + RNA_def_property_struct_type(prop, "IntAttributeValue"); + RNA_def_property_collection_funcs(prop, + "rna_Curves_curve_offset_data_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_Curves_curve_offset_data_length", + NULL, + NULL, + NULL); + RNA_def_property_update(prop, 0, "rna_Curves_update_data"); + + /* materials */ + prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol"); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_ui_text(prop, "Materials", ""); + RNA_def_property_srna(prop, "IDMaterials"); /* see rna_ID.c */ + RNA_def_property_collection_funcs( + prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int"); + + /* attributes */ + rna_def_attributes_common(srna); + + /* common */ + rna_def_animdata_common(srna); +} + +void RNA_def_curves(BlenderRNA *brna) +{ + rna_def_curves_point(brna); + rna_def_curves_curve(brna); + rna_def_curves(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 3f380cd1830..7ef2f757cd8 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -2228,7 +2228,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) prop = RNA_def_property(srna, "use_solo_mode", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_SOLO_MODE); RNA_def_property_ui_text( - prop, "Solo Mode", "In Paint mode display only layers with keyframe in current frame"); + prop, "Solo Mode", "In Draw Mode only display layers with keyframe in current frame"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Layer is used as Ruler. */ diff --git a/source/blender/makesrna/intern/rna_hair.c b/source/blender/makesrna/intern/rna_hair.c deleted file mode 100644 index 4ca66c6b583..00000000000 --- a/source/blender/makesrna/intern/rna_hair.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup RNA - */ - -#include <stdlib.h> - -#include "RNA_define.h" -#include "RNA_enum_types.h" - -#include "rna_internal.h" - -#include "DNA_hair_types.h" - -#include "BLI_math_base.h" -#include "BLI_string.h" - -#ifdef RNA_RUNTIME - -# include "BLI_math_vector.h" - -# include "BKE_attribute.h" -# include "BKE_hair.h" - -# include "DEG_depsgraph.h" - -# include "WM_api.h" -# include "WM_types.h" - -static Hair *rna_hair(PointerRNA *ptr) -{ - return (Hair *)ptr->owner_id; -} - -static int rna_HairPoint_index_get(PointerRNA *ptr) -{ - const Hair *hair = rna_hair(ptr); - const float(*co)[3] = ptr->data; - return (int)(co - hair->co); -} - -static void rna_HairPoint_location_get(PointerRNA *ptr, float value[3]) -{ - copy_v3_v3(value, (const float *)ptr->data); -} - -static void rna_HairPoint_location_set(PointerRNA *ptr, const float value[3]) -{ - copy_v3_v3((float *)ptr->data, value); -} - -static float rna_HairPoint_radius_get(PointerRNA *ptr) -{ - const Hair *hair = rna_hair(ptr); - if (hair->radius == NULL) { - return 0.0f; - } - const float(*co)[3] = ptr->data; - return hair->radius[co - hair->co]; -} - -static void rna_HairPoint_radius_set(PointerRNA *ptr, float value) -{ - const Hair *hair = rna_hair(ptr); - if (hair->radius == NULL) { - return; - } - const float(*co)[3] = ptr->data; - hair->radius[co - hair->co] = value; -} - -static char *rna_HairPoint_path(PointerRNA *ptr) -{ - return BLI_sprintfN("points[%d]", rna_HairPoint_index_get(ptr)); -} - -static int rna_HairCurve_index_get(PointerRNA *ptr) -{ - Hair *hair = rna_hair(ptr); - return (int)((HairCurve *)ptr->data - hair->curves); -} - -static char *rna_HairCurve_path(PointerRNA *ptr) -{ - return BLI_sprintfN("curves[%d]", rna_HairCurve_index_get(ptr)); -} - -static void rna_HairCurve_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ - Hair *hair = rna_hair(ptr); - HairCurve *curve = ptr->data; - float(*co)[3] = hair->co + curve->firstpoint; - rna_iterator_array_begin(iter, co, sizeof(float[3]), curve->numpoints, 0, NULL); -} - -static int rna_HairCurve_points_length(PointerRNA *ptr) -{ - HairCurve *curve = ptr->data; - return curve->numpoints; -} - -static void rna_Hair_update_data(struct Main *UNUSED(bmain), - struct 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); - } -} - -#else - -static void rna_def_hair_point(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "HairPoint", NULL); - RNA_def_struct_ui_text(srna, "Hair Point", "Hair curve control point"); - RNA_def_struct_path_func(srna, "rna_HairPoint_path"); - - prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION); - RNA_def_property_array(prop, 3); - RNA_def_property_float_funcs( - prop, "rna_HairPoint_location_get", "rna_HairPoint_location_set", NULL); - RNA_def_property_ui_text(prop, "Location", ""); - RNA_def_property_update(prop, 0, "rna_Hair_update_data"); - - prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_funcs(prop, "rna_HairPoint_radius_get", "rna_HairPoint_radius_set", NULL); - RNA_def_property_ui_text(prop, "Radius", ""); - RNA_def_property_update(prop, 0, "rna_Hair_update_data"); - - prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_int_funcs(prop, "rna_HairPoint_index_get", NULL, NULL); - RNA_def_property_ui_text(prop, "Index", "Index of this points"); -} - -static void rna_def_hair_curve(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "HairCurve", NULL); - RNA_def_struct_ui_text(srna, "Hair Curve", "Hair curve"); - RNA_def_struct_path_func(srna, "rna_HairCurve_path"); - - prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_struct_type(prop, "HairPoint"); - RNA_def_property_ui_text(prop, "Points", "Control points of the curve"); - RNA_def_property_collection_funcs(prop, - "rna_HairCurve_points_begin", - "rna_iterator_array_next", - "rna_iterator_array_end", - "rna_iterator_array_get", - "rna_HairCurve_points_length", - NULL, - NULL, - NULL); - - /* TODO: naming consistency, editable? */ - prop = RNA_def_property(srna, "first_point_index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_sdna(prop, NULL, "firstpoint"); - RNA_def_property_ui_text(prop, "First Point Index", "Index of the first loop of this polygon"); - - prop = RNA_def_property(srna, "num_points", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_sdna(prop, NULL, "numpoints"); - RNA_def_property_ui_text(prop, "Number of Points", "Number of loops used by this polygon"); - - prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_int_funcs(prop, "rna_HairCurve_index_get", NULL, NULL); - RNA_def_property_ui_text(prop, "Index", "Index of this curve"); -} - -static void rna_def_hair(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "Hair", "ID"); - RNA_def_struct_ui_text(srna, "Hair", "Hair data-block for hair curves"); - RNA_def_struct_ui_icon(srna, ICON_HAIR_DATA); - - /* geometry */ - prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "curves", "totcurve"); - RNA_def_property_struct_type(prop, "HairCurve"); - RNA_def_property_ui_text(prop, "Curves", "All hair curves"); - - /* TODO: better solution for (*co)[3] parsing issue. */ - RNA_define_verify_sdna(0); - prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "co", "totpoint"); - RNA_def_property_struct_type(prop, "HairPoint"); - RNA_def_property_ui_text(prop, "Points", "Control points of all hair curves"); - RNA_define_verify_sdna(1); - - /* materials */ - prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol"); - RNA_def_property_struct_type(prop, "Material"); - RNA_def_property_ui_text(prop, "Materials", ""); - RNA_def_property_srna(prop, "IDMaterials"); /* see rna_ID.c */ - RNA_def_property_collection_funcs( - prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int"); - - /* attributes */ - rna_def_attributes_common(srna); - - /* common */ - rna_def_animdata_common(srna); -} - -void RNA_def_hair(BlenderRNA *brna) -{ - rna_def_hair_point(brna); - rna_def_hair_curve(brna); - rna_def_hair(brna); -} - -#endif diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 0d86572357f..af13baad5a2 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -1107,7 +1107,7 @@ static void rna_def_image(BlenderRNA *brna) prop, "Duration", "Duration (in frames) of the image (1 when not a video/sequence)"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - /* NOTE about pixels/channels/is_float: + /* NOTE: About pixels/channels/is_float: * These properties describe how the image is stored internally (inside of ImBuf), * not how it was saved to disk or how it'll be saved on disk. */ diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 95ad184c6b9..407f474ddab 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -170,7 +170,7 @@ void RNA_def_fcurve(struct BlenderRNA *brna); void RNA_def_gpencil(struct BlenderRNA *brna); void RNA_def_greasepencil_modifier(struct BlenderRNA *brna); void RNA_def_shader_fx(struct BlenderRNA *brna); -void RNA_def_hair(struct BlenderRNA *brna); +void RNA_def_curves(struct BlenderRNA *brna); void RNA_def_image(struct BlenderRNA *brna); void RNA_def_key(struct BlenderRNA *brna); void RNA_def_light(struct BlenderRNA *brna); @@ -369,6 +369,14 @@ void rna_ViewLayer_active_aov_index_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax); int rna_ViewLayer_active_aov_index_get(PointerRNA *ptr); void rna_ViewLayer_active_aov_index_set(PointerRNA *ptr, int value); +/** + * Set `r_rna_path` with the base view-layer path. + * `rna_path_buffer_size` should be at least `sizeof(ViewLayer.name) * 3`. + * \return actual length of the generated RNA path. + */ +size_t rna_ViewLayer_path_buffer_get(struct ViewLayer *view_layer, + char *r_rna_path, + const size_t rna_path_buffer_size); /* named internal so as not to conflict with obj.update() rna func */ void rna_Object_internal_update_data(struct Main *bmain, @@ -488,12 +496,10 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop); -#ifdef WITH_HAIR_NODES -void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop); +#ifdef WITH_NEW_CURVES_TYPE +void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop); #endif -#ifdef WITH_POINT_CLOUD void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop); -#endif void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop); #ifdef WITH_SIMULATION_DATABLOCK void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop); diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index ab4cbc429ce..278d611cc41 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -53,6 +53,8 @@ # include "BKE_node.h" # include "BKE_scene.h" +# include "NOD_composite.h" + # include "BLI_listbase.h" # include "DEG_depsgraph_build.h" @@ -110,13 +112,24 @@ static void rna_LayerObjects_active_object_set(PointerRNA *ptr, } } +size_t rna_ViewLayer_path_buffer_get(ViewLayer *view_layer, + char *r_rna_path, + const size_t rna_path_buffer_size) +{ + char name_esc[sizeof(view_layer->name) * 2]; + BLI_str_escape(name_esc, view_layer->name, sizeof(name_esc)); + + return BLI_snprintf_rlen(r_rna_path, rna_path_buffer_size, "view_layers[\"%s\"]", name_esc); +} + static char *rna_ViewLayer_path(PointerRNA *ptr) { - ViewLayer *srl = (ViewLayer *)ptr->data; - char name_esc[sizeof(srl->name) * 2]; + ViewLayer *view_layer = (ViewLayer *)ptr->data; + char rna_path[sizeof(view_layer->name) * 3]; + + rna_ViewLayer_path_buffer_get(view_layer, rna_path, sizeof(rna_path)); - BLI_str_escape(name_esc, srl->name, sizeof(name_esc)); - return BLI_sprintfN("view_layers[\"%s\"]", name_esc); + return BLI_strdup(rna_path); } static IDProperty **rna_ViewLayer_idprops(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index a162aa26b78..230c04bfd9c 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -110,8 +110,8 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections) RNA_MAIN_LISTBASE_FUNCS_DEF(curves) RNA_MAIN_LISTBASE_FUNCS_DEF(fonts) RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils) -# ifdef WITH_HAIR_NODES -RNA_MAIN_LISTBASE_FUNCS_DEF(hairs) +# ifdef WITH_NEW_CURVES_TYPE +RNA_MAIN_LISTBASE_FUNCS_DEF(hair_curves) # endif RNA_MAIN_LISTBASE_FUNCS_DEF(images) RNA_MAIN_LISTBASE_FUNCS_DEF(lattices) @@ -129,9 +129,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(objects) RNA_MAIN_LISTBASE_FUNCS_DEF(paintcurves) RNA_MAIN_LISTBASE_FUNCS_DEF(palettes) RNA_MAIN_LISTBASE_FUNCS_DEF(particles) -# ifdef WITH_POINT_CLOUD RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds) -# endif RNA_MAIN_LISTBASE_FUNCS_DEF(scenes) RNA_MAIN_LISTBASE_FUNCS_DEF(screens) RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys) @@ -391,17 +389,24 @@ void RNA_def_main(BlenderRNA *brna) "Light Probes", "Light Probe data-blocks", RNA_def_main_lightprobes}, -# ifdef WITH_HAIR_NODES - {"hairs", "Hair", "rna_Main_hairs_begin", "Hairs", "Hair data-blocks", RNA_def_main_hairs}, +# ifdef WITH_NEW_CURVES_TYPE + /** + * \note The name `hair_curves` is chosen to be different than `curves`, + * but they are generic curve data-blocks, not just for hair. + */ + {"hair_curves", + "Curves", + "rna_Main_hair_curves_begin", + "Hair Curves", + "Hair curve data-blocks", + RNA_def_main_hair_curves}, # endif -# ifdef WITH_POINT_CLOUD {"pointclouds", "PointCloud", "rna_Main_pointclouds_begin", "Point Clouds", "Point cloud data-blocks", RNA_def_main_pointclouds}, -# endif {"volumes", "Volume", "rna_Main_volumes_begin", diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 0276c8a3f8a..f8d2d6524c2 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -46,9 +46,9 @@ # include "BKE_camera.h" # include "BKE_collection.h" # include "BKE_curve.h" +# include "BKE_curves.h" # include "BKE_displist.h" # include "BKE_gpencil.h" -# include "BKE_hair.h" # include "BKE_icons.h" # include "BKE_idtype.h" # include "BKE_image.h" @@ -86,8 +86,8 @@ # include "DNA_camera_types.h" # include "DNA_collection_types.h" # include "DNA_curve_types.h" +# include "DNA_curves_types.h" # include "DNA_gpencil_types.h" -# include "DNA_hair_types.h" # include "DNA_lattice_types.h" # include "DNA_light_types.h" # include "DNA_lightprobe_types.h" @@ -763,22 +763,21 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name) return gpd; } -# ifdef WITH_HAIR_NODES -static Hair *rna_Main_hairs_new(Main *bmain, const char *name) +# ifdef WITH_NEW_CURVES_TYPE +static Curves *rna_Main_hair_curves_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; rna_idname_validate(name, safe_name); - Hair *hair = BKE_hair_add(bmain, safe_name); - id_us_min(&hair->id); + Curves *curves = BKE_curves_add(bmain, safe_name); + id_us_min(&curves->id); WM_main_add_notifier(NC_ID | NA_ADDED, NULL); - return hair; + return curves; } # endif -# ifdef WITH_POINT_CLOUD static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; @@ -791,7 +790,6 @@ static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name) return pointcloud; } -# endif static Volume *rna_Main_volumes_new(Main *bmain, const char *name) { @@ -863,12 +861,10 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF) RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC) RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS) RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP) -# ifdef WITH_HAIR_NODES -RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA) +# ifdef WITH_NEW_CURVES_TYPE +RNA_MAIN_ID_TAG_FUNCS_DEF(hair_curves, hair_curves, ID_CV) # endif -# ifdef WITH_POINT_CLOUD RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT) -# endif RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO) # ifdef WITH_SIMULATION_DATABLOCK RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM) @@ -2273,53 +2269,52 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } -# ifdef WITH_HAIR_NODES -void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop) +# ifdef WITH_NEW_CURVES_TYPE +void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; FunctionRNA *func; PropertyRNA *parm; - RNA_def_property_srna(cprop, "BlendDataHairs"); - srna = RNA_def_struct(brna, "BlendDataHairs", NULL); + RNA_def_property_srna(cprop, "BlendDataHairCurves"); + srna = RNA_def_struct(brna, "BlendDataHairCurves", NULL); RNA_def_struct_sdna(srna, "Main"); - RNA_def_struct_ui_text(srna, "Main Hairs", "Collection of hairs"); + RNA_def_struct_ui_text(srna, "Main Hair Curves", "Collection of hair curves"); - func = RNA_def_function(srna, "new", "rna_Main_hairs_new"); + func = RNA_def_function(srna, "new", "rna_Main_hair_curves_new"); RNA_def_function_ui_description(func, "Add a new hair to the main database"); - parm = RNA_def_string(func, "name", "Hair", 0, "", "New name for the data-block"); + parm = RNA_def_string(func, "name", "Curves", 0, "", "New name for the data-block"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); /* return type */ - parm = RNA_def_pointer(func, "hair", "Hair", "", "New hair data-block"); + parm = RNA_def_pointer(func, "curves", "Curves", "", "New curves data-block"); RNA_def_function_return(func, parm); func = RNA_def_function(srna, "remove", "rna_Main_ID_remove"); RNA_def_function_flag(func, FUNC_USE_REPORTS); - RNA_def_function_ui_description(func, "Remove a hair from the current blendfile"); - parm = RNA_def_pointer(func, "hair", "Hair", "", "Hair to remove"); + RNA_def_function_ui_description(func, "Remove a curves data-block from the current blendfile"); + parm = RNA_def_pointer(func, "curves", "Curves", "", "Curves data-block to remove"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); RNA_def_boolean(func, "do_unlink", true, "", - "Unlink all usages of this hair before deleting it " - "(WARNING: will also delete objects instancing that hair data)"); + "Unlink all usages of this curves before deleting it " + "(WARNING: will also delete objects instancing that curves data)"); RNA_def_boolean(func, "do_id_user", true, "", - "Decrement user counter of all datablocks used by this hair data"); + "Decrement user counter of all datablocks used by this curves data"); RNA_def_boolean( - func, "do_ui_user", true, "", "Make sure interface does not reference this hair data"); + func, "do_ui_user", true, "", "Make sure interface does not reference this curves data"); - func = RNA_def_function(srna, "tag", "rna_Main_hairs_tag"); + func = RNA_def_function(srna, "tag", "rna_Main_hair_curves_tag"); parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } # endif -# ifdef WITH_POINT_CLOUD void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -2366,7 +2361,6 @@ void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } -# endif void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop) { diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 22a75c0d992..67943b290da 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -718,7 +718,7 @@ void RNA_def_material(BlenderRNA *brna) {MA_FLAT, "FLAT", ICON_MATPLANE, "Flat", "Flat XY plane"}, {MA_SPHERE, "SPHERE", ICON_MATSPHERE, "Sphere", "Sphere"}, {MA_CUBE, "CUBE", ICON_MATCUBE, "Cube", "Cube"}, - {MA_HAIR, "HAIR", ICON_HAIR, "Hair", "Hair strands"}, + {MA_HAIR, "HAIR", ICON_CURVES, "Hair", "Hair strands"}, {MA_SHADERBALL, "SHADERBALL", ICON_MATSHADERBALL, "Shader Ball", "Shader ball"}, {MA_CLOTH, "CLOTH", ICON_MATCLOTH, "Cloth", "Cloth"}, {MA_FLUID, "FLUID", ICON_MATFLUID, "Fluid", "Fluid"}, diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index b0acd30dc04..55b70fd1b41 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -3120,6 +3120,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "vertex_normals", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshNormalValue"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); RNA_def_property_ui_text(prop, "Vertex Normals", "The normal direction of each vertex, defined as the average of the " @@ -3136,6 +3137,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "polygon_normals", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshNormalValue"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); RNA_def_property_ui_text(prop, "Polygon Normals", "The normal direction of each polygon, defined by the winding order " @@ -3246,6 +3248,7 @@ static void rna_def_mesh(BlenderRNA *brna) NULL, NULL); RNA_def_property_struct_type(prop, "MeshVertColorLayer"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "All vertex colors"); rna_def_vert_colors(brna, prop); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 4192a9975be..13c8444de1d 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -300,9 +300,33 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = { }; const EnumPropertyItem rna_enum_node_boolean_math_items[] = { - {NODE_BOOLEAN_MATH_AND, "AND", 0, "And", "Outputs true only when both inputs are true"}, - {NODE_BOOLEAN_MATH_OR, "OR", 0, "Or", "Outputs or when at least one of the inputs is true"}, - {NODE_BOOLEAN_MATH_NOT, "NOT", 0, "Not", "Outputs the opposite of the input"}, + {NODE_BOOLEAN_MATH_AND, "AND", 0, "And", "True when both inputs are true"}, + {NODE_BOOLEAN_MATH_OR, "OR", 0, "Or", "True when at least one input is true"}, + {NODE_BOOLEAN_MATH_NOT, "NOT", 0, "Not", "Opposite of the input"}, + {0, "", ICON_NONE, NULL, NULL}, + {NODE_BOOLEAN_MATH_NAND, "NAND", 0, "Not And", "True when at least one input is false"}, + {NODE_BOOLEAN_MATH_NOR, "NOR", 0, "Nor", "True when both inputs are false"}, + {NODE_BOOLEAN_MATH_XNOR, + "XNOR", + 0, + "Equal", + "True when both inputs are equal (exclusive nor)"}, + {NODE_BOOLEAN_MATH_XOR, + "XOR", + 0, + "Not Equal", + "True when both inputs are different (exclusive or)"}, + {0, "", ICON_NONE, NULL, NULL}, + {NODE_BOOLEAN_MATH_IMPLY, + "IMPLY", + 0, + "Imply", + "True unless the first input is true and the second is false"}, + {NODE_BOOLEAN_MATH_NIMPLY, + "NIMPLY", + 0, + "Subtract", + "True when the first input is true and the second is false (not imply)"}, {0, NULL, 0, NULL, NULL}, }; @@ -631,6 +655,7 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_bo # include "NOD_geometry.h" # include "NOD_shader.h" # include "NOD_socket.h" +# include "NOD_texture.h" # include "RE_engine.h" # include "RE_pipeline.h" @@ -10499,7 +10524,7 @@ static void def_geo_object_info(StructRNA *srna) RNA_def_property_enum_items(prop, rna_node_geometry_object_info_transform_space_items); RNA_def_property_ui_text( prop, "Transform Space", "The transformation of the vector and geometry outputs"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations"); } static void def_geo_legacy_points_to_volume(StructRNA *srna) @@ -10583,7 +10608,7 @@ static void def_geo_collection_info(StructRNA *srna) prop = RNA_def_property(srna, "transform_space", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_node_geometry_collection_info_transform_space_items); RNA_def_property_ui_text(prop, "Transform Space", "The transformation of the geometry output"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations"); } static void def_geo_legacy_attribute_proximity(StructRNA *srna) @@ -11353,6 +11378,33 @@ static void def_geo_string_to_curves(StructRNA *srna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem rna_node_geometry_string_to_curves_pivot_mode[] = { + {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_MIDPOINT, "MIDPOINT", 0, "Midpoint", "Midpoint"}, + {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_LEFT, "TOP_LEFT", 0, "Top Left", "Top Left"}, + {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_CENTER, + "TOP_CENTER", + 0, + "Top Center", + "Top Center"}, + {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_RIGHT, "TOP_RIGHT", 0, "Top Right", "Top Right"}, + {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT, + "BOTTOM_LEFT", + 0, + "Bottom Left", + "Bottom Left"}, + {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_CENTER, + "BOTTOM_CENTER", + 0, + "Bottom Center", + "Bottom Center"}, + {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_RIGHT, + "BOTTOM_RIGHT", + 0, + "Bottom Right", + "Bottom Right"}, + {0, NULL, 0, NULL, NULL}, + }; + PropertyRNA *prop; prop = RNA_def_property(srna, "font", PROP_POINTER, PROP_NONE); @@ -11385,6 +11437,13 @@ static void def_geo_string_to_curves(StructRNA *srna) RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE); RNA_def_property_ui_text(prop, "Align Y", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "pivot_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "pivot_mode"); + RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_pivot_mode); + RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT); + RNA_def_property_ui_text(prop, "Pivot Point", "Pivot point position relative to character"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } static void def_geo_separate_geometry(StructRNA *srna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 0cb132786cd..be37e574c9c 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -271,7 +271,7 @@ const EnumPropertyItem rna_enum_object_type_items[] = { OBTYPE_CU_SURF, {OB_MBALL, "META", ICON_OUTLINER_OB_META, "Metaball", ""}, OBTYPE_CU_FONT, - {OB_HAIR, "HAIR", ICON_OUTLINER_OB_HAIR, "Hair", ""}, + {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Hair Curves", ""}, {OB_POINTCLOUD, "POINTCLOUD", ICON_OUTLINER_OB_POINTCLOUD, "Point Cloud", ""}, {OB_VOLUME, "VOLUME", ICON_OUTLINER_OB_VOLUME, "Volume", ""}, {OB_GPENCIL, "GPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil", ""}, @@ -613,18 +613,14 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) return &RNA_LightProbe; case OB_GPENCIL: return &RNA_GreasePencil; - case OB_HAIR: -# ifdef WITH_HAIR_NODES - return &RNA_Hair; + case OB_CURVES: +# ifdef WITH_NEW_CURVES_TYPE + return &RNA_Curves; # else return &RNA_ID; # endif case OB_POINTCLOUD: -# ifdef WITH_POINT_CLOUD return &RNA_PointCloud; -# else - return &RNA_ID; -# endif case OB_VOLUME: return &RNA_Volume; default: @@ -3154,19 +3150,6 @@ static void rna_def_object(BlenderRNA *brna) "Align to Vertex Normal is enabled)"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update"); - /* proxy */ - prop = RNA_def_property(srna, "proxy", PROP_POINTER, PROP_NONE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); - RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_ui_text(prop, "Proxy", "Library object this proxy object controls"); - - prop = RNA_def_property(srna, "proxy_collection", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "proxy_group"); - RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); - RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_ui_text( - prop, "Proxy Collection", "Library collection duplicator object this proxy object controls"); - /* materials */ prop = RNA_def_property(srna, "material_slots", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MaterialSlot"); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index ec20fa54a44..dbf9b757728 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -68,6 +68,7 @@ static const EnumPropertyItem space_items[] = { # include "BKE_bvhutils.h" # include "BKE_constraint.h" # include "BKE_context.h" +# include "BKE_crazyspace.h" # include "BKE_customdata.h" # include "BKE_global.h" # include "BKE_layer.h" @@ -379,6 +380,39 @@ static void rna_Object_camera_fit_coords( depsgraph, (const float(*)[3])cos, num_cos / 3, ob, co_ret, scale_ret); } +static void rna_Object_crazyspace_eval(Object *object, + ReportList *reports, + Depsgraph *depsgraph, + Scene *scene) +{ + BKE_crazyspace_api_eval(depsgraph, scene, object, reports); +} + +static void rna_Object_crazyspace_displacement_to_deformed(Object *object, + ReportList *reports, + const int vertex_index, + float displacement[3], + float r_displacement_deformed[3]) +{ + BKE_crazyspace_api_displacement_to_deformed( + object, reports, vertex_index, displacement, r_displacement_deformed); +} + +static void rna_Object_crazyspace_displacement_to_original(Object *object, + ReportList *reports, + const int vertex_index, + float displacement_deformed[3], + float r_displacement[3]) +{ + BKE_crazyspace_api_displacement_to_original( + object, reports, vertex_index, displacement_deformed, r_displacement); +} + +static void rna_Object_crazyspace_eval_clear(Object *object) +{ + BKE_crazyspace_api_eval_clear(object); +} + /* copied from Mesh_getFromObject and adapted to RNA interface */ static Mesh *rna_Object_to_mesh(Object *object, ReportList *reports, @@ -978,6 +1012,52 @@ void RNA_api_object(StructRNA *srna) parm, "", "The ortho scale to aim to be able to see all given points (if relevant)"); RNA_def_parameter_flags(parm, 0, PARM_OUTPUT); + /* Crazy-space access. */ + + func = RNA_def_function(srna, "crazyspace_eval", "rna_Object_crazyspace_eval"); + RNA_def_function_ui_description( + func, + "Compute orientation mapping between vertices of an original object and object with shape " + "keys and deforming modifiers applied." + "The evaluation is to be freed with the crazyspace_eval_free function"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer( + func, "depsgraph", "Depsgraph", "Dependency Graph", "Evaluated dependency graph"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "scene", "Scene", "Scene", "Scene of the object"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + func = RNA_def_function(srna, + "crazyspace_displacement_to_deformed", + "rna_Object_crazyspace_displacement_to_deformed"); + RNA_def_function_ui_description( + func, "Convert displacement vector from non-deformed object space to deformed object space"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_property(func, "vertex_index", PROP_INT, PROP_NONE); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_property(func, "displacement", PROP_FLOAT, PROP_XYZ); + RNA_def_property_array(parm, 3); + parm = RNA_def_property(func, "displacement_deformed", PROP_FLOAT, PROP_XYZ); + RNA_def_property_array(parm, 3); + RNA_def_function_output(func, parm); + + func = RNA_def_function(srna, + "crazyspace_displacement_to_original", + "rna_Object_crazyspace_displacement_to_original"); + RNA_def_function_ui_description( + func, "Convert displacement vector from deformed object space to non-deformed object space"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_property(func, "vertex_index", PROP_INT, PROP_NONE); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_property(func, "displacement", PROP_FLOAT, PROP_XYZ); + RNA_def_property_array(parm, 3); + parm = RNA_def_property(func, "displacement_original", PROP_FLOAT, PROP_XYZ); + RNA_def_property_array(parm, 3); + RNA_def_function_output(func, parm); + + RNA_def_function(srna, "crazyspace_eval_clear", "rna_Object_crazyspace_eval_clear"); + RNA_def_function_ui_description(func, "Free evaluated state of crazyspace"); + /* mesh */ func = RNA_def_function(srna, "to_mesh", "rna_Object_to_mesh"); RNA_def_function_ui_description( diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index fbc7625d815..23ec5148f00 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -249,7 +249,8 @@ static void rna_ParticleHairKey_location_object_get(PointerRNA *ptr, float *valu * * Such trickery is needed to allow modification of hair keys in the original object using * evaluated particle and object to access proper hair matrix. */ -static int hair_key_index_get(/*const*/ HairKey *hair_key, +static int hair_key_index_get(const Object *object, + /*const*/ HairKey *hair_key, /*const*/ ParticleSystemModifierData *modifier, /*const*/ ParticleData *particle) { @@ -261,7 +262,7 @@ static int hair_key_index_get(/*const*/ HairKey *hair_key, const int particle_index = particle - particle_system->particles; const ParticleSystemModifierData *original_modifier = (ParticleSystemModifierData *) - BKE_modifier_get_original(&modifier->modifier); + BKE_modifier_get_original(object, &modifier->modifier); const ParticleSystem *original_particle_system = original_modifier->psys; const ParticleData *original_particle = &original_particle_system->particles[particle_index]; @@ -288,7 +289,7 @@ static void hair_key_location_object_set(HairKey *hair_key, NULL; if (hair_mesh != NULL) { - const int hair_key_index = hair_key_index_get(hair_key, modifier, particle); + const int hair_key_index = hair_key_index_get(object, hair_key, modifier, particle); if (hair_key_index == -1) { return; } @@ -368,7 +369,7 @@ static void rna_ParticleHairKey_co_object_set(ID *id, /* Mark particle system as edited, so then particle_system_update() does not reset the hair * keys from path. This behavior is similar to how particle edit mode sets flags. */ ParticleSystemModifierData *orig_modifier = (ParticleSystemModifierData *) - modifier->modifier.orig_modifier_data; + BKE_modifier_get_original(object, &modifier->modifier); orig_modifier->psys->flag |= PSYS_EDITED; hair_key_location_object_set(hair_key, object, modifier, particle, co); diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 76bfea00a79..7714e4d1e59 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -140,7 +140,7 @@ static char *rna_PoseBone_path(PointerRNA *ptr) static bool rna_bone_group_poll(Object *ob, ReportList *reports) { - if ((ob->proxy != NULL) || (ob->proxy_group != NULL) || ID_IS_OVERRIDE_LIBRARY(ob)) { + if (ID_IS_OVERRIDE_LIBRARY(ob)) { BKE_report(reports, RPT_ERROR, "Cannot edit bone groups for proxies or library overrides"); return false; } @@ -717,7 +717,7 @@ static int rna_PoseChannel_proxy_editable(PointerRNA *ptr, const char **r_info) bArmature *arm = ob->data; bPoseChannel *pchan = (bPoseChannel *)ptr->data; - if (ob->proxy && pchan->bone && (pchan->bone->layer & arm->layer_protected)) { + if (false && pchan->bone && (pchan->bone->layer & arm->layer_protected)) { *r_info = "Can't edit property of a proxy on a protected layer"; return 0; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index d0e40af582c..3802f5ceaed 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -663,6 +663,8 @@ const EnumPropertyItem rna_enum_transform_orientation_items[] = { # include "BKE_screen.h" # include "BKE_unit.h" +# include "NOD_composite.h" + # include "ED_image.h" # include "ED_info.h" # include "ED_keyframing.h" @@ -1034,7 +1036,7 @@ static void rna_Scene_frame_update(Main *UNUSED(bmain), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_main_add_notifier(NC_SCENE | ND_FRAME, scene); } @@ -1775,6 +1777,20 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr) rna_Scene_glsl_update(bmain, activescene, ptr); } +static char *rna_ViewLayerEEVEE_path(PointerRNA *ptr) +{ + ViewLayerEEVEE *view_layer_eevee = (ViewLayerEEVEE *)ptr->data; + ViewLayer *view_layer = (ViewLayer *)((uint8_t *)view_layer_eevee - offsetof(ViewLayer, eevee)); + char rna_path[sizeof(view_layer->name) * 3]; + + const size_t view_layer_path_len = rna_ViewLayer_path_buffer_get( + view_layer, rna_path, sizeof(rna_path)); + + BLI_strncpy(rna_path + view_layer_path_len, ".eevee", sizeof(rna_path) - view_layer_path_len); + + return BLI_strdup(rna_path); +} + static char *rna_SceneRenderView_path(PointerRNA *ptr) { SceneRenderView *srv = (SceneRenderView *)ptr->data; @@ -4025,6 +4041,7 @@ static void rna_def_view_layer_eevee(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; srna = RNA_def_struct(brna, "ViewLayerEEVEE", NULL); + RNA_def_struct_path_func(srna, "rna_ViewLayerEEVEE_path"); RNA_def_struct_ui_text(srna, "Eevee Settings", "View layer settings for Eevee"); prop = RNA_def_property(srna, "use_pass_volume_direct", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 6c3e3ab3058..9fee54ef38d 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1456,6 +1456,12 @@ static void rna_def_strip_crop(BlenderRNA *brna) RNA_def_struct_path_func(srna, "rna_SequenceCrop_path"); } +static const EnumPropertyItem transform_filter_items[] = { + {SEQ_TRANSFORM_FILTER_NEAREST, "NEAREST", 0, "Nearest", ""}, + {SEQ_TRANSFORM_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", ""}, + {0, NULL, 0, NULL, NULL}, +}; + static void rna_def_strip_transform(BlenderRNA *brna) { StructRNA *srna; @@ -1502,6 +1508,13 @@ static void rna_def_strip_transform(BlenderRNA *brna) RNA_def_property_ui_range(prop, 0, 1, 1, 3); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update"); + prop = RNA_def_property(srna, "filter", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "filter"); + RNA_def_property_enum_items(prop, transform_filter_items); + RNA_def_property_enum_default(prop, SEQ_TRANSFORM_FILTER_BILINEAR); + RNA_def_property_ui_text(prop, "Filter", "Type of filter to use for image transformation"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update"); + RNA_def_struct_path_func(srna, "rna_SequenceTransform_path"); } diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 7989c316c4c..e2e2bc2d5e8 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -328,8 +328,7 @@ static Sequence *rna_Sequences_new_movie(ID *id, SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.fit_method = fit_method; load_data.allow_invalid_file = true; - double start_offset = -1; - Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &start_offset); + Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -378,7 +377,7 @@ static Sequence *rna_Sequences_new_sound(ID *id, SeqLoadData load_data; SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.allow_invalid_file = true; - Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data, 0.0f); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data); if (seq == NULL) { BKE_report(reports, RPT_ERROR, "Sequences.new_sound: unable to open sound file"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 05c1a645823..07521d39256 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1861,6 +1861,9 @@ static void rna_SpaceTextEditor_text_set(PointerRNA *ptr, SpaceText *st = (SpaceText *)(ptr->data); st->text = value.data; + if (st->text != NULL) { + id_us_ensure_real((ID *)st->text); + } ScrArea *area = rna_area_from_space(ptr); if (area) { @@ -2337,7 +2340,8 @@ static void seq_build_proxy(bContext *C, PointerRNA *ptr) seq->strip->proxy->build_size_flags |= SEQ_rendersize_to_proxysize(sseq->render_size); /* Build proxy. */ - SEQ_proxy_rebuild_context(pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue); + SEQ_proxy_rebuild_context( + pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue, true); } BLI_gset_free(file_list, MEM_freeN); @@ -3273,7 +3277,7 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[ ICON_OUTLINER_COLLECTION, "Objects & Collections", "Show objects and collections"}, - {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_HA | + {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO, "category_geometry", ICON_NODETREE, @@ -4304,7 +4308,7 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "bone_wire_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.bone_wire_alpha"); RNA_def_property_ui_text( - prop, "Bone Wireframe Opacity", "Maximim opacity of bones in wireframe display mode"); + prop, "Bone Wireframe Opacity", "Maximum opacity of bones in wireframe display mode"); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 2); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -5000,7 +5004,7 @@ static void rna_def_space_view3d(BlenderRNA *brna) {"Surface", (1 << OB_SURF), {"show_object_viewport_surf", "show_object_select_surf"}}, {"Meta", (1 << OB_MBALL), {"show_object_viewport_meta", "show_object_select_meta"}}, {"Font", (1 << OB_FONT), {"show_object_viewport_font", "show_object_select_font"}}, - {"Hair", (1 << OB_HAIR), {"show_object_viewport_hair", "show_object_select_hair"}}, + {"Hair", (1 << OB_CURVES), {"show_object_viewport_hair", "show_object_select_hair"}}, {"Point Cloud", (1 << OB_POINTCLOUD), {"show_object_viewport_pointcloud", "show_object_select_pointcloud"}}, diff --git a/source/blender/makesrna/intern/rna_texture_api.c b/source/blender/makesrna/intern/rna_texture_api.c index 0920fe6679a..2a4cdaebcee 100644 --- a/source/blender/makesrna/intern/rna_texture_api.c +++ b/source/blender/makesrna/intern/rna_texture_api.c @@ -32,6 +32,7 @@ # include "BKE_context.h" # include "BKE_global.h" +# include "BLI_math.h" # include "DNA_scene_types.h" # include "IMB_imbuf.h" # include "IMB_imbuf_types.h" @@ -40,14 +41,12 @@ static void texture_evaluate(struct Tex *tex, float value[3], float r_color[4]) { - TexResult texres = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL}; + TexResult texres = {0.0f}; /* TODO(sergey): always use color management now. */ multitex_ext(tex, value, NULL, NULL, 1, &texres, 0, NULL, true, false); - r_color[0] = texres.tr; - r_color[1] = texres.tg; - r_color[2] = texres.tb; + copy_v3_v3(r_color, texres.trgba); r_color[3] = texres.tin; } diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 4379b4ebe1d..53af3f5bed5 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -5293,7 +5293,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna) prop, "Duplicate GPencil", "Causes grease pencil data to be duplicated with the object"); prop = RNA_def_property(srna, "use_duplicate_hair", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_HAIR); + RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_CURVES); RNA_def_property_ui_text( prop, "Duplicate Hair", "Causes hair data to be duplicated with the object"); @@ -6386,13 +6386,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) "Enable library overrides automatic resync detection and process on file load. Disable when " "dealing with older .blend files that need manual Resync (Enforce) handling"); - prop = RNA_def_property(srna, "proxy_to_override_auto_conversion", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "no_proxy_to_override_conversion", 1); - RNA_def_property_ui_text( - prop, - "Proxy to Override Auto Conversion", - "Enable automatic conversion of proxies to library overrides on file load"); - prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1); RNA_def_property_ui_text( @@ -6406,9 +6399,9 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) "reduces execution time and memory usage)"); RNA_def_property_update(prop, 0, "rna_userdef_update"); - prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1); - RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui"); + prop = RNA_def_property(srna, "use_new_curves_type", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_new_curves_type", 1); + RNA_def_property_ui_text(prop, "New Curves Type", "Enable the new curves data type in the UI"); prop = RNA_def_property(srna, "use_cycles_debug", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_cycles_debug", 1); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index c64a47fc2ab..178a50c4d29 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -2175,7 +2175,8 @@ static void rna_def_event(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Mouse Previous Y Position", "The window relative vertical location of the mouse"); - prop = RNA_def_property(srna, "pressure", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "pressure", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_default(prop, 1.0f); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_float_funcs(prop, "rna_Event_pressure_get", NULL, NULL); RNA_def_property_ui_text( diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index e5c94c36a13..719a07adbab 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -183,10 +183,6 @@ if(WITH_BULLET) add_definitions(-DWITH_BULLET) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - # To disable adaptive subdivision test in subsurf UI without cycles if(WITH_CYCLES) add_definitions(-DWITH_CYCLES) @@ -236,8 +232,7 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) - add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_HAIR_NODES) + add_definitions(-DWITH_NEW_CURVES_TYPE) endif() # So we can have special tricks in modifier system. diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 828b8b79664..a86a667974e 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -296,7 +296,6 @@ ModifierTypeInfo modifierType_Armature = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ deformMatricesEM, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 56db68b163c..c2b1478c1b2 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -1037,7 +1037,6 @@ ModifierTypeInfo modifierType_Array = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index add95a0d248..984e85f58ef 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -446,7 +446,6 @@ ModifierTypeInfo modifierType_Bevel = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index bb05ae3e1b3..7b084d608fb 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -641,7 +641,6 @@ ModifierTypeInfo modifierType_Boolean = { /* deformVertsEM */ nullptr, /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, - /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 86f0df1418b..867c1e9b5bb 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -345,7 +345,6 @@ ModifierTypeInfo modifierType_Build = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 715bc26e5d3..defc7df31dc 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -589,7 +589,6 @@ ModifierTypeInfo modifierType_Cast = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 8aff29dc17d..4b8928009fe 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -308,7 +308,6 @@ ModifierTypeInfo modifierType_Cloth = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 02e1f61b824..658a569627b 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -317,7 +317,6 @@ ModifierTypeInfo modifierType_Collision = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 49ff4acb31f..d75c2a13587 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -610,7 +610,7 @@ static void correctivesmooth_modifier_do(ModifierData *md, BLI_assert(csmd->bind_coords != NULL); /* Copy bound data to the original modifier. */ CorrectiveSmoothModifierData *csmd_orig = (CorrectiveSmoothModifierData *) - BKE_modifier_get_original(&csmd->modifier); + BKE_modifier_get_original(ob, &csmd->modifier); csmd_orig->bind_coords = MEM_dupallocN(csmd->bind_coords); csmd_orig->bind_coords_num = csmd->bind_coords_num; } @@ -850,7 +850,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index 20dbb299767..b01b70000b8 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -234,7 +234,6 @@ ModifierTypeInfo modifierType_Curve = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index 34bb93cbbbc..a289b9c918a 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -484,7 +484,6 @@ ModifierTypeInfo modifierType_DataTransfer = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 975f80a04f8..ad5391d2b6c 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -298,7 +298,6 @@ ModifierTypeInfo modifierType_Decimate = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 010292d2ebb..94cdcad3b6a 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -262,9 +262,9 @@ static void displaceModifier_do_task(void *__restrict userdata, } break; case MOD_DISP_DIR_RGB_XYZ: - local_vec[0] = texres.tr - dmd->midlevel; - local_vec[1] = texres.tg - dmd->midlevel; - local_vec[2] = texres.tb - dmd->midlevel; + local_vec[0] = texres.trgba[0] - dmd->midlevel; + local_vec[1] = texres.trgba[1] - dmd->midlevel; + local_vec[2] = texres.trgba[2] - dmd->midlevel; if (use_global_direction) { mul_transposed_mat3_m4_v3(data->local_mat, local_vec); } @@ -508,7 +508,6 @@ ModifierTypeInfo modifierType_Displace = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index a696ce216c7..676433b14b1 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_DynamicPaint = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 1039bcb2b3b..55707435e52 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -185,7 +185,6 @@ ModifierTypeInfo modifierType_EdgeSplit = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index 68d6b4a3626..c788633f978 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -1255,7 +1255,6 @@ ModifierTypeInfo modifierType_Explode = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index a21eb603300..748633b78b3 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -273,7 +273,6 @@ ModifierTypeInfo modifierType_Fluid = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 18ce37c5d85..b4c081906ba 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -572,7 +572,6 @@ ModifierTypeInfo modifierType_Hook = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index 6efeec1970f..090973f12c0 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -887,7 +887,6 @@ ModifierTypeInfo modifierType_LaplacianDeform = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index a36a8c386b4..8cc34d43d82 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -633,7 +633,6 @@ ModifierTypeInfo modifierType_LaplacianSmooth = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ init_data, diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 29d1ecf6050..709b617d3ec 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -191,7 +191,6 @@ ModifierTypeInfo modifierType_Lattice = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 0de8b26a1b7..097f7241205 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -837,7 +837,6 @@ ModifierTypeInfo modifierType_Mask = { /* deformVertsEM */ nullptr, /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, - /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index 910b52dea67..a8e1c91dd20 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -308,7 +308,6 @@ ModifierTypeInfo modifierType_MeshToVolume = { /* deformVertsEM */ nullptr, /* deformMatricesEM */ nullptr, /* modifyMesh */ nullptr, - /* modifyHair */ nullptr, /* modifyGeometrySet */ modifyGeometrySet, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 74f9887a973..48ff4f7d6af 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -446,7 +446,6 @@ ModifierTypeInfo modifierType_MeshCache = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index cb043643dd9..5c6bcb4ba24 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -390,7 +390,7 @@ static void meshdeformModifier_do(ModifierData *md, } if (!recursive_bind_sentinel) { recursive_bind_sentinel = 1; - mmd->bindfunc(mmd, cagemesh, (float *)vertexCos, numVerts, cagemat); + mmd->bindfunc(ob, mmd, cagemesh, (float *)vertexCos, numVerts, cagemat); recursive_bind_sentinel = 0; } @@ -642,7 +642,6 @@ ModifierTypeInfo modifierType_MeshDeform = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index e1459ccba6d..8bde0cab3dc 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -404,6 +404,10 @@ static void override_layers_panel_draw(const bContext *C, Panel *panel) return; } + if (RNA_pointer_is_null(&fileptr)) { + return; + } + uiLayoutSetPropSep(layout, true); uiTemplateCacheFileLayers(layout, C, &fileptr); } @@ -452,7 +456,6 @@ ModifierTypeInfo modifierType_MeshSequenceCache = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index bbac6589577..721906a6a01 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -120,9 +120,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = mirrorModifier__doMirror(mmd, ctx->object, mesh); - if (result != mesh) { - BKE_mesh_normals_tag_dirty(result); - } return result; } @@ -241,7 +238,6 @@ ModifierTypeInfo modifierType_Mirror = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c index c3b34f3cd23..205839774d8 100644 --- a/source/blender/modifiers/intern/MOD_multires.c +++ b/source/blender/modifiers/intern/MOD_multires.c @@ -518,7 +518,6 @@ ModifierTypeInfo modifierType_Multires = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 49528845197..ca4bfbed5f5 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -109,6 +109,8 @@ using blender::float3; using blender::FunctionRef; using blender::IndexRange; using blender::Map; +using blender::MultiValueMap; +using blender::MutableSpan; using blender::Set; using blender::Span; using blender::StringRef; @@ -119,7 +121,9 @@ using blender::fn::Field; using blender::fn::GField; using blender::fn::GMutablePointer; using blender::fn::GPointer; +using blender::fn::GVArray; using blender::fn::ValueOrField; +using blender::fn::ValueOrFieldCPPType; using blender::nodes::FieldInferencingInterface; using blender::nodes::GeoNodeExecParams; using blender::nodes::InputSocketFieldType; @@ -137,56 +141,85 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(nmd, DNA_struct_default_get(NodesModifierData), modifier); } -static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids) +static void add_used_ids_from_sockets(const ListBase &sockets, Set<ID *> &ids) { - LISTBASE_FOREACH (const bNodeSocket *, socket, sockets) { - if (socket->type == SOCK_OBJECT) { - Object *object = ((bNodeSocketValueObject *)socket->default_value)->value; - if (object != nullptr) { - ids.add(&object->id); + LISTBASE_FOREACH (const bNodeSocket *, socket, &sockets) { + switch (socket->type) { + case SOCK_OBJECT: { + if (Object *object = ((bNodeSocketValueObject *)socket->default_value)->value) { + ids.add(&object->id); + } + break; } - } - else if (socket->type == SOCK_COLLECTION) { - Collection *collection = ((bNodeSocketValueCollection *)socket->default_value)->value; - if (collection != nullptr) { - ids.add(&collection->id); + case SOCK_COLLECTION: { + if (Collection *collection = + ((bNodeSocketValueCollection *)socket->default_value)->value) { + ids.add(&collection->id); + } + break; } - } - else if (socket->type == SOCK_MATERIAL) { - Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value; - if (material != nullptr) { - ids.add(&material->id); + case SOCK_MATERIAL: { + if (Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value) { + ids.add(&material->id); + } + break; } - } - else if (socket->type == SOCK_TEXTURE) { - Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value; - if (texture != nullptr) { - ids.add(&texture->id); + case SOCK_TEXTURE: { + if (Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value) { + ids.add(&texture->id); + } + break; } - } - else if (socket->type == SOCK_IMAGE) { - Image *image = ((bNodeSocketValueImage *)socket->default_value)->value; - if (image != nullptr) { - ids.add(&image->id); + case SOCK_IMAGE: { + if (Image *image = ((bNodeSocketValueImage *)socket->default_value)->value) { + ids.add(&image->id); + } + break; } } } } -static void find_used_ids_from_nodes(const bNodeTree &tree, Set<ID *> &ids) +/** + * \note We can only check properties here that cause the dependency graph to update relations when + * they are changed, otherwise there may be a missing relation after editing. So this could check + * more properties like whether the node is muted, but we would have to accept the cost of updating + * relations when those properties are changed. + */ +static bool node_needs_own_transform_relation(const bNode &node) +{ + if (node.type == GEO_NODE_COLLECTION_INFO) { + const NodeGeometryCollectionInfo &storage = *static_cast<const NodeGeometryCollectionInfo *>( + node.storage); + return storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE; + } + + if (node.type == GEO_NODE_OBJECT_INFO) { + const NodeGeometryObjectInfo &storage = *static_cast<const NodeGeometryObjectInfo *>( + node.storage); + return storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE; + } + + return false; +} + +static void process_nodes_for_depsgraph(const bNodeTree &tree, + Set<ID *> &ids, + bool &needs_own_transform_relation) { Set<const bNodeTree *> handled_groups; LISTBASE_FOREACH (const bNode *, node, &tree.nodes) { - addIdsUsedBySocket(&node->inputs, ids); - addIdsUsedBySocket(&node->outputs, ids); + add_used_ids_from_sockets(node->inputs, ids); + add_used_ids_from_sockets(node->outputs, ids); if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { const bNodeTree *group = (bNodeTree *)node->id; if (group != nullptr && handled_groups.add(group)) { - find_used_ids_from_nodes(*group, ids); + process_nodes_for_depsgraph(*group, ids, needs_own_transform_relation); } } + needs_own_transform_relation |= node_needs_own_transform_relation(*node); } } @@ -236,37 +269,43 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md); - DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier"); - if (nmd->node_group != nullptr) { - DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Nodes Modifier"); - - Set<ID *> used_ids; - find_used_ids_from_settings(nmd->settings, used_ids); - find_used_ids_from_nodes(*nmd->node_group, used_ids); - for (ID *id : used_ids) { - switch ((ID_Type)GS(id->name)) { - case ID_OB: { - Object *object = reinterpret_cast<Object *>(id); - add_object_relation(ctx, *object); - break; - } - case ID_GR: { - Collection *collection = reinterpret_cast<Collection *>(id); - add_collection_relation(ctx, *collection); - break; - } - case ID_IM: - case ID_TE: { - DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier"); - } - default: { - /* Purposefully don't add relations for materials. While there are material sockets, - * the pointers are only passed around as handles rather than dereferenced. */ - break; - } + if (nmd->node_group == nullptr) { + return; + } + + DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Nodes Modifier"); + + bool needs_own_transform_relation = false; + Set<ID *> used_ids; + find_used_ids_from_settings(nmd->settings, used_ids); + process_nodes_for_depsgraph(*nmd->node_group, used_ids, needs_own_transform_relation); + for (ID *id : used_ids) { + switch ((ID_Type)GS(id->name)) { + case ID_OB: { + Object *object = reinterpret_cast<Object *>(id); + add_object_relation(ctx, *object); + break; + } + case ID_GR: { + Collection *collection = reinterpret_cast<Collection *>(id); + add_collection_relation(ctx, *collection); + break; + } + case ID_IM: + case ID_TE: { + DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier"); + } + default: { + /* Purposefully don't add relations for materials. While there are material sockets, + * the pointers are only passed around as handles rather than dereferenced. */ + break; } } } + + if (needs_own_transform_relation) { + DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier"); + } } static bool check_tree_for_time_node(const bNodeTree &tree, @@ -892,80 +931,142 @@ static void clear_runtime_data(NodesModifierData *nmd) } } -static void store_field_on_geometry_component(GeometryComponent &component, - const StringRef attribute_name, - AttributeDomain domain, - const GField &field) +struct OutputAttributeInfo { + GField field; + StringRefNull name; +}; + +struct OutputAttributeToStore { + GeometryComponentType component_type; + AttributeDomain domain; + StringRefNull name; + GMutableSpan data; +}; + +/** + * The output attributes are organized based on their domain, because attributes on the same domain + * can be evaluated together. + */ +static MultiValueMap<AttributeDomain, OutputAttributeInfo> find_output_attributes_to_store( + const NodesModifierData &nmd, const NodeRef &output_node, Span<GMutablePointer> output_values) { - /* If the attribute name corresponds to a built-in attribute, use the domain of the built-in - * attribute instead. */ - if (component.attribute_is_builtin(attribute_name)) { - component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); - std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(attribute_name); - if (meta_data.has_value()) { - domain = meta_data->domain; + MultiValueMap<AttributeDomain, OutputAttributeInfo> outputs_by_domain; + for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) { + if (!socket_type_has_attribute_toggle(*socket->bsocket())) { + continue; } - else { - return; + + const std::string prop_name = socket->identifier() + attribute_name_suffix; + const IDProperty *prop = IDP_GetPropertyFromGroup(nmd.settings.properties, prop_name.c_str()); + if (prop == nullptr) { + continue; } + const StringRefNull attribute_name = IDP_String(prop); + if (attribute_name.is_empty()) { + continue; + } + + const int index = socket->index(); + const GPointer value = output_values[index]; + const ValueOrFieldCPPType *cpp_type = dynamic_cast<const ValueOrFieldCPPType *>(value.type()); + BLI_assert(cpp_type != nullptr); + const GField field = cpp_type->as_field(value.get()); + + const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink( + &nmd.node_group->outputs, socket->index()); + const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain; + OutputAttributeInfo output_info; + output_info.field = std::move(field); + output_info.name = attribute_name; + outputs_by_domain.add(domain, std::move(output_info)); } - const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(field.cpp_type()); - OutputAttribute attribute = component.attribute_try_get_for_output_only( - attribute_name, domain, data_type); - if (attribute) { - /* In the future we could also evaluate all output fields at once. */ - const int domain_size = component.attribute_domain_size(domain); - blender::bke::GeometryComponentFieldContext field_context{component, domain}; - blender::fn::FieldEvaluator field_evaluator{field_context, domain_size}; - field_evaluator.add_with_destination(field, attribute.varray()); - field_evaluator.evaluate(); - attribute.save(); - } + return outputs_by_domain; } -static void store_output_value_in_geometry(GeometrySet &geometry_set, - NodesModifierData *nmd, - const InputSocketRef &socket, - const GPointer value) +/** + * The computed values are stored in newly allocated arrays. They still have to be moved to the + * actual geometry. + */ +static Vector<OutputAttributeToStore> compute_attributes_to_store( + const GeometrySet &geometry, + const MultiValueMap<AttributeDomain, OutputAttributeInfo> &outputs_by_domain) { - if (!socket_type_has_attribute_toggle(*socket.bsocket())) { - return; - } - const std::string prop_name = socket.identifier() + attribute_name_suffix; - const IDProperty *prop = IDP_GetPropertyFromGroup(nmd->settings.properties, prop_name.c_str()); - if (prop == nullptr) { - return; - } - const StringRefNull attribute_name = IDP_String(prop); - if (attribute_name.is_empty()) { - return; + Vector<OutputAttributeToStore> attributes_to_store; + for (const GeometryComponentType component_type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}) { + if (!geometry.has(component_type)) { + continue; + } + const GeometryComponent &component = *geometry.get_component_for_read(component_type); + for (const auto item : outputs_by_domain.items()) { + const AttributeDomain domain = item.key; + const Span<OutputAttributeInfo> outputs_info = item.value; + if (!component.attribute_domain_supported(domain)) { + continue; + } + const int domain_size = component.attribute_domain_size(domain); + blender::bke::GeometryComponentFieldContext field_context{component, domain}; + blender::fn::FieldEvaluator field_evaluator{field_context, domain_size}; + for (const OutputAttributeInfo &output_info : outputs_info) { + const CPPType &type = output_info.field.cpp_type(); + OutputAttributeToStore store{ + component_type, + domain, + output_info.name, + GMutableSpan{ + type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}}; + field_evaluator.add_with_destination(output_info.field, store.data); + attributes_to_store.append(store); + } + field_evaluator.evaluate(); + } } - const blender::fn::ValueOrFieldCPPType *cpp_type = - dynamic_cast<const blender::fn::ValueOrFieldCPPType *>(value.type()); - BLI_assert(cpp_type != nullptr); + return attributes_to_store; +} - const GField field = cpp_type->as_field(value.get()); - const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&nmd->node_group->outputs, - socket.index()); - const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain; - if (geometry_set.has_mesh()) { - MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>(); - store_field_on_geometry_component(component, attribute_name, domain, field); - } - if (geometry_set.has_pointcloud()) { - PointCloudComponent &component = geometry_set.get_component_for_write<PointCloudComponent>(); - store_field_on_geometry_component(component, attribute_name, domain, field); - } - if (geometry_set.has_curve()) { - CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); - store_field_on_geometry_component(component, attribute_name, domain, field); - } - if (geometry_set.has_instances()) { - InstancesComponent &component = geometry_set.get_component_for_write<InstancesComponent>(); - store_field_on_geometry_component(component, attribute_name, domain, field); +static void store_computed_output_attributes( + GeometrySet &geometry, const Span<OutputAttributeToStore> attributes_to_store) +{ + for (const OutputAttributeToStore &store : attributes_to_store) { + GeometryComponent &component = geometry.get_component_for_write(store.component_type); + if (component.attribute_exists(store.name)) { + /* Copy the data into an existing attribute. */ + blender::bke::WriteAttributeLookup write_attribute = component.attribute_try_get_for_write( + store.name); + if (write_attribute) { + write_attribute.varray.set_all(store.data.data()); + if (write_attribute.tag_modified_fn) { + write_attribute.tag_modified_fn(); + } + } + store.data.type().destruct_n(store.data.data(), store.data.size()); + MEM_freeN(store.data.data()); + } + else { + component.attribute_try_create(store.name, + store.domain, + blender::bke::cpp_type_to_custom_data_type(store.data.type()), + AttributeInitMove(store.data.data())); + } } } +static void store_output_attributes(GeometrySet &geometry, + const NodesModifierData &nmd, + const NodeRef &output_node, + Span<GMutablePointer> output_values) +{ + /* All new attribute values have to be computed before the geometry is actually changed. This is + * necessary because some fields might depend on attributes that are overwritten. */ + MultiValueMap<AttributeDomain, OutputAttributeInfo> outputs_by_domain = + find_output_attributes_to_store(nmd, output_node, output_values); + Vector<OutputAttributeToStore> attributes_to_store = compute_attributes_to_store( + geometry, outputs_by_domain); + store_computed_output_attributes(geometry, attributes_to_store); +} + /** * Evaluate a node group to compute the output geometry. */ @@ -1040,19 +1141,20 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr; blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); - GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out<GeometrySet>(); + GeometrySet output_geometry_set = std::move(*eval_params.r_output_values[0].get<GeometrySet>()); if (geo_logger.has_value()) { geo_logger->log_output_geometry(output_geometry_set); - NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(&nmd->modifier); + NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(ctx->object, + &nmd->modifier); clear_runtime_data(nmd_orig); nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); } - for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) { - GMutablePointer socket_value = eval_params.r_output_values[socket->index()]; - store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value); - socket_value.destruct(); + store_output_attributes(output_geometry_set, *nmd, output_node, eval_params.r_output_values); + + for (GMutablePointer value : eval_params.r_output_values) { + value.destruct(); } return output_geometry_set; @@ -1616,7 +1718,6 @@ ModifierTypeInfo modifierType_Nodes = { /* deformVertsEM */ nullptr, /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, - /* modifyHair */ nullptr, /* modifyGeometrySet */ modifyGeometrySet, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 5362d86a87f..f59af9074ce 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1347,7 +1347,7 @@ class GeometryNodesEvaluator { } input_state.usage = ValueUsage::Unused; - /* If the input is unused, it's value can be destructed now. */ + /* If the input is unused, its value can be destructed now. */ this->destruct_input_value_if_exists(locked_node, socket); if (input_state.was_ready_for_execution) { diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c index a01f63be791..cce3434f6e3 100644 --- a/source/blender/modifiers/intern/MOD_none.c +++ b/source/blender/modifiers/intern/MOD_none.c @@ -58,7 +58,6 @@ ModifierTypeInfo modifierType_None = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ NULL, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 61099fedf46..8b58b575d24 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -336,8 +336,6 @@ static void normalEditModifier_do_radial(NormalEditModifierData *enmd, if (do_polynors_fix && polygons_check_flip( mloop, nos, &mesh->ldata, mpoly, BKE_mesh_poly_normals_for_write(mesh), num_polys)) { - /* XXX TODO: is this still needed? */ - // mesh->dirty |= DM_DIRTY_TESS_CDLAYERS; /* We need to recompute vertex normals! */ BKE_mesh_normals_tag_dirty(mesh); } @@ -788,7 +786,6 @@ ModifierTypeInfo modifierType_NormalEdit = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index d821caf25a7..0c00f807df3 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -741,7 +741,6 @@ ModifierTypeInfo modifierType_Ocean = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 4fffa7c93f3..67a492d4154 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -677,7 +677,6 @@ ModifierTypeInfo modifierType_ParticleInstance = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index 2a4cc1c2747..6fb60fffcf4 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -334,7 +334,6 @@ ModifierTypeInfo modifierType_ParticleSystem = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index 937a73fddd9..67f83b17f3f 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -299,7 +299,6 @@ ModifierTypeInfo modifierType_Remesh = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index f5db3bced7a..33c62197dbd 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -27,6 +27,7 @@ #include "BLI_utildefines.h" #include "BLI_alloca.h" +#include "BLI_bitmap.h" #include "BLI_math.h" #include "BLT_translation.h" @@ -134,6 +135,8 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result, const float axis_offset[3], const float merge_threshold) { + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(totvert, __func__); + const float merge_threshold_sq = square_f(merge_threshold); const bool use_offset = axis_offset != NULL; uint tot_doubles = 0; @@ -150,13 +153,10 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result, } const float dist_sq = len_squared_v3v3(axis_co, mvert_new[i].co); if (dist_sq <= merge_threshold_sq) { - mvert_new[i].flag |= ME_VERT_TMP_TAG; + BLI_BITMAP_ENABLE(vert_tag, i); tot_doubles += 1; copy_v3_v3(mvert_new[i].co, axis_co); } - else { - mvert_new[i].flag &= ~ME_VERT_TMP_TAG & 0xFF; - } } if (tot_doubles != 0) { @@ -166,7 +166,7 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result, uint tot_doubles_left = tot_doubles; for (uint i = 0; i < totvert; i += 1) { - if (mvert_new[i].flag & ME_VERT_TMP_TAG) { + if (BLI_BITMAP_TEST(vert_tag, i)) { int *doubles_map = &full_doubles_map[totvert + i]; for (uint step = 1; step < step_tot; step += 1) { *doubles_map = (int)i; @@ -184,6 +184,9 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result, MESH_MERGE_VERTS_DUMP_IF_MAPPED); MEM_freeN(full_doubles_map); } + + MEM_freeN(vert_tag); + return result; } @@ -439,6 +442,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * mv_new = mvert_new; mv_orig = mvert_orig; + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(totvert, __func__); + /* Copy the first set of edges */ med_orig = medge_orig; med_new = medge_new; @@ -447,10 +452,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * med_new->v2 = med_orig->v2; med_new->crease = med_orig->crease; med_new->flag = med_orig->flag & ~ME_LOOSEEDGE; - /* Tag mvert as not loose. - * NOTE: ME_VERT_TMP_TAG is given to be cleared by BKE_mesh_new_nomain_from_template. */ - mvert_new[med_orig->v1].flag |= ME_VERT_TMP_TAG; - mvert_new[med_orig->v2].flag |= ME_VERT_TMP_TAG; + + /* Tag mvert as not loose. */ + BLI_BITMAP_ENABLE(vert_tag, med_orig->v1); + BLI_BITMAP_ENABLE(vert_tag, med_orig->v1); } /* build polygon -> edge map */ @@ -910,7 +915,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * med_new->v1 = varray_stride + j; med_new->v2 = med_new->v1 - totvert; med_new->flag = ME_EDGEDRAW | ME_EDGERENDER; - if ((mv_new_base->flag & ME_VERT_TMP_TAG) == 0) { + if (!BLI_BITMAP_TEST(vert_tag, j)) { med_new->flag |= ME_LOOSEEDGE; } med_new++; @@ -931,7 +936,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * med_new->v1 = i; med_new->v2 = varray_stride + i; med_new->flag = ME_EDGEDRAW | ME_EDGERENDER; - if ((mvert_new[i].flag & ME_VERT_TMP_TAG) == 0) { + if (!BLI_BITMAP_TEST(vert_tag, i)) { med_new->flag |= ME_LOOSEEDGE; } med_new++; @@ -1119,6 +1124,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } #endif + MEM_freeN(vert_tag); + if (edge_poly_map) { MEM_freeN(edge_poly_map); } @@ -1257,7 +1264,6 @@ ModifierTypeInfo modifierType_Screw = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c index b517bc102f8..8c5299a965d 100644 --- a/source/blender/modifiers/intern/MOD_shapekey.c +++ b/source/blender/modifiers/intern/MOD_shapekey.c @@ -139,7 +139,6 @@ ModifierTypeInfo modifierType_ShapeKey = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ deformMatricesEM, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ NULL, diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index a12724ec23c..4d10df91331 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -291,7 +291,6 @@ ModifierTypeInfo modifierType_Shrinkwrap = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index 39ebc415021..25c3acb0c4f 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -596,7 +596,6 @@ ModifierTypeInfo modifierType_SimpleDeform = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 07ce819e91c..d1cb120132d 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -2101,7 +2101,6 @@ ModifierTypeInfo modifierType_Skin = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c index 97027e2ecff..3228a72d958 100644 --- a/source/blender/modifiers/intern/MOD_smooth.c +++ b/source/blender/modifiers/intern/MOD_smooth.c @@ -284,7 +284,6 @@ ModifierTypeInfo modifierType_Smooth = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 46e960e10d4..f0178a817dc 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -120,7 +120,6 @@ ModifierTypeInfo modifierType_Softbody = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ NULL, diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 736dd08a713..09933fef7ff 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -274,7 +274,6 @@ ModifierTypeInfo modifierType_Solidify = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index b77f6b7e3e2..65ad298cff1 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -245,9 +245,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * if ((ctx->flag & MOD_APPLY_TO_BASE_MESH) == 0) { Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); const bool is_render_mode = (ctx->flag & MOD_APPLY_RENDER) != 0; - /* Same check as in `DRW_mesh_batch_cache_create_requested` to keep both code coherent. */ - const bool is_editmode = (mesh->edit_mesh != NULL) && - (mesh->edit_mesh->mesh_eval_final != NULL); + /* Same check as in `DRW_mesh_batch_cache_create_requested` to keep both code coherent. The + * difference is that here we do not check for the final edit mesh pointer as it is not yet + * assigned at this stage of modifier stack evaluation. */ + const bool is_editmode = (mesh->edit_mesh != NULL); const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode); if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex(scene, ctx->object, smd, required_mode, false)) { subdiv_cache_cpu_evaluation_settings(ctx, mesh, smd); @@ -499,7 +500,6 @@ ModifierTypeInfo modifierType_Subsurf = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index c8be2bd2829..a54af766b2f 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -245,7 +245,6 @@ ModifierTypeInfo modifierType_Surface = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index ec6de8f8387..70a05002a4f 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1454,7 +1454,7 @@ static void surfacedeformModifier_do(ModifierData *md, BKE_modifier_set_error(ob, md, "Attempt to bind from inactive dependency graph"); return; } - ModifierData *md_orig = BKE_modifier_get_original(md); + ModifierData *md_orig = BKE_modifier_get_original(ob, md); freeData(md_orig); } return; @@ -1478,7 +1478,7 @@ static void surfacedeformModifier_do(ModifierData *md, } SurfaceDeformModifierData *smd_orig = (SurfaceDeformModifierData *)BKE_modifier_get_original( - md); + ob, md); float tmp_mat[4][4]; invert_m4_m4(tmp_mat, ob->obmat); @@ -1715,7 +1715,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index b713df05b80..d4fcebb3216 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -170,7 +170,6 @@ ModifierTypeInfo modifierType_Triangulate = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index 238952fde00..5721834c032 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -383,7 +383,6 @@ ModifierTypeInfo modifierType_UVProject = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index 3f161d339c2..552cf1d5d3b 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -233,9 +233,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * settings.use_threading = (numPolys > 1000); BLI_task_parallel_range(0, numPolys, &data, uv_warp_compute, &settings); - /* XXX TODO: is this still needed? */ - // me_eval->dirty |= DM_DIRTY_TESS_CDLAYERS; - mesh->runtime.is_original = false; return mesh; @@ -340,7 +337,6 @@ ModifierTypeInfo modifierType_UVWarp = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index a1ca29f454c..2b9ee97ea09 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -183,7 +183,7 @@ template<typename GridType> struct DisplaceOp { TexResult texture_result = {0}; BKE_texture_get_value( nullptr, this->texture, const_cast<float *>(pos.asV()), &texture_result, false); - return {texture_result.tr, texture_result.tg, texture_result.tb}; + return {texture_result.trgba[0], texture_result.trgba[1], texture_result.trgba[2]}; } }; @@ -339,7 +339,6 @@ ModifierTypeInfo modifierType_VolumeDisplace = { /* deformVertsEM */ nullptr, /* deformMatricesEM */ nullptr, /* modifyMesh */ nullptr, - /* modifyHair */ nullptr, /* modifyGeometrySet */ modifyGeometrySet, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index 72358844838..9557abf0a8a 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_VolumeToMesh = { /* deformVertsEM */ nullptr, /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, - /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 25e33b22bde..777ae4d9b10 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -539,7 +539,6 @@ ModifierTypeInfo modifierType_Warp = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index b7ab5dac388..5a56ec1d9e5 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -494,7 +494,6 @@ ModifierTypeInfo modifierType_Wave = { /* deformVertsEM */ deformVertsEM, /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index bfe389eb080..21e6ed46908 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -757,7 +757,6 @@ ModifierTypeInfo modifierType_WeightedNormal = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index cd9e5162527..eedaea9d403 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -186,28 +186,32 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, org_w[i] = (new_w[i] * texres.tin * fact) + (org_w[i] * (1.0f - (texres.tin * fact))); break; case MOD_WVG_MASK_TEX_USE_RED: - org_w[i] = (new_w[i] * texres.tr * fact) + (org_w[i] * (1.0f - (texres.tr * fact))); + org_w[i] = (new_w[i] * texres.trgba[0] * fact) + + (org_w[i] * (1.0f - (texres.trgba[0] * fact))); break; case MOD_WVG_MASK_TEX_USE_GREEN: - org_w[i] = (new_w[i] * texres.tg * fact) + (org_w[i] * (1.0f - (texres.tg * fact))); + org_w[i] = (new_w[i] * texres.trgba[1] * fact) + + (org_w[i] * (1.0f - (texres.trgba[1] * fact))); break; case MOD_WVG_MASK_TEX_USE_BLUE: - org_w[i] = (new_w[i] * texres.tb * fact) + (org_w[i] * (1.0f - (texres.tb * fact))); + org_w[i] = (new_w[i] * texres.trgba[2] * fact) + + (org_w[i] * (1.0f - (texres.trgba[2] * fact))); break; case MOD_WVG_MASK_TEX_USE_HUE: - rgb_to_hsv_v(&texres.tr, hsv); + rgb_to_hsv_v(texres.trgba, hsv); org_w[i] = (new_w[i] * hsv[0] * fact) + (org_w[i] * (1.0f - (hsv[0] * fact))); break; case MOD_WVG_MASK_TEX_USE_SAT: - rgb_to_hsv_v(&texres.tr, hsv); + rgb_to_hsv_v(texres.trgba, hsv); org_w[i] = (new_w[i] * hsv[1] * fact) + (org_w[i] * (1.0f - (hsv[1] * fact))); break; case MOD_WVG_MASK_TEX_USE_VAL: - rgb_to_hsv_v(&texres.tr, hsv); + rgb_to_hsv_v(texres.trgba, hsv); org_w[i] = (new_w[i] * hsv[2] * fact) + (org_w[i] * (1.0f - (hsv[2] * fact))); break; case MOD_WVG_MASK_TEX_USE_ALPHA: - org_w[i] = (new_w[i] * texres.ta * fact) + (org_w[i] * (1.0f - (texres.ta * fact))); + org_w[i] = (new_w[i] * texres.trgba[3] * fact) + + (org_w[i] * (1.0f - (texres.trgba[3] * fact))); break; default: org_w[i] = (new_w[i] * texres.tin * fact) + (org_w[i] * (1.0f - (texres.tin * fact))); diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index a9d01c64ff1..6e4f8be8c50 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -428,7 +428,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index b369b82ebb7..1b2608afa12 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -514,7 +514,6 @@ ModifierTypeInfo modifierType_WeightVGMix = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 7ee19e1c537..27242b32432 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -767,7 +767,6 @@ ModifierTypeInfo modifierType_WeightVGProximity = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weld.cc b/source/blender/modifiers/intern/MOD_weld.cc index 2d4710ca71a..e9d98429ff2 100644 --- a/source/blender/modifiers/intern/MOD_weld.cc +++ b/source/blender/modifiers/intern/MOD_weld.cc @@ -27,21 +27,12 @@ * - Review weight and vertex color interpolation.; */ -//#define USE_WELD_DEBUG -//#define USE_WELD_NORMALS -//#define USE_BVHTREEKDOP - -#include <algorithm> - #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" #include "BLI_array.hh" -#include "BLI_bitmap.h" #include "BLI_index_range.hh" -#include "BLI_kdtree.h" -#include "BLI_math.h" #include "BLI_span.hh" #include "BLI_vector.hh" @@ -51,7 +42,6 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" -#include "DNA_object_types.h" #include "DNA_screen_types.h" #ifdef USE_BVHTREEKDOP @@ -60,7 +50,6 @@ #include "BKE_context.h" #include "BKE_deform.h" -#include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_screen.h" @@ -74,1808 +63,94 @@ #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" +#include "GEO_mesh_merge_by_distance.hh" + using blender::Array; -using blender::IndexRange; -using blender::MutableSpan; +using blender::IndexMask; using blender::Span; using blender::Vector; -/* Indicates when the element was not computed. */ -#define OUT_OF_CONTEXT (int)(-1) -/* Indicates if the edge or face will be collapsed. */ -#define ELEM_COLLAPSED (int)(-2) -/* indicates whether an edge or vertex in groups_map will be merged. */ -#define ELEM_MERGED (int)(-2) - -/* Used to indicate a range in an array specifying a group. */ -struct WeldGroup { - int len; - int ofs; -}; - -/* Edge groups that will be merged. Final vertices are also indicated. */ -struct WeldGroupEdge { - struct WeldGroup group; - int v1; - int v2; -}; - -struct WeldVert { - /* Indexes relative to the original Mesh. */ - int vert_dest; - int vert_orig; -}; - -struct WeldEdge { - union { - int flag; - struct { - /* Indexes relative to the original Mesh. */ - int edge_dest; - int edge_orig; - int vert_a; - int vert_b; - }; - }; -}; - -struct WeldLoop { - union { - int flag; - struct { - /* Indexes relative to the original Mesh. */ - int vert; - int edge; - int loop_orig; - int loop_skip_to; - }; - }; -}; - -struct WeldPoly { - union { - int flag; - struct { - /* Indexes relative to the original Mesh. */ - int poly_dst; - int poly_orig; - int loop_start; - int loop_end; - /* Final Polygon Size. */ - int len; - /* Group of loops that will be affected. */ - struct WeldGroup loops; - }; - }; -}; - -struct WeldMesh { - /* Group of vertices to be merged. */ - Array<WeldGroup> vert_groups; - Array<int> vert_groups_buffer; - - /* Group of edges to be merged. */ - Array<WeldGroupEdge> edge_groups; - Array<int> edge_groups_buffer; - /* From the original index of the vertex, this indicates which group it is or is going to be - * merged. */ - Array<int> edge_groups_map; - - /* References all polygons and loops that will be affected. */ - Vector<WeldLoop> wloop; - Vector<WeldPoly> wpoly; - WeldPoly *wpoly_new; - int wloop_len; - int wpoly_len; - int wpoly_new_len; - - /* From the actual index of the element in the mesh, it indicates what is the index of the Weld - * element above. */ - Array<int> loop_map; - Array<int> poly_map; - - int vert_kill_len; - int edge_kill_len; - int loop_kill_len; - int poly_kill_len; /* Including the new polygons. */ - - /* Size of the affected polygon with more sides. */ - int max_poly_len; -}; - -struct WeldLoopOfPolyIter { - int loop_start; - int loop_end; - Span<WeldLoop> wloop; - Span<MLoop> mloop; - Span<int> loop_map; - /* Weld group. */ - int *group; - - int l_curr; - int l_next; - - /* Return */ - int group_len; - int v; - int e; - char type; -}; - -/* -------------------------------------------------------------------- */ -/** \name Debug Utils - * \{ */ - -#ifdef USE_WELD_DEBUG -static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, - const WeldPoly &wp, - Span<WeldLoop> wloop, - Span<MLoop> mloop, - Span<int> loop_map, - int *group_buffer); - -static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter); - -static void weld_assert_edge_kill_len(Span<WeldEdge> wedge, const int supposed_kill_len) -{ - int kills = 0; - const WeldEdge *we = &wedge[0]; - for (int i = wedge.size(); i--; we++) { - int edge_dest = we->edge_dest; - /* Magically includes collapsed edges. */ - if (edge_dest != OUT_OF_CONTEXT) { - kills++; - } - } - BLI_assert(kills == supposed_kill_len); -} - -static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly, - Span<WeldPoly> wpoly_new, - Span<WeldLoop> wloop, - Span<MLoop> mloop, - Span<int> loop_map, - Span<int> poly_map, - Span<MPoly> mpoly, - const int supposed_poly_kill_len, - const int supposed_loop_kill_len) -{ - int poly_kills = 0; - int loop_kills = mloop.size(); - const MPoly *mp = &mpoly[0]; - for (int i = 0; i < mpoly.size(); i++, mp++) { - int poly_ctx = poly_map[i]; - if (poly_ctx != OUT_OF_CONTEXT) { - const WeldPoly *wp = &wpoly[poly_ctx]; - WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin(&iter, *wp, wloop, mloop, loop_map, nullptr)) { - poly_kills++; - continue; - } - else { - if (wp->poly_dst != OUT_OF_CONTEXT) { - poly_kills++; - continue; - } - int remain = wp->len; - int l = wp->loop_start; - while (remain) { - int l_next = l + 1; - int loop_ctx = loop_map[l]; - if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; - if (wl->loop_skip_to != OUT_OF_CONTEXT) { - l_next = wl->loop_skip_to; - } - if (wl->flag != ELEM_COLLAPSED) { - loop_kills--; - remain--; - } - } - else { - loop_kills--; - remain--; - } - l = l_next; - } - } - } - else { - loop_kills -= mp->totloop; - } - } - - const WeldPoly *wp = wpoly_new.data(); - for (int i = wpoly_new.size(); i--; wp++) { - if (wp->poly_dst != OUT_OF_CONTEXT) { - poly_kills++; - continue; - } - int remain = wp->len; - int l = wp->loop_start; - while (remain) { - int l_next = l + 1; - int loop_ctx = loop_map[l]; - if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; - if (wl->loop_skip_to != OUT_OF_CONTEXT) { - l_next = wl->loop_skip_to; - } - if (wl->flag != ELEM_COLLAPSED) { - loop_kills--; - remain--; - } - } - else { - loop_kills--; - remain--; - } - l = l_next; - } - } - - BLI_assert(poly_kills == supposed_poly_kill_len); - BLI_assert(loop_kills == supposed_loop_kill_len); -} - -static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp, - Span<WeldLoop> wloop, - Span<MLoop> mloop, - Span<int> loop_map) -{ - const int len = wp.len; - Array<int, 64> verts(len); - WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) { - return; - } - else { - int i = 0; - while (weld_iter_loop_of_poly_next(iter)) { - verts[i++] = iter.v; - } - } - for (int i = 0; i < len; i++) { - int va = verts[i]; - for (int j = i + 1; j < len; j++) { - int vb = verts[j]; - BLI_assert(va != vb); - } - } -} - -static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop) -{ - if (wp->flag == ELEM_COLLAPSED) { - return; - } - - int len = wp->len; - const WeldLoop *wl = &wloop[wp->loops.ofs]; - BLI_assert(wp->loop_start <= wl->loop_orig); - - int end_wloop = wp->loops.ofs + wp->loops.len; - const WeldLoop *wl_end = &wloop[end_wloop - 1]; - - int min_len = 0; - for (; wl <= wl_end; wl++) { - BLI_assert(wl->loop_skip_to == OUT_OF_CONTEXT); /* Not for this case. */ - if (wl->flag != ELEM_COLLAPSED) { - min_len++; - } - } - BLI_assert(len >= min_len); - - int max_len = wp->loop_end - wp->loop_start + 1; - BLI_assert(len <= max_len); -} - -#endif /* USE_WELD_DEBUG */ - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Vert API - * \{ */ - -static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map, - const int vert_kill_len) -{ - Vector<WeldVert> wvert; - wvert.reserve(std::min<int>(2 * vert_kill_len, vert_dest_map.size())); - - for (const int i : vert_dest_map.index_range()) { - if (vert_dest_map[i] != OUT_OF_CONTEXT) { - WeldVert wv{}; - wv.vert_dest = vert_dest_map[i]; - wv.vert_orig = i; - wvert.append(wv); - } - } - return wvert; -} - -static void weld_vert_groups_setup(Span<WeldVert> wvert, - Span<int> vert_dest_map, - MutableSpan<int> r_vert_groups_map, - Array<int> &r_vert_groups_buffer, - Array<WeldGroup> &r_vert_groups) -{ - /* Get weld vert groups. */ - - int wgroups_len = 0; - for (const int i : vert_dest_map.index_range()) { - const int vert_dest = vert_dest_map[i]; - if (vert_dest != OUT_OF_CONTEXT) { - if (vert_dest != i) { - r_vert_groups_map[i] = ELEM_MERGED; - } - else { - r_vert_groups_map[i] = wgroups_len; - wgroups_len++; - } - } - else { - r_vert_groups_map[i] = OUT_OF_CONTEXT; - } - } - - r_vert_groups.reinitialize(wgroups_len); - r_vert_groups.fill({0, 0}); - MutableSpan<WeldGroup> wgroups = r_vert_groups; - - for (const WeldVert &wv : wvert) { - int group_index = r_vert_groups_map[wv.vert_dest]; - wgroups[group_index].len++; - } - - int ofs = 0; - for (WeldGroup &wg : wgroups) { - wg.ofs = ofs; - ofs += wg.len; - } - - BLI_assert(ofs == wvert.size()); - - r_vert_groups_buffer.reinitialize(ofs); - for (const WeldVert &wv : wvert) { - int group_index = r_vert_groups_map[wv.vert_dest]; - r_vert_groups_buffer[wgroups[group_index].ofs++] = wv.vert_orig; - } - - for (WeldGroup &wg : wgroups) { - wg.ofs -= wg.len; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Edge API - * \{ */ - -static void weld_edge_ctx_setup(MutableSpan<WeldGroup> r_vlinks, - MutableSpan<int> r_edge_dest_map, - MutableSpan<WeldEdge> r_wedge, - int *r_edge_kiil_len) +static Span<MDeformVert> get_vertex_group(const Mesh &mesh, const int defgrp_index) { - /* Setup Edge Overlap. */ - int edge_kill_len = 0; - - MutableSpan<WeldGroup> v_links = r_vlinks; - - for (WeldEdge &we : r_wedge) { - int dst_vert_a = we.vert_a; - int dst_vert_b = we.vert_b; - - if (dst_vert_a == dst_vert_b) { - BLI_assert(we.edge_dest == OUT_OF_CONTEXT); - r_edge_dest_map[we.edge_orig] = ELEM_COLLAPSED; - we.flag = ELEM_COLLAPSED; - edge_kill_len++; - continue; - } - - v_links[dst_vert_a].len++; - v_links[dst_vert_b].len++; - } - - int link_len = 0; - for (WeldGroup &vl : r_vlinks) { - vl.ofs = link_len; - link_len += vl.len; + if (defgrp_index == -1) { + return {}; } - - if (link_len > 0) { - Array<int> link_edge_buffer(link_len); - - for (const int i : r_wedge.index_range()) { - const WeldEdge &we = r_wedge[i]; - if (we.flag == ELEM_COLLAPSED) { - continue; - } - - int dst_vert_a = we.vert_a; - int dst_vert_b = we.vert_b; - - link_edge_buffer[v_links[dst_vert_a].ofs++] = i; - link_edge_buffer[v_links[dst_vert_b].ofs++] = i; - } - - for (WeldGroup &vl : r_vlinks) { - /* Fix offset */ - vl.ofs -= vl.len; - } - - for (const int i : r_wedge.index_range()) { - const WeldEdge &we = r_wedge[i]; - if (we.edge_dest != OUT_OF_CONTEXT) { - /* No need to retest edges. - * (Already includes collapsed edges). */ - continue; - } - - int dst_vert_a = we.vert_a; - int dst_vert_b = we.vert_b; - - struct WeldGroup *link_a = &v_links[dst_vert_a]; - struct WeldGroup *link_b = &v_links[dst_vert_b]; - - int edges_len_a = link_a->len; - int edges_len_b = link_b->len; - - if (edges_len_a <= 1 || edges_len_b <= 1) { - continue; - } - - int *edges_ctx_a = &link_edge_buffer[link_a->ofs]; - int *edges_ctx_b = &link_edge_buffer[link_b->ofs]; - int edge_orig = we.edge_orig; - - for (; edges_len_a--; edges_ctx_a++) { - int e_ctx_a = *edges_ctx_a; - if (e_ctx_a == i) { - continue; - } - while (edges_len_b && *edges_ctx_b < e_ctx_a) { - edges_ctx_b++; - edges_len_b--; - } - if (edges_len_b == 0) { - break; - } - int e_ctx_b = *edges_ctx_b; - if (e_ctx_a == e_ctx_b) { - WeldEdge *we_b = &r_wedge[e_ctx_b]; - BLI_assert(ELEM(we_b->vert_a, dst_vert_a, dst_vert_b)); - BLI_assert(ELEM(we_b->vert_b, dst_vert_a, dst_vert_b)); - BLI_assert(we_b->edge_dest == OUT_OF_CONTEXT); - BLI_assert(we_b->edge_orig != edge_orig); - r_edge_dest_map[we_b->edge_orig] = edge_orig; - we_b->edge_dest = edge_orig; - edge_kill_len++; - } - } - } - -#ifdef USE_WELD_DEBUG - weld_assert_edge_kill_len(r_wedge, edge_kill_len); -#endif + const MDeformVert *vertex_group = static_cast<const MDeformVert *>( + CustomData_get_layer(&mesh.vdata, CD_MDEFORMVERT)); + if (!vertex_group) { + return {}; } - - *r_edge_kiil_len = edge_kill_len; + return {vertex_group, mesh.totvert}; } -static Vector<WeldEdge> weld_edge_ctx_alloc(Span<MEdge> medge, - Span<int> vert_dest_map, - MutableSpan<int> r_edge_dest_map, - MutableSpan<int> r_edge_ctx_map) +static Vector<int64_t> selected_indices_from_vertex_group(Span<MDeformVert> vertex_group, + const int index, + const bool invert) { - /* Edge Context. */ - int wedge_len = 0; - - Vector<WeldEdge> wedge; - wedge.reserve(medge.size()); - - for (const int i : medge.index_range()) { - int v1 = medge[i].v1; - int v2 = medge[i].v2; - int v_dest_1 = vert_dest_map[v1]; - int v_dest_2 = vert_dest_map[v2]; - if ((v_dest_1 != OUT_OF_CONTEXT) || (v_dest_2 != OUT_OF_CONTEXT)) { - WeldEdge we{}; - we.vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1; - we.vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2; - we.edge_dest = OUT_OF_CONTEXT; - we.edge_orig = i; - wedge.append(we); - r_edge_dest_map[i] = i; - r_edge_ctx_map[i] = wedge_len++; - } - else { - r_edge_dest_map[i] = OUT_OF_CONTEXT; - r_edge_ctx_map[i] = OUT_OF_CONTEXT; + Vector<int64_t> selected_indices; + for (const int i : vertex_group.index_range()) { + const bool found = BKE_defvert_find_weight(&vertex_group[i], index) > 0.0f; + if (found != invert) { + selected_indices.append(i); } } - - return wedge; + return selected_indices; } -static void weld_edge_groups_setup(const int medge_len, - const int edge_kill_len, - MutableSpan<WeldEdge> wedge, - Span<int> wedge_map, - MutableSpan<int> r_edge_groups_map, - Array<int> &r_edge_groups_buffer, - Array<WeldGroupEdge> &r_edge_groups) +static Array<bool> selection_array_from_vertex_group(Span<MDeformVert> vertex_group, + const int index, + const bool invert) { - /* Get weld edge groups. */ - - struct WeldGroupEdge *wegrp_iter; - - int wgroups_len = wedge.size() - edge_kill_len; - r_edge_groups.reinitialize(wgroups_len); - r_edge_groups.fill({{0}}); - MutableSpan<WeldGroupEdge> wegroups = r_edge_groups; - wegrp_iter = &r_edge_groups[0]; - - wgroups_len = 0; - for (const int i : IndexRange(medge_len)) { - int edge_ctx = wedge_map[i]; - if (edge_ctx != OUT_OF_CONTEXT) { - WeldEdge *we = &wedge[edge_ctx]; - int edge_dest = we->edge_dest; - if (edge_dest != OUT_OF_CONTEXT) { - BLI_assert(edge_dest != we->edge_orig); - r_edge_groups_map[i] = ELEM_MERGED; - } - else { - we->edge_dest = we->edge_orig; - wegrp_iter->v1 = we->vert_a; - wegrp_iter->v2 = we->vert_b; - r_edge_groups_map[i] = wgroups_len; - wgroups_len++; - wegrp_iter++; - } - } - else { - r_edge_groups_map[i] = OUT_OF_CONTEXT; - } - } - - BLI_assert(wgroups_len == wedge.size() - edge_kill_len); - - for (const WeldEdge &we : wedge) { - if (we.flag == ELEM_COLLAPSED) { - continue; - } - int group_index = r_edge_groups_map[we.edge_dest]; - wegroups[group_index].group.len++; - } - - int ofs = 0; - for (WeldGroupEdge &wegrp : wegroups) { - wegrp.group.ofs = ofs; - ofs += wegrp.group.len; - } - - r_edge_groups_buffer.reinitialize(ofs); - for (const WeldEdge &we : wedge) { - if (we.flag == ELEM_COLLAPSED) { - continue; - } - int group_index = r_edge_groups_map[we.edge_dest]; - r_edge_groups_buffer[wegroups[group_index].group.ofs++] = we.edge_orig; - } - - for (WeldGroupEdge &wegrp : wegroups) { - wegrp.group.ofs -= wegrp.group.len; + Array<bool> selection(vertex_group.size()); + for (const int i : vertex_group.index_range()) { + const bool found = BKE_defvert_find_weight(&vertex_group[i], index) > 0.0f; + selection[i] = (found != invert); } + return selection; } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Poly and Loop API - * \{ */ - -static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter &iter, - const WeldPoly &wp, - Span<WeldLoop> wloop, - Span<MLoop> mloop, - Span<int> loop_map, - int *group_buffer) +static std::optional<Mesh *> calculate_weld(const Mesh &mesh, const WeldModifierData &wmd) { - if (wp.flag == ELEM_COLLAPSED) { - return false; - } - - iter.loop_start = wp.loop_start; - iter.loop_end = wp.loop_end; - iter.wloop = wloop; - iter.mloop = mloop; - iter.loop_map = loop_map; - iter.group = group_buffer; + const int defgrp_index = BKE_id_defgroup_name_index(&mesh.id, wmd.defgrp_name); + Span<MDeformVert> vertex_group = get_vertex_group(mesh, defgrp_index); + const bool invert = (wmd.flag & MOD_WELD_INVERT_VGROUP) != 0; - int group_len = 0; - if (group_buffer) { - /* First loop group needs more attention. */ - int loop_start, loop_end, l; - loop_start = iter.loop_start; - loop_end = l = iter.loop_end; - while (l >= loop_start) { - const int loop_ctx = loop_map[l]; - if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; - if (wl->flag == ELEM_COLLAPSED) { - l--; - continue; - } - } - break; - } - if (l != loop_end) { - group_len = loop_end - l; - int i = 0; - while (l < loop_end) { - iter.group[i++] = ++l; - } + if (wmd.mode == MOD_WELD_MODE_ALL) { + if (!vertex_group.is_empty()) { + Vector<int64_t> selected_indices = selected_indices_from_vertex_group( + vertex_group, defgrp_index, invert); + return blender::geometry::mesh_merge_by_distance_all( + mesh, IndexMask(selected_indices), wmd.merge_dist); } + return blender::geometry::mesh_merge_by_distance_all( + mesh, IndexMask(mesh.totvert), wmd.merge_dist); } - iter.group_len = group_len; - - iter.l_next = iter.loop_start; -#ifdef USE_WELD_DEBUG - iter.v = OUT_OF_CONTEXT; -#endif - return true; -} - -static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter) -{ - const int loop_end = iter.loop_end; - Span<WeldLoop> wloop = iter.wloop; - Span<int> loop_map = iter.loop_map; - int l = iter.l_curr = iter.l_next; - if (l == iter.loop_start) { - /* `grupo_len` is already calculated in the first loop */ - } - else { - iter.group_len = 0; - } - while (l <= loop_end) { - int l_next = l + 1; - const int loop_ctx = loop_map[l]; - if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; - if (wl->loop_skip_to != OUT_OF_CONTEXT) { - l_next = wl->loop_skip_to; - } - if (wl->flag == ELEM_COLLAPSED) { - if (iter.group) { - iter.group[iter.group_len++] = l; - } - l = l_next; - continue; - } -#ifdef USE_WELD_DEBUG - BLI_assert(iter.v != wl->vert); -#endif - iter.v = wl->vert; - iter.e = wl->edge; - iter.type = 1; - } - else { - const MLoop &ml = iter.mloop[l]; -#ifdef USE_WELD_DEBUG - BLI_assert((uint)iter.v != ml.v); -#endif - iter.v = ml.v; - iter.e = ml.e; - iter.type = 0; - } - if (iter.group) { - iter.group[iter.group_len++] = l; - } - iter.l_next = l_next; - return true; - } - - return false; -} - -static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly, - Span<MLoop> mloop, - Span<int> vert_dest_map, - Span<int> edge_dest_map, - WeldMesh *r_weld_mesh) -{ - /* Loop/Poly Context. */ - Array<int> loop_map(mloop.size()); - Array<int> poly_map(mpoly.size()); - int wloop_len = 0; - int wpoly_len = 0; - int max_ctx_poly_len = 4; - - Vector<WeldLoop> wloop; - wloop.reserve(mloop.size()); - - Vector<WeldPoly> wpoly; - wpoly.reserve(mpoly.size()); - - int maybe_new_poly = 0; - - for (const int i : mpoly.index_range()) { - const MPoly &mp = mpoly[i]; - const int loopstart = mp.loopstart; - const int totloop = mp.totloop; - - int vert_ctx_len = 0; - - int prev_wloop_len = wloop_len; - for (const int i_loop : mloop.index_range().slice(loopstart, totloop)) { - int v = mloop[i_loop].v; - int e = mloop[i_loop].e; - int v_dest = vert_dest_map[v]; - int e_dest = edge_dest_map[e]; - bool is_vert_ctx = v_dest != OUT_OF_CONTEXT; - bool is_edge_ctx = e_dest != OUT_OF_CONTEXT; - if (is_vert_ctx) { - vert_ctx_len++; - } - if (is_vert_ctx || is_edge_ctx) { - WeldLoop wl{}; - wl.vert = is_vert_ctx ? v_dest : v; - wl.edge = is_edge_ctx ? e_dest : e; - wl.loop_orig = i_loop; - wl.loop_skip_to = OUT_OF_CONTEXT; - wloop.append(wl); - - loop_map[i_loop] = wloop_len++; - } - else { - loop_map[i_loop] = OUT_OF_CONTEXT; - } - } - if (wloop_len != prev_wloop_len) { - int loops_len = wloop_len - prev_wloop_len; - WeldPoly wp{}; - wp.poly_dst = OUT_OF_CONTEXT; - wp.poly_orig = i; - wp.loops.len = loops_len; - wp.loops.ofs = prev_wloop_len; - wp.loop_start = loopstart; - wp.loop_end = loopstart + totloop - 1; - wp.len = totloop; - wpoly.append(wp); - - poly_map[i] = wpoly_len++; - if (totloop > 5 && vert_ctx_len > 1) { - int max_new = (totloop / 3) - 1; - vert_ctx_len /= 2; - maybe_new_poly += MIN2(max_new, vert_ctx_len); - CLAMP_MIN(max_ctx_poly_len, totloop); - } - } - else { - poly_map[i] = OUT_OF_CONTEXT; + if (wmd.mode == MOD_WELD_MODE_CONNECTED) { + const bool only_loose_edges = (wmd.flag & MOD_WELD_LOOSE_EDGES) != 0; + if (!vertex_group.is_empty()) { + Array<bool> selection = selection_array_from_vertex_group( + vertex_group, defgrp_index, invert); + return blender::geometry::mesh_merge_by_distance_connected( + mesh, selection, wmd.merge_dist, only_loose_edges); } + Array<bool> selection(mesh.totvert, true); + return blender::geometry::mesh_merge_by_distance_connected( + mesh, selection, wmd.merge_dist, only_loose_edges); } - if (mpoly.size() < (wpoly_len + maybe_new_poly)) { - wpoly.resize(wpoly_len + maybe_new_poly); - } - - WeldPoly *poly_new = wpoly.data() + wpoly_len; - - r_weld_mesh->wloop = std::move(wloop); - r_weld_mesh->wpoly = std::move(wpoly); - r_weld_mesh->wpoly_new = poly_new; - r_weld_mesh->wloop_len = wloop_len; - r_weld_mesh->wpoly_len = wpoly_len; - r_weld_mesh->wpoly_new_len = 0; - r_weld_mesh->loop_map = std::move(loop_map); - r_weld_mesh->poly_map = std::move(poly_map); - r_weld_mesh->max_poly_len = max_ctx_poly_len; + BLI_assert_unreachable(); + return nullptr; } -static void weld_poly_split_recursive(Span<int> vert_dest_map, -#ifdef USE_WELD_DEBUG - const Span<MLoop> mloop, -#endif - int ctx_verts_len, - WeldPoly *r_wp, - WeldMesh *r_weld_mesh, - int *r_poly_kill, - int *r_loop_kill) +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh) { - int poly_len = r_wp->len; - if (poly_len > 3 && ctx_verts_len > 1) { - const int ctx_loops_len = r_wp->loops.len; - const int ctx_loops_ofs = r_wp->loops.ofs; - MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; - WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; - - int loop_kill = 0; - - WeldLoop *poly_loops = &wloop[ctx_loops_ofs]; - WeldLoop *wla = &poly_loops[0]; - WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1]; - while (wla_prev->flag == ELEM_COLLAPSED) { - wla_prev--; - } - const int la_len = ctx_loops_len - 1; - for (int la = 0; la < la_len; la++, wla++) { - wa_continue: - if (wla->flag == ELEM_COLLAPSED) { - continue; - } - int vert_a = wla->vert; - /* Only test vertices that will be merged. */ - if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) { - int lb = la + 1; - WeldLoop *wlb = wla + 1; - WeldLoop *wlb_prev = wla; - int killed_ab = 0; - ctx_verts_len = 1; - for (; lb < ctx_loops_len; lb++, wlb++) { - BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT); - if (wlb->flag == ELEM_COLLAPSED) { - killed_ab++; - continue; - } - int vert_b = wlb->vert; - if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) { - ctx_verts_len++; - } - if (vert_a == vert_b) { - const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; - const int dist_b = poly_len - dist_a; + const WeldModifierData &wmd = reinterpret_cast<WeldModifierData &>(*md); - BLI_assert(dist_a != 0 && dist_b != 0); - if (dist_a == 1 || dist_b == 1) { - BLI_assert(dist_a != dist_b); - BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED)); - } - else { - WeldLoop *wl_tmp = nullptr; - if (dist_a == 2) { - wl_tmp = wlb_prev; - BLI_assert(wla->flag != ELEM_COLLAPSED); - BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); - wla->flag = ELEM_COLLAPSED; - wl_tmp->flag = ELEM_COLLAPSED; - loop_kill += 2; - poly_len -= 2; - } - if (dist_b == 2) { - if (wl_tmp != nullptr) { - r_wp->flag = ELEM_COLLAPSED; - *r_poly_kill += 1; - } - else { - wl_tmp = wla_prev; - BLI_assert(wlb->flag != ELEM_COLLAPSED); - BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); - wlb->flag = ELEM_COLLAPSED; - wl_tmp->flag = ELEM_COLLAPSED; - } - loop_kill += 2; - poly_len -= 2; - } - if (wl_tmp == nullptr) { - const int new_loops_len = lb - la; - const int new_loops_ofs = ctx_loops_ofs + la; - - WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++]; - new_wp->poly_dst = OUT_OF_CONTEXT; - new_wp->poly_orig = r_wp->poly_orig; - new_wp->loops.len = new_loops_len; - new_wp->loops.ofs = new_loops_ofs; - new_wp->loop_start = wla->loop_orig; - new_wp->loop_end = wlb_prev->loop_orig; - new_wp->len = dist_a; - weld_poly_split_recursive(vert_dest_map, -#ifdef USE_WELD_DEBUG - mloop, -#endif - ctx_verts_len, - new_wp, - r_weld_mesh, - r_poly_kill, - r_loop_kill); - BLI_assert(dist_b == poly_len - dist_a); - poly_len = dist_b; - if (wla_prev->loop_orig > wla->loop_orig) { - /* New start. */ - r_wp->loop_start = wlb->loop_orig; - } - else { - /* The `loop_start` doesn't change but some loops must be skipped. */ - wla_prev->loop_skip_to = wlb->loop_orig; - } - wla = wlb; - la = lb; - goto wa_continue; - } - break; - } - } - if (wlb->flag != ELEM_COLLAPSED) { - wlb_prev = wlb; - } - } - } - if (wla->flag != ELEM_COLLAPSED) { - wla_prev = wla; - } - } - r_wp->len = poly_len; - *r_loop_kill += loop_kill; - -#ifdef USE_WELD_DEBUG - weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map); -#endif - } -} - -static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, -#ifdef USE_WELD_DEBUG - Span<MPoly> mpoly, -#endif - const int mvert_len, - Span<int> vert_dest_map, - const int remain_edge_ctx_len, - MutableSpan<WeldGroup> r_vlinks, - WeldMesh *r_weld_mesh) -{ - MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly; - MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; - WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; - int wpoly_len = r_weld_mesh->wpoly_len; - int wpoly_new_len = 0; - int poly_kill_len = 0; - int loop_kill_len = 0; - - Span<int> loop_map = r_weld_mesh->loop_map; - - if (remain_edge_ctx_len) { - - /* Setup Poly/Loop. Note that `wpoly_len` may be different than `wpoly.size()` here. */ - for (const int i : IndexRange(wpoly_len)) { - WeldPoly &wp = wpoly[i]; - const int ctx_loops_len = wp.loops.len; - const int ctx_loops_ofs = wp.loops.ofs; - - int poly_len = wp.len; - int ctx_verts_len = 0; - WeldLoop *wl = &wloop[ctx_loops_ofs]; - for (int l = ctx_loops_len; l--; wl++) { - const int edge_dest = wl->edge; - if (edge_dest == ELEM_COLLAPSED) { - wl->flag = ELEM_COLLAPSED; - if (poly_len == 3) { - wp.flag = ELEM_COLLAPSED; - poly_kill_len++; - loop_kill_len += 3; - poly_len = 0; - break; - } - loop_kill_len++; - poly_len--; - } - else { - const int vert_dst = wl->vert; - if (vert_dest_map[vert_dst] != OUT_OF_CONTEXT) { - ctx_verts_len++; - } - } - } - - if (poly_len) { - wp.len = poly_len; -#ifdef USE_WELD_DEBUG - weld_assert_poly_len(wp, wloop); -#endif - - weld_poly_split_recursive(vert_dest_map, -#ifdef USE_WELD_DEBUG - mloop, -#endif - ctx_verts_len, - &wp, - r_weld_mesh, - &poly_kill_len, - &loop_kill_len); - - wpoly_new_len = r_weld_mesh->wpoly_new_len; - } - } - -#ifdef USE_WELD_DEBUG - weld_assert_poly_and_loop_kill_len(wpoly, - {wpoly_new, wpoly_new_len}, - wloop, - mloop, - loop_map, - r_weld_mesh->poly_map, - mpoly, - poly_kill_len, - loop_kill_len); -#endif - - /* Setup Polygon Overlap. */ - - const int wpoly_and_new_len = wpoly_len + wpoly_new_len; - - r_vlinks.fill({0, 0}); - MutableSpan<WeldGroup> v_links = r_vlinks; - - for (const int i : IndexRange(wpoly_and_new_len)) { - const WeldPoly &wp = wpoly[i]; - WeldLoopOfPolyIter iter; - if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { - while (weld_iter_loop_of_poly_next(iter)) { - v_links[iter.v].len++; - } - } - } - - int link_len = 0; - for (const int i : IndexRange(mvert_len)) { - v_links[i].ofs = link_len; - link_len += v_links[i].len; - } - - if (link_len) { - Array<int> link_poly_buffer(link_len); - - for (const int i : IndexRange(wpoly_and_new_len)) { - const WeldPoly &wp = wpoly[i]; - WeldLoopOfPolyIter iter; - if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { - while (weld_iter_loop_of_poly_next(iter)) { - link_poly_buffer[v_links[iter.v].ofs++] = i; - } - } - } - - for (WeldGroup &vl : r_vlinks) { - /* Fix offset */ - vl.ofs -= vl.len; - } - - int polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b; - polys_len_b = p_ctx_b = 0; /* silence warnings */ - - for (const int i : IndexRange(wpoly_and_new_len)) { - const WeldPoly &wp = wpoly[i]; - if (wp.poly_dst != OUT_OF_CONTEXT) { - /* No need to retest poly. - * (Already includes collapsed polygons). */ - continue; - } - - WeldLoopOfPolyIter iter; - weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr); - weld_iter_loop_of_poly_next(iter); - struct WeldGroup *link_a = &v_links[iter.v]; - polys_len_a = link_a->len; - if (polys_len_a == 1) { - BLI_assert(link_poly_buffer[link_a->ofs] == i); - continue; - } - int wp_len = wp.len; - polys_ctx_a = &link_poly_buffer[link_a->ofs]; - for (; polys_len_a--; polys_ctx_a++) { - p_ctx_a = *polys_ctx_a; - if (p_ctx_a == i) { - continue; - } - - WeldPoly *wp_tmp = &wpoly[p_ctx_a]; - if (wp_tmp->len != wp_len) { - continue; - } - - WeldLoopOfPolyIter iter_b = iter; - while (weld_iter_loop_of_poly_next(iter_b)) { - struct WeldGroup *link_b = &v_links[iter_b.v]; - polys_len_b = link_b->len; - if (polys_len_b == 1) { - BLI_assert(link_poly_buffer[link_b->ofs] == i); - polys_len_b = 0; - break; - } - - polys_ctx_b = &link_poly_buffer[link_b->ofs]; - for (; polys_len_b; polys_len_b--, polys_ctx_b++) { - p_ctx_b = *polys_ctx_b; - if (p_ctx_b < p_ctx_a) { - continue; - } - if (p_ctx_b >= p_ctx_a) { - if (p_ctx_b > p_ctx_a) { - polys_len_b = 0; - } - break; - } - } - if (polys_len_b == 0) { - break; - } - } - if (polys_len_b == 0) { - continue; - } - BLI_assert(p_ctx_a > i); - BLI_assert(p_ctx_a == p_ctx_b); - BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT); - BLI_assert(wp_tmp != &wp); - wp_tmp->poly_dst = wp.poly_orig; - loop_kill_len += wp_tmp->len; - poly_kill_len++; - } - } - } - } - else { - poly_kill_len = r_weld_mesh->wpoly_len; - loop_kill_len = r_weld_mesh->wloop_len; - - for (WeldPoly &wp : wpoly) { - wp.flag = ELEM_COLLAPSED; - } - } - -#ifdef USE_WELD_DEBUG - weld_assert_poly_and_loop_kill_len(wpoly, - {wpoly_new, wpoly_new_len}, - wloop, - mloop, - loop_map, - r_weld_mesh->poly_map, - mpoly, - poly_kill_len, - loop_kill_len); -#endif - - r_weld_mesh->wpoly_new = wpoly_new; - r_weld_mesh->poly_kill_len = poly_kill_len; - r_weld_mesh->loop_kill_len = loop_kill_len; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Mesh API - * \{ */ - -static void weld_mesh_context_create(const Mesh &mesh, - MutableSpan<int> vert_dest_map, - const int vert_kill_len, - WeldMesh *r_weld_mesh) -{ - Span<MEdge> medge{mesh.medge, mesh.totedge}; - Span<MPoly> mpoly{mesh.mpoly, mesh.totpoly}; - Span<MLoop> mloop{mesh.mloop, mesh.totloop}; - const int mvert_len = mesh.totvert; - - Vector<WeldVert> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map, vert_kill_len); - r_weld_mesh->vert_kill_len = vert_kill_len; - - Array<int> edge_dest_map(medge.size()); - Array<int> edge_ctx_map(medge.size()); - Vector<WeldEdge> wedge = weld_edge_ctx_alloc(medge, vert_dest_map, edge_dest_map, edge_ctx_map); - - Array<WeldGroup> v_links(mvert_len, {0, 0}); - weld_edge_ctx_setup(v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len); - - weld_poly_loop_ctx_alloc(mpoly, mloop, vert_dest_map, edge_dest_map, r_weld_mesh); - - weld_poly_loop_ctx_setup(mloop, -#ifdef USE_WELD_DEBUG - mpoly, - -#endif - mvert_len, - vert_dest_map, - wedge.size() - r_weld_mesh->edge_kill_len, - v_links, - r_weld_mesh); - - weld_vert_groups_setup(wvert, - vert_dest_map, - vert_dest_map, - r_weld_mesh->vert_groups_buffer, - r_weld_mesh->vert_groups); - - weld_edge_groups_setup(medge.size(), - r_weld_mesh->edge_kill_len, - wedge, - edge_ctx_map, - edge_dest_map, - r_weld_mesh->edge_groups_buffer, - r_weld_mesh->edge_groups); - - r_weld_mesh->edge_groups_map = std::move(edge_dest_map); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld CustomData - * \{ */ - -static void customdata_weld( - const CustomData *source, CustomData *dest, const int *src_indices, int count, int dest_index) -{ - if (count == 1) { - CustomData_copy_data(source, dest, src_indices[0], dest_index, 1); - return; - } - - CustomData_interp(source, dest, (const int *)src_indices, nullptr, nullptr, count, dest_index); - - int src_i, dest_i; - int j; - - float co[3] = {0.0f, 0.0f, 0.0f}; -#ifdef USE_WELD_NORMALS - float no[3] = {0.0f, 0.0f, 0.0f}; -#endif - int crease = 0; - int bweight = 0; - short flag = 0; - - /* interpolates a layer at a time */ - dest_i = 0; - for (src_i = 0; src_i < source->totlayer; src_i++) { - const int type = source->layers[src_i].type; - - /* find the first dest layer with type >= the source type - * (this should work because layers are ordered by type) - */ - while (dest_i < dest->totlayer && dest->layers[dest_i].type < type) { - dest_i++; - } - - /* if there are no more dest layers, we're done */ - if (dest_i == dest->totlayer) { - break; - } - - /* if we found a matching layer, add the data */ - if (dest->layers[dest_i].type == type) { - void *src_data = source->layers[src_i].data; - - if (type == CD_MVERT) { - for (j = 0; j < count; j++) { - MVert *mv_src = &((MVert *)src_data)[src_indices[j]]; - add_v3_v3(co, mv_src->co); -#ifdef USE_WELD_NORMALS - short *mv_src_no = mv_src->no; - no[0] += mv_src_no[0]; - no[1] += mv_src_no[1]; - no[2] += mv_src_no[2]; -#endif - bweight += mv_src->bweight; - flag |= mv_src->flag; - } - } - else if (type == CD_MEDGE) { - for (j = 0; j < count; j++) { - MEdge *me_src = &((MEdge *)src_data)[src_indices[j]]; - crease += me_src->crease; - bweight += me_src->bweight; - flag |= me_src->flag; - } - } - else if (CustomData_layer_has_interp(dest, dest_i)) { - /* Already calculated. - * TODO: Optimize by exposing `typeInfo->interp`. */ - } - else if (CustomData_layer_has_math(dest, dest_i)) { - const int size = CustomData_sizeof(type); - void *dst_data = dest->layers[dest_i].data; - void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); - for (j = 0; j < count; j++) { - CustomData_data_add( - type, v_dst, POINTER_OFFSET(src_data, (size_t)src_indices[j] * size)); - } - } - else { - CustomData_copy_layer_type_data(source, dest, type, src_indices[0], dest_index, 1); - } - - /* if there are multiple source & dest layers of the same type, - * we don't want to copy all source layers to the same dest, so - * increment dest_i - */ - dest_i++; - } - } - - float fac = 1.0f / count; - - for (dest_i = 0; dest_i < dest->totlayer; dest_i++) { - CustomDataLayer *layer_dst = &dest->layers[dest_i]; - const int type = layer_dst->type; - if (type == CD_MVERT) { - MVert *mv = &((MVert *)layer_dst->data)[dest_index]; - mul_v3_fl(co, fac); - bweight *= fac; - CLAMP_MAX(bweight, 255); - - copy_v3_v3(mv->co, co); -#ifdef USE_WELD_NORMALS - mul_v3_fl(no, fac); - short *mv_no = mv->no; - mv_no[0] = (short)no[0]; - mv_no[1] = (short)no[1]; - mv_no[2] = (short)no[2]; -#endif - - mv->flag = (char)flag; - mv->bweight = (char)bweight; - } - else if (type == CD_MEDGE) { - MEdge *me = &((MEdge *)layer_dst->data)[dest_index]; - crease *= fac; - bweight *= fac; - CLAMP_MAX(crease, 255); - CLAMP_MAX(bweight, 255); - - me->crease = (char)crease; - me->bweight = (char)bweight; - me->flag = flag; - } - else if (CustomData_layer_has_interp(dest, dest_i)) { - /* Already calculated. */ - } - else if (CustomData_layer_has_math(dest, dest_i)) { - const int size = CustomData_sizeof(type); - void *dst_data = layer_dst->data; - void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); - CustomData_data_multiply(type, v_dst, fac); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Modifier Main - * \{ */ - -#ifdef USE_BVHTREEKDOP -struct WeldOverlapData { - const MVert *mvert; - float merge_dist_sq; -}; -static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) -{ - if (index_a < index_b) { - struct WeldOverlapData *data = userdata; - const MVert *mvert = data->mvert; - const float dist_sq = len_squared_v3v3(mvert[index_a].co, mvert[index_b].co); - BLI_assert(dist_sq <= ((data->merge_dist_sq + FLT_EPSILON) * 3)); - return dist_sq <= data->merge_dist_sq; - } - return false; -} -#endif - -/** Use for #MOD_WELD_MODE_CONNECTED calculation. */ -struct WeldVertexCluster { - float co[3]; - int merged_verts; -}; - -static Mesh *weldModifier_doWeld(WeldModifierData *wmd, - const ModifierEvalContext *UNUSED(ctx), - Mesh *mesh) -{ - BLI_bitmap *v_mask = nullptr; - int v_mask_act = 0; - - Span<MVert> mvert{mesh->mvert, mesh->totvert}; - Span<MEdge> medge{mesh->medge, mesh->totedge}; - Span<MPoly> mpoly{mesh->mpoly, mesh->totpoly}; - Span<MLoop> mloop{mesh->mloop, mesh->totloop}; - const int totvert = mesh->totvert; - const int totedge = mesh->totedge; - const int totloop = mesh->totloop; - const int totpoly = mesh->totpoly; - - /* Vertex Group. */ - const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); - if (defgrp_index != -1) { - MDeformVert *dvert; - dvert = static_cast<MDeformVert *>(CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT)); - if (dvert) { - const bool invert_vgroup = (wmd->flag & MOD_WELD_INVERT_VGROUP) != 0; - v_mask = BLI_BITMAP_NEW(totvert, __func__); - for (const int i : IndexRange(totvert)) { - const bool found = BKE_defvert_find_weight(&dvert[i], defgrp_index) > 0.0f; - if (found != invert_vgroup) { - BLI_BITMAP_ENABLE(v_mask, i); - v_mask_act++; - } - } - } - } - - /* From the original index of the vertex. - * This indicates which vert it is or is going to be merged. */ - Array<int> vert_dest_map(totvert); - int vert_kill_len = 0; - if (wmd->mode == MOD_WELD_MODE_ALL) -#ifdef USE_BVHTREEKDOP - { - /* Get overlap map. */ - struct BVHTreeFromMesh treedata; - BVHTree *bvhtree = bvhtree_from_mesh_verts_ex(&treedata, - mvert.data(), - totvert, - false, - v_mask, - v_mask_act, - wmd->merge_dist / 2, - 2, - 6, - 0, - nullptr, - nullptr); - - if (bvhtree) { - struct WeldOverlapData data; - data.mvert = mvert; - data.merge_dist_sq = square_f(wmd->merge_dist); - - uint overlap_len; - BVHTreeOverlap *overlap = BLI_bvhtree_overlap_ex(bvhtree, - bvhtree, - &overlap_len, - bvhtree_weld_overlap_cb, - &data, - 1, - BVH_OVERLAP_RETURN_PAIRS); - - free_bvhtree_from_mesh(&treedata); - if (overlap) { - range_vn_i(vert_dest_map.data(), totvert, 0); - - const BVHTreeOverlap *overlap_iter = &overlap[0]; - for (int i = 0; i < overlap_len; i++, overlap_iter++) { - int indexA = overlap_iter->indexA; - int indexB = overlap_iter->indexB; - - BLI_assert(indexA < indexB); - - int va_dst = vert_dest_map[indexA]; - while (va_dst != vert_dest_map[va_dst]) { - va_dst = vert_dest_map[va_dst]; - } - int vb_dst = vert_dest_map[indexB]; - while (vb_dst != vert_dest_map[vb_dst]) { - vb_dst = vert_dest_map[vb_dst]; - } - if (va_dst == vb_dst) { - continue; - } - if (va_dst > vb_dst) { - SWAP(int, va_dst, vb_dst); - } - vert_kill_len++; - vert_dest_map[vb_dst] = va_dst; - } - - /* Fix #r_vert_dest_map for next step. */ - for (int i = 0; i < totvert; i++) { - if (i == vert_dest_map[i]) { - vert_dest_map[i] = OUT_OF_CONTEXT; - } - else { - int v = i; - while (v != vert_dest_map[v] && vert_dest_map[v] != OUT_OF_CONTEXT) { - v = vert_dest_map[v]; - } - vert_dest_map[v] = v; - vert_dest_map[i] = v; - } - } - - MEM_freeN(overlap); - } - } - } -#else - { - KDTree_3d *tree = BLI_kdtree_3d_new(v_mask ? v_mask_act : totvert); - for (const int i : IndexRange(totvert)) { - if (!v_mask || BLI_BITMAP_TEST(v_mask, i)) { - BLI_kdtree_3d_insert(tree, i, mvert[i].co); - } - vert_dest_map[i] = OUT_OF_CONTEXT; - } - - BLI_kdtree_3d_balance(tree); - vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast( - tree, wmd->merge_dist, false, vert_dest_map.data()); - BLI_kdtree_3d_free(tree); - } -#endif - else { - BLI_assert(wmd->mode == MOD_WELD_MODE_CONNECTED); - - Array<WeldVertexCluster> vert_clusters(totvert); - - for (const int i : mvert.index_range()) { - WeldVertexCluster &vc = vert_clusters[i]; - copy_v3_v3(vc.co, mvert[i].co); - vc.merged_verts = 0; - } - const float merge_dist_sq = square_f(wmd->merge_dist); - - range_vn_i(vert_dest_map.data(), totvert, 0); - - /* Collapse Edges that are shorter than the threshold. */ - for (const int i : medge.index_range()) { - int v1 = medge[i].v1; - int v2 = medge[i].v2; - - if (wmd->flag & MOD_WELD_LOOSE_EDGES && (medge[i].flag & ME_LOOSEEDGE) == 0) { - continue; - } - while (v1 != vert_dest_map[v1]) { - v1 = vert_dest_map[v1]; - } - while (v2 != vert_dest_map[v2]) { - v2 = vert_dest_map[v2]; - } - if (v1 == v2) { - continue; - } - if (v_mask && (!BLI_BITMAP_TEST(v_mask, v1) || !BLI_BITMAP_TEST(v_mask, v2))) { - continue; - } - if (v1 > v2) { - SWAP(int, v1, v2); - } - WeldVertexCluster *v1_cluster = &vert_clusters[v1]; - WeldVertexCluster *v2_cluster = &vert_clusters[v2]; - - float edgedir[3]; - sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co); - const float dist_sq = len_squared_v3(edgedir); - if (dist_sq <= merge_dist_sq) { - float influence = (v2_cluster->merged_verts + 1) / - (float)(v1_cluster->merged_verts + v2_cluster->merged_verts + 2); - madd_v3_v3fl(v1_cluster->co, edgedir, influence); - - v1_cluster->merged_verts += v2_cluster->merged_verts + 1; - vert_dest_map[v2] = v1; - vert_kill_len++; - } - } - - for (const int i : IndexRange(totvert)) { - if (i == vert_dest_map[i]) { - vert_dest_map[i] = OUT_OF_CONTEXT; - } - else { - int v = i; - while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) { - v = vert_dest_map[v]; - } - vert_dest_map[v] = v; - vert_dest_map[i] = v; - } - } - } - - if (v_mask) { - MEM_freeN(v_mask); - } - - if (vert_kill_len == 0) { + std::optional<Mesh *> result = calculate_weld(*mesh, wmd); + if (!result) { return mesh; } - - WeldMesh weld_mesh; - weld_mesh_context_create(*mesh, vert_dest_map, vert_kill_len, &weld_mesh); - - const int result_nverts = totvert - weld_mesh.vert_kill_len; - const int result_nedges = totedge - weld_mesh.edge_kill_len; - const int result_nloops = totloop - weld_mesh.loop_kill_len; - const int result_npolys = totpoly - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len; - - Mesh *result = BKE_mesh_new_nomain_from_template( - mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys); - - /* Vertices. */ - - /* Be careful when editing this array, to avoid new allocations it uses the same buffer as - * #vert_dest_map. This map will be used to adjust the edges, polys and loops. */ - MutableSpan<int> vert_final = vert_dest_map; - - int dest_index = 0; - for (int i = 0; i < totvert; i++) { - int source_index = i; - int count = 0; - while (i < totvert && vert_dest_map[i] == OUT_OF_CONTEXT) { - vert_final[i] = dest_index + count; - count++; - i++; - } - if (count) { - CustomData_copy_data(&mesh->vdata, &result->vdata, source_index, dest_index, count); - dest_index += count; - } - if (i == totvert) { - break; - } - if (vert_dest_map[i] != ELEM_MERGED) { - struct WeldGroup *wgroup = &weld_mesh.vert_groups[vert_dest_map[i]]; - customdata_weld(&mesh->vdata, - &result->vdata, - &weld_mesh.vert_groups_buffer[wgroup->ofs], - wgroup->len, - dest_index); - vert_final[i] = dest_index; - dest_index++; - } - } - - BLI_assert(dest_index == result_nverts); - - /* Edges. */ - - /* Be careful when editing this array, to avoid new allocations it uses the same buffer as - * #edge_groups_map. This map will be used to adjust the polys and loops. */ - MutableSpan<int> edge_final = weld_mesh.edge_groups_map; - - dest_index = 0; - for (int i = 0; i < totedge; i++) { - const int source_index = i; - int count = 0; - while (i < totedge && weld_mesh.edge_groups_map[i] == OUT_OF_CONTEXT) { - edge_final[i] = dest_index + count; - count++; - i++; - } - if (count) { - CustomData_copy_data(&mesh->edata, &result->edata, source_index, dest_index, count); - MEdge *me = &result->medge[dest_index]; - dest_index += count; - for (; count--; me++) { - me->v1 = vert_final[me->v1]; - me->v2 = vert_final[me->v2]; - } - } - if (i == totedge) { - break; - } - if (weld_mesh.edge_groups_map[i] != ELEM_MERGED) { - struct WeldGroupEdge *wegrp = &weld_mesh.edge_groups[weld_mesh.edge_groups_map[i]]; - customdata_weld(&mesh->edata, - &result->edata, - &weld_mesh.edge_groups_buffer[wegrp->group.ofs], - wegrp->group.len, - dest_index); - MEdge *me = &result->medge[dest_index]; - me->v1 = vert_final[wegrp->v1]; - me->v2 = vert_final[wegrp->v2]; - me->flag |= ME_LOOSEEDGE; - - edge_final[i] = dest_index; - dest_index++; - } - } - - BLI_assert(dest_index == result_nedges); - - /* Polys/Loops. */ - - MPoly *r_mp = &result->mpoly[0]; - MLoop *r_ml = &result->mloop[0]; - int r_i = 0; - int loop_cur = 0; - Array<int, 64> group_buffer(weld_mesh.max_poly_len); - for (const int i : mpoly.index_range()) { - const MPoly &mp = mpoly[i]; - const int loop_start = loop_cur; - const int poly_ctx = weld_mesh.poly_map[i]; - if (poly_ctx == OUT_OF_CONTEXT) { - int mp_loop_len = mp.totloop; - CustomData_copy_data(&mesh->ldata, &result->ldata, mp.loopstart, loop_cur, mp_loop_len); - loop_cur += mp_loop_len; - for (; mp_loop_len--; r_ml++) { - r_ml->v = vert_final[r_ml->v]; - r_ml->e = edge_final[r_ml->e]; - } - } - else { - const WeldPoly &wp = weld_mesh.wpoly[poly_ctx]; - WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin( - iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { - continue; - } - - if (wp.poly_dst != OUT_OF_CONTEXT) { - continue; - } - while (weld_iter_loop_of_poly_next(iter)) { - customdata_weld( - &mesh->ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur); - int v = vert_final[iter.v]; - int e = edge_final[iter.e]; - r_ml->v = v; - r_ml->e = e; - r_ml++; - loop_cur++; - if (iter.type) { - result->medge[e].flag &= ~ME_LOOSEEDGE; - } - BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0); - } - } - - CustomData_copy_data(&mesh->pdata, &result->pdata, i, r_i, 1); - r_mp->loopstart = loop_start; - r_mp->totloop = loop_cur - loop_start; - r_mp++; - r_i++; - } - - for (const int i : IndexRange(weld_mesh.wpoly_new_len)) { - const WeldPoly &wp = weld_mesh.wpoly_new[i]; - const int loop_start = loop_cur; - WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin( - iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { - continue; - } - - if (wp.poly_dst != OUT_OF_CONTEXT) { - continue; - } - while (weld_iter_loop_of_poly_next(iter)) { - customdata_weld(&mesh->ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur); - int v = vert_final[iter.v]; - int e = edge_final[iter.e]; - r_ml->v = v; - r_ml->e = e; - r_ml++; - loop_cur++; - if (iter.type) { - result->medge[e].flag &= ~ME_LOOSEEDGE; - } - BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0); - } - - r_mp->loopstart = loop_start; - r_mp->totloop = loop_cur - loop_start; - r_mp++; - r_i++; - } - - BLI_assert((int)r_i == result_npolys); - BLI_assert(loop_cur == result_nloops); - - /* We could only update the normals of the elements in context, but the next modifier can make it - * dirty anyway which would make the work useless. */ - BKE_mesh_normals_tag_dirty(result); - - return result; -} - -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) -{ - WeldModifierData *wmd = (WeldModifierData *)md; - return weldModifier_doWeld(wmd, ctx, mesh); + return *result; } static void initData(ModifierData *md) @@ -1943,7 +218,6 @@ ModifierTypeInfo modifierType_Weld = { /* deformVertsEM */ nullptr, /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, - /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index 706960182cf..72bc4336eff 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -194,7 +194,6 @@ ModifierTypeInfo modifierType_Wireframe = { /* deformVertsEM */ NULL, /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, /* initData */ initData, diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 402a71af27f..43fb90a52cd 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -133,10 +133,6 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_TBB) list(APPEND INC_SYS ${TBB_INCLUDE_DIRS} diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index c80d0bcdd1e..71acc738472 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -140,6 +140,8 @@ void register_node_type_cmp_pixelate(void); void register_node_type_cmp_trackpos(void); void register_node_type_cmp_planetrackdeform(void); void register_node_type_cmp_cornerpin(void); +void register_node_type_cmp_separate_xyz(void); +void register_node_type_cmp_combine_xyz(void); void node_cmp_rlayers_outputs(struct bNodeTree *ntree, struct bNode *node); void node_cmp_rlayers_register_pass(struct bNodeTree *ntree, @@ -152,6 +154,75 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index); void register_node_type_cmp_custom_group(bNodeType *ntype); +void ntreeCompositExecTree(struct Scene *scene, + struct bNodeTree *ntree, + struct RenderData *rd, + int rendering, + int do_previews, + const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings, + const char *view_name); + +/** + * Called from render pipeline, to tag render input and output. + * need to do all scenes, to prevent errors when you re-render 1 scene. + */ +void ntreeCompositTagRender(struct Scene *scene); + +/** + * Update the outputs of the render layer nodes. + * Since the outputs depend on the render engine, this part is a bit complex: + * - #ntreeCompositUpdateRLayers is called and loops over all render layer nodes. + * - Each render layer node calls the update function of the + * render engine that's used for its scene. + * - The render engine calls RE_engine_register_pass for each pass. + * - #RE_engine_register_pass calls #node_cmp_rlayers_register_pass. + */ +void ntreeCompositUpdateRLayers(struct bNodeTree *ntree); + +void ntreeCompositClearTags(struct bNodeTree *ntree); + +struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree, + struct bNode *node, + const char *name, + struct ImageFormatData *im_format); + +int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node); +void ntreeCompositOutputFileSetPath(struct bNode *node, + struct bNodeSocket *sock, + const char *name); +void ntreeCompositOutputFileSetLayer(struct bNode *node, + struct bNodeSocket *sock, + const char *name); +/* needed in do_versions */ +void ntreeCompositOutputFileUniquePath(struct ListBase *list, + struct bNodeSocket *sock, + const char defname[], + char delim); +void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, + struct bNodeSocket *sock, + const char defname[], + char delim); + +void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); +void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); + +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); +void ntreeCompositCryptomatteSyncFromRemove(bNode *node); +bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); +int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len); + +/** + * Update the runtime layer names with the crypto-matte layer names of the references render layer + * or image. + */ +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); +struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index e5c005f8c95..609d92c09df 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -132,6 +132,7 @@ void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_replace(void); void register_node_type_geo_material_selection(void); +void register_node_type_geo_merge_by_distance(void); void register_node_type_geo_mesh_primitive_circle(void); void register_node_type_geo_mesh_primitive_cone(void); void register_node_type_geo_mesh_primitive_cube(void); diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 76c174201e8..57740ce3ad9 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -85,6 +85,7 @@ void register_node_type_sh_layer_weight(void); void register_node_type_sh_tex_coord(void); void register_node_type_sh_particle_info(void); void register_node_type_sh_hair_info(void); +void register_node_type_sh_point_info(void); void register_node_type_sh_volume_info(void); void register_node_type_sh_script(void); void register_node_type_sh_normal_map(void); @@ -143,6 +144,27 @@ void register_node_type_sh_tex_white_noise(void); void register_node_type_sh_custom_group(bNodeType *ntype); +struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree); +void ntreeShaderEndExecTree(struct bNodeTreeExec *exec); + +/** + * Find an output node of the shader tree. + * + * \note it will only return output which is NOT in the group, which isn't how + * render engines works but it's how the GPU shader compilation works. This we + * can change in the future and make it a generic function, but for now it stays + * private here. + */ +struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target); + +/** + * This one needs to work on a local tree. + */ +void ntreeGPUMaterialNodes(struct bNodeTree *localtree, + struct GPUMaterial *mat, + bool *has_surface_output, + bool *has_volume_output); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 66a2d3720ca..8fd71a978e3 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -96,6 +96,7 @@ DefNode(ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIG DefNode(ShaderNode, SH_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "" ) DefNode(ShaderNode, SH_NODE_PARTICLE_INFO, 0, "PARTICLE_INFO", ParticleInfo, "Particle Info", "" ) DefNode(ShaderNode, SH_NODE_HAIR_INFO, 0, "HAIR_INFO", HairInfo, "Hair Info", "" ) +DefNode(ShaderNode, SH_NODE_POINT_INFO, 0, "POINT_INFO", PointInfo, "Point Info", "" ) DefNode(ShaderNode, SH_NODE_VOLUME_INFO, 0, "VOLUME_INFO", VolumeInfo, "Volume Info", "" ) DefNode(ShaderNode, SH_NODE_WIREFRAME, def_sh_tex_wireframe, "WIREFRAME", Wireframe, "Wireframe", "" ) DefNode(ShaderNode, SH_NODE_WAVELENGTH, 0, "WAVELENGTH", Wavelength, "Wavelength", "" ) @@ -229,6 +230,8 @@ DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIAL DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" ) DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space","" ) DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" ) +DefNode(CompositorNode, CMP_NODE_COMBINE_XYZ, 0, "COMBINE_XYZ", CombineXYZ, "Combine XYZ", "" ) +DefNode(CompositorNode, CMP_NODE_SEPARATE_XYZ, 0, "SEPARATE_XYZ", SeparateXYZ, "Separate XYZ", "" ) DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) @@ -386,6 +389,7 @@ DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", In DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") +DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, 0, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "") DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") diff --git a/source/blender/nodes/NOD_texture.h b/source/blender/nodes/NOD_texture.h index af59fefd925..c08bc814915 100644 --- a/source/blender/nodes/NOD_texture.h +++ b/source/blender/nodes/NOD_texture.h @@ -74,6 +74,22 @@ void register_node_type_tex_proc_noise(void); void register_node_type_tex_proc_stucci(void); void register_node_type_tex_proc_distnoise(void); +void ntreeTexCheckCyclics(struct bNodeTree *ntree); +struct bNodeTreeExec *ntreeTexBeginExecTree(struct bNodeTree *ntree); +void ntreeTexEndExecTree(struct bNodeTreeExec *exec); +int ntreeTexExecTree(struct bNodeTree *ntree, + struct TexResult *target, + const float co[3], + float dxt[3], + float dyt[3], + int osatex, + short thread, + const struct Tex *tex, + short which_output, + int cfra, + int preview, + struct MTex *mtex); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index 8d2b2befd1a..8581ab7f82f 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -110,6 +110,7 @@ set(SRC nodes/node_composite_sepcomb_rgba.cc nodes/node_composite_sepcomb_ycca.cc nodes/node_composite_sepcomb_yuva.cc + nodes/node_composite_sepcomb_xyz.cc nodes/node_composite_setalpha.cc nodes/node_composite_split_viewer.cc nodes/node_composite_stabilize2d.cc @@ -133,10 +134,6 @@ set(SRC node_composite_util.hh ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_IMAGE_OPENEXR) add_definitions(-DWITH_OPENEXR) endif() diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc new file mode 100644 index 00000000000..5dfcf5dca1e --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc @@ -0,0 +1,71 @@ +/* + * 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 cmpnodes + */ + +#include "node_composite_util.hh" + +/* **************** SEPARATE XYZ ******************** */ +namespace blender::nodes { + +static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f); + b.add_output<decl::Float>("X"); + b.add_output<decl::Float>("Y"); + b.add_output<decl::Float>("Z"); +} + +} // namespace blender::nodes + +void register_node_type_cmp_separate_xyz() +{ + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_XYZ, "Separate XYZ", NODE_CLASS_CONVERTER); + ntype.declare = blender::nodes::cmp_node_separate_xyz_declare; + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE XYZ ******************** */ + +namespace blender::nodes { + +static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("X").min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>("Y").min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>("Z").min(-10000.0f).max(10000.0f); + b.add_output<decl::Vector>("Vector"); +} + +} // namespace blender::nodes + +void register_node_type_cmp_combine_xyz() +{ + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COMBINE_XYZ, "Combine XYZ", NODE_CLASS_CONVERTER); + ntype.declare = blender::nodes::cmp_node_combine_xyz_declare; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/CMakeLists.txt b/source/blender/nodes/function/CMakeLists.txt index 0c3c6a34995..6418c525776 100644 --- a/source/blender/nodes/function/CMakeLists.txt +++ b/source/blender/nodes/function/CMakeLists.txt @@ -63,10 +63,6 @@ set(LIB bf_functions ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_nodes_function "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_UNITY_BUILD) diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index cd05f07d392..9425c4ff328 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -22,6 +22,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_function_util.hh" namespace blender::nodes::node_fn_boolean_math_cc { @@ -43,8 +45,7 @@ static void node_boolean_math_update(bNodeTree *ntree, bNode *node) { bNodeSocket *sockB = (bNodeSocket *)BLI_findlink(&node->inputs, 1); - nodeSetSocketAvailability( - ntree, sockB, ELEM(node->custom1, NODE_BOOLEAN_MATH_AND, NODE_BOOLEAN_MATH_OR)); + nodeSetSocketAvailability(ntree, sockB, !ELEM(node->custom1, NODE_BOOLEAN_MATH_NOT)); } static void node_boolean_math_label(const bNodeTree *UNUSED(ntree), @@ -60,6 +61,27 @@ static void node_boolean_math_label(const bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + if (!params.node_tree().typeinfo->validate_link( + static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_BOOLEAN)) { + return; + } + + for (const EnumPropertyItem *item = rna_enum_node_boolean_math_items; + item->identifier != nullptr; + item++) { + if (item->name != nullptr && item->identifier[0] != '\0') { + NodeBooleanMathOperation operation = static_cast<NodeBooleanMathOperation>(item->value); + params.add_item(IFACE_(item->name), [operation](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("FunctionNodeBooleanMath"); + node.custom1 = operation; + params.update_and_connect_available_socket(node, "Boolean"); + }); + } + } +} + static const fn::MultiFunction *get_multi_function(bNode &bnode) { static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{"And", @@ -67,6 +89,18 @@ static const fn::MultiFunction *get_multi_function(bNode &bnode) static fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{"Or", [](bool a, bool b) { return a || b; }}; static fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nand_fn{"Not And", + [](bool a, bool b) { return !(a && b); }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nor_fn{"Nor", + [](bool a, bool b) { return !(a || b); }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> xnor_fn{"Equal", + [](bool a, bool b) { return a == b; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> xor_fn{"Not Equal", + [](bool a, bool b) { return a != b; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> imply_fn{"Imply", + [](bool a, bool b) { return !a || b; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nimply_fn{"Subtract", + [](bool a, bool b) { return a && !b; }}; switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: @@ -75,6 +109,18 @@ static const fn::MultiFunction *get_multi_function(bNode &bnode) return &or_fn; case NODE_BOOLEAN_MATH_NOT: return ¬_fn; + case NODE_BOOLEAN_MATH_NAND: + return &nand_fn; + case NODE_BOOLEAN_MATH_NOR: + return &nor_fn; + case NODE_BOOLEAN_MATH_XNOR: + return &xnor_fn; + case NODE_BOOLEAN_MATH_XOR: + return &xor_fn; + case NODE_BOOLEAN_MATH_IMPLY: + return &imply_fn; + case NODE_BOOLEAN_MATH_NIMPLY: + return &nimply_fn; } BLI_assert_unreachable(); @@ -101,5 +147,6 @@ void register_node_type_fn_boolean_math() node_type_update(&ntype, file_ns::node_boolean_math_update); ntype.build_multi_function = file_ns::fn_node_boolean_math_build_multi_function; ntype.draw_buttons = file_ns::fn_node_boolean_math_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 0e5f90b58bf..2a8faf65a6d 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -150,6 +150,7 @@ set(SRC nodes/node_geo_join_geometry.cc nodes/node_geo_material_replace.cc nodes/node_geo_material_selection.cc + nodes/node_geo_merge_by_distance.cc nodes/node_geo_mesh_primitive_circle.cc nodes/node_geo_mesh_primitive_cone.cc nodes/node_geo_mesh_primitive_cube.cc @@ -238,10 +239,6 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_TBB) list(APPEND INC_SYS ${TBB_INCLUDE_DIRS} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc index ae034d152be..a71431bdf8e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc @@ -93,7 +93,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */ const float3 remapped_position = position * 2.0f - float3(1.0f); BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false); - colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta}; + copy_v4_v4(colors[i], texture_result.trgba); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 68b609f8045..81421609dfd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -44,7 +44,9 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PropertySubType::PROP_DISTANCE) .default_value(0.25f) .supports_field(); - b.add_input<decl::Bool>(N_("Limit Radius")); + b.add_input<decl::Bool>(N_("Limit Radius")) + .description( + N_("Limit the maximum value of the radius in order to avoid overlapping fillets")); b.add_output<decl::Geometry>(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index ae282017e0c..22549e59ad2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -30,7 +30,13 @@ namespace blender::nodes::node_geo_curve_subdivide_cc { static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input<decl::Int>(N_("Cuts")).default_value(1).min(0).max(1000).supports_field(); + b.add_input<decl::Int>(N_("Cuts")) + .default_value(1) + .min(0) + .max(1000) + .supports_field() + .description( + N_("The number of control points to create on the segment following each point")); b.add_output<decl::Geometry>(N_("Curve")); } 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 index 19efd4b7508..c0c1244f031 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -282,18 +282,20 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, } if (!data.tangents.is_empty()) { - spline.sample_with_index_factors<float3>( - spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); - for (float3 &tangent : data.tangents) { - tangent = math::normalize(tangent); + Span<float3> src_tangents = spline.evaluated_tangents(); + MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, size); + spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents); + for (float3 &vector : sampled_tangents) { + vector = math::normalize(vector); } } if (!data.normals.is_empty()) { - spline.sample_with_index_factors<float3>( - spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); - for (float3 &normals : data.normals) { - normals = math::normalize(normals); + Span<float3> src_normals = spline.evaluated_normals(); + MutableSpan<float3> sampled_normals = data.normals.slice(offset, size); + spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals); + for (float3 &vector : sampled_normals) { + vector = math::normalize(vector); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc index fbf6b521fd8..b1144b58c37 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -22,7 +22,11 @@ namespace blender::nodes::node_geo_input_curve_handles_cc { static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Bool>(N_("Relative")).default_value(false).supports_field(); + b.add_input<decl::Bool>(N_("Relative")) + .default_value(false) + .supports_field() + .description(N_("Output the handle positions relative to the corresponding control point " + "instead of in the local space of the geometry")); b.add_output<decl::Vector>(N_("Left")).field_source(); b.add_output<decl::Vector>(N_("Right")).field_source(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc index 3fe0588a46d..afe7546f7e5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc @@ -20,7 +20,9 @@ namespace blender::nodes::node_geo_input_id_cc { static void node_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Int>(N_("ID")).field_source(); + b.add_output<decl::Int>(N_("ID")).field_source().description( + N_("The values from the \"id\" attribute on points, or the index if that attribute does not " + "exist")); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc index ddeb3ded511..43c867e0977 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc @@ -27,7 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_output<decl::Int>(N_("Face Count")) .field_source() - .description(N_("Number of faces that contain the edge")); + .description(N_("The number of faces that use each edge as one of their sides")); } class EdgeNeighborCountFieldInput final : public GeometryFieldInput { diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index 68bb93bbb64..f1777c9ebf5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -29,8 +29,8 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_output<decl::Int>(N_("Island Index")) .field_source() - .description(N_("Island indices are based on the order of the lowest-numbered vertex " - "contained in each island")); + .description(N_("The index of the each vertex's island. Indices are based on the " + "lowest vertex index contained in each island")); b.add_output<decl::Int>(N_("Island Count")) .field_source() .description(N_("The total number of mesh islands")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc index 7d79164634d..c2da065cbfc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc @@ -27,7 +27,8 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_output<decl::Int>(N_("Vertex Count")) .field_source() - .description(N_("Vertex count and edge count are equal")); + .description(N_("The number of vertices connected to this vertex with an edge, " + "equal to the number of connected edges")); b.add_output<decl::Int>(N_("Face Count")) .field_source() .description(N_("Number of faces that contain the vertex")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 71256a7f781..06f24113308 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -36,7 +36,8 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("Geometry that is instanced on the points")); b.add_input<decl::Bool>(N_("Pick Instance")) .supports_field() - .description(N_("Place different instances on different points")); + .description(N_("Choose instances from the \"Instance\" input at each point instead of " + "instancing the entire geometry")); b.add_input<decl::Int>(N_("Instance Index")) .implicit_field() .description(N_( diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc new file mode 100644 index 00000000000..3c790079b5b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -0,0 +1,108 @@ +/* + * 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 "GEO_mesh_merge_by_distance.hh" +#include "GEO_point_merge_by_distance.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_merge_by_distance_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")) + .supported_type({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_MESH}); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Distance")).default_value(0.001f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points, + const float merge_distance, + const Field<bool> &selection_field) +{ + const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, src_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + if (selection.is_empty()) { + return nullptr; + } + + return geometry::point_merge_by_distance(src_points, merge_distance, selection); +} + +static std::optional<Mesh *> mesh_merge_by_distance(const MeshComponent &mesh_component, + const float merge_distance, + const Field<bool> &selection_field) +{ + const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, src_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + if (selection.is_empty()) { + return nullptr; + } + + const Mesh &mesh = *mesh_component.get_for_read(); + return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + const Field<bool> selection = params.extract_input<Field<bool>>("Selection"); + const float merge_distance = params.extract_input<float>("Distance"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_pointcloud()) { + PointCloud *result = pointcloud_merge_by_distance( + *geometry_set.get_component_for_read<PointCloudComponent>(), merge_distance, selection); + geometry_set.replace_pointcloud(result); + } + if (geometry_set.has_mesh()) { + std::optional<Mesh *> result = mesh_merge_by_distance( + *geometry_set.get_component_for_read<MeshComponent>(), merge_distance, selection); + if (result) { + geometry_set.replace_mesh(*result); + } + } + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_merge_by_distance_cc + +void register_node_type_geo_merge_by_distance() +{ + namespace file_ns = blender::nodes::node_geo_merge_by_distance_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MERGE_BY_DISTANCE, "Merge by Distance", NODE_CLASS_GEOMETRY); + + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 10c0d61ccb6..c4bd6c6157f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -65,6 +65,8 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::String>(N_("Remainder")).make_available([](bNode &node) { node_storage(node).overflow = GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE; }); + b.add_output<decl::Int>(N_("Line")).field_source(); + b.add_output<decl::Vector>(N_("Pivot Point")).field_source(); } static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) @@ -84,6 +86,7 @@ static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) uiItemR(layout, ptr, "overflow", 0, "", ICON_NONE); uiItemR(layout, ptr, "align_x", 0, "", ICON_NONE); uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE); + uiItemR(layout, ptr, "pivot_mode", 0, IFACE_("Pivot Point"), ICON_NONE); } static void node_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -93,6 +96,7 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW; data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT; data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE; + data->pivot_mode = GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT; node->storage = data; node->id = (ID *)BKE_vfont_builtin_get(); } @@ -115,10 +119,51 @@ static void node_update(bNodeTree *ntree, bNode *node) N_("Text Box Width")); } +static float3 get_pivot_point(GeoNodeExecParams ¶ms, CurveEval &curve) +{ + const NodeGeometryStringToCurves &storage = node_storage(params.node()); + const GeometryNodeStringToCurvesPivotMode pivot_mode = (GeometryNodeStringToCurvesPivotMode) + storage.pivot_mode; + + float3 min(FLT_MAX), max(FLT_MIN); + + /* Check if curve is empty. */ + if (!curve.bounds_min_max(min, max, false)) { + return {0.0f, 0.0f, 0.0f}; + } + + switch (pivot_mode) { + case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_MIDPOINT: + return (min + max) / 2; + case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT: + return float3(min.x, min.y, 0.0f); + case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_CENTER: + return float3((min.x + max.x) / 2, min.y, 0.0f); + case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_RIGHT: + return float3(max.x, min.y, 0.0f); + case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_LEFT: + return float3(min.x, max.y, 0.0f); + case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_CENTER: + return float3((min.x + max.x) / 2, max.y, 0.0f); + case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_RIGHT: + return float3(max.x, max.y, 0.0f); + } + return {0.0f, 0.0f, 0.0f}; +} + struct TextLayout { /* Position of each character. */ Vector<float2> positions; + /* Line number of each character. */ + Array<int> line_numbers; + + /* Map of Pivot point for each character code. */ + Map<int, float3> pivot_points; + + /* UTF-32 Character codes. */ + Vector<char32_t> char_codes; + /* The text that fit into the text box, with newline character sequences replaced. */ std::string text; @@ -212,6 +257,20 @@ static TextLayout get_text_layout(GeoNodeExecParams ¶ms) } } + if (params.output_is_required("Line")) { + layout.line_numbers.reinitialize(layout.positions.size()); + for (const int i : layout.positions.index_range()) { + CharTrans &ct = chartransdata[i]; + layout.line_numbers[i] = ct.linenr; + } + } + + /* Convert UTF-8 encoded string to UTF-32. */ + len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); + layout.char_codes.resize(len_chars + 1); + BLI_str_utf8_as_utf32(layout.char_codes.data(), layout.text.c_str(), layout.char_codes.size()); + layout.char_codes.remove_last(); + MEM_SAFE_FREE(chartransdata); MEM_SAFE_FREE(cu.str); MEM_SAFE_FREE(cu.strinfo); @@ -222,15 +281,15 @@ static TextLayout get_text_layout(GeoNodeExecParams ¶ms) /* Returns a mapping of UTF-32 character code to instance handle. */ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, - const float fontsize, - const Span<char32_t> charcodes, + TextLayout &layout, InstancesComponent &instance_component) { VFont *vfont = (VFont *)params.node().id; Map<int, int> handles; + bool pivot_required = params.output_is_required("Pivot Point"); - for (int i : charcodes.index_range()) { - if (handles.contains(charcodes[i])) { + for (int i : layout.char_codes.index_range()) { + if (handles.contains(layout.char_codes[i])) { continue; } Curve cu = {{nullptr}}; @@ -240,36 +299,75 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, CharInfo charinfo = {0}; charinfo.mat_nr = 1; - BKE_vfont_build_char(&cu, &cu.nurb, charcodes[i], &charinfo, 0, 0, 0, i, 1); + BKE_vfont_build_char(&cu, &cu.nurb, layout.char_codes[i], &charinfo, 0, 0, 0, i, 1); std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(cu); BKE_nurbList_free(&cu.nurb); + float4x4 size_matrix = float4x4::identity(); - size_matrix.apply_scale(fontsize); + size_matrix.apply_scale(layout.final_font_size); curve_eval->transform(size_matrix); + if (pivot_required) { + float3 pivot_point = get_pivot_point(params, *curve_eval); + layout.pivot_points.add_new(layout.char_codes[i], pivot_point); + } + GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release()); - handles.add_new(charcodes[i], instance_component.add_reference(std::move(geometry_set_curve))); + handles.add_new(layout.char_codes[i], + instance_component.add_reference(std::move(geometry_set_curve))); } return handles; } static void add_instances_from_handles(InstancesComponent &instances, const Map<int, int> &char_handles, - const Span<char32_t> charcodes, - const Span<float2> positions) + const TextLayout &layout) { - instances.resize(positions.size()); + instances.resize(layout.positions.size()); MutableSpan<int> handles = instances.instance_reference_handles(); MutableSpan<float4x4> transforms = instances.instance_transforms(); - threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) { + threading::parallel_for(IndexRange(layout.positions.size()), 256, [&](IndexRange range) { for (const int i : range) { - handles[i] = char_handles.lookup(charcodes[i]); - transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0}); + handles[i] = char_handles.lookup(layout.char_codes[i]); + transforms[i] = float4x4::from_location({layout.positions[i].x, layout.positions[i].y, 0}); } }); } +static void create_attributes(GeoNodeExecParams ¶ms, + const TextLayout &layout, + InstancesComponent &instances) +{ + if (params.output_is_required("Line")) { + StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line"); + OutputAttribute_Typed<int> line_attribute = instances.attribute_try_get_for_output_only<int>( + line_id.get(), ATTR_DOMAIN_INSTANCE); + MutableSpan<int> lines = line_attribute.as_span(); + lines.copy_from(layout.line_numbers); + line_attribute.save(); + params.set_output("Line", + AnonymousAttributeFieldInput::Create<int>(std::move(line_id), + params.attribute_producer_name())); + } + + if (params.output_is_required("Pivot Point")) { + StrongAnonymousAttributeID pivot_id = StrongAnonymousAttributeID("Pivot"); + OutputAttribute_Typed<float3> pivot_attribute = + instances.attribute_try_get_for_output_only<float3>(pivot_id.get(), ATTR_DOMAIN_INSTANCE); + MutableSpan<float3> pivots = pivot_attribute.as_span(); + + for (const int i : layout.char_codes.index_range()) { + pivots[i] = layout.pivot_points.lookup(layout.char_codes[i]); + } + + pivot_attribute.save(); + params.set_output("Pivot Point", + AnonymousAttributeFieldInput::Create<float3>( + std::move(pivot_id), params.attribute_producer_name())); + } +} + static void node_geo_exec(GeoNodeExecParams params) { TextLayout layout = get_text_layout(params); @@ -282,22 +380,16 @@ static void node_geo_exec(GeoNodeExecParams params) if (layout.positions.size() == 0) { params.set_output("Curve Instances", GeometrySet()); + params.set_default_remaining_outputs(); return; } - /* Convert UTF-8 encoded string to UTF-32. */ - size_t len_bytes; - size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); - Array<char32_t> char_codes_with_null(len_chars + 1); - BLI_str_utf8_as_utf32(char_codes_with_null.data(), layout.text.c_str(), len_chars + 1); - const Span<char32_t> char_codes = char_codes_with_null.as_span().drop_back(1); - /* Create and add instances. */ GeometrySet geometry_set_out; InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); - Map<int, int> char_handles = create_curve_instances( - params, layout.final_font_size, char_codes, instances); - add_instances_from_handles(instances, char_handles, char_codes, layout.positions); + Map<int, int> char_handles = create_curve_instances(params, layout, instances); + add_instances_from_handles(instances, char_handles, layout); + create_attributes(params, layout, instances); params.set_output("Curve Instances", std::move(geometry_set_out)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 5a8d9ab470d..e6b14bc69e5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -386,11 +386,11 @@ static bool component_is_available(const GeometrySet &geometry, /** * \note Multi-threading for this function is provided by the field evaluator. Since the #call - * function could be called many times, calculate the data from the target geometry once and store + * function could be called many times, calculate the data from the source geometry once and store * it for later. */ class NearestInterpolatedTransferFunction : public fn::MultiFunction { - GeometrySet target_; + GeometrySet source_; GField src_field_; /** @@ -403,18 +403,18 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { fn::MFSignature signature_; - std::optional<GeometryComponentFieldContext> target_context_; - std::unique_ptr<FieldEvaluator> target_evaluator_; - const GVArray *target_data_; + std::optional<GeometryComponentFieldContext> source_context_; + std::unique_ptr<FieldEvaluator> source_evaluator_; + const GVArray *source_data_; public: NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field) - : target_(std::move(geometry)), src_field_(std::move(src_field)) + : source_(std::move(geometry)), src_field_(std::move(src_field)) { - target_.ensure_owns_direct_data(); + source_.ensure_owns_direct_data(); signature_ = this->create_signature(); this->set_signature(&signature_); - this->evaluate_target_field(); + this->evaluate_source_field(); } fn::MFSignature create_signature() @@ -430,7 +430,7 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); - const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); + const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); BLI_assert(mesh_component.has_mesh()); const Mesh &mesh = *mesh_component.get_for_read(); BLI_assert(mesh.totpoly > 0); @@ -441,29 +441,29 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices); - interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); + interp.sample_data(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); } private: - void evaluate_target_field() + void evaluate_source_field() { - const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); - target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); + const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); + source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); const int domain_size = mesh_component.attribute_domain_size(domain_); - target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); - target_evaluator_->add(src_field_); - target_evaluator_->evaluate(); - target_data_ = &target_evaluator_->get_evaluated(0); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); + source_evaluator_->add(src_field_); + source_evaluator_->evaluate(); + source_data_ = &source_evaluator_->get_evaluated(0); } }; /** * \note Multi-threading for this function is provided by the field evaluator. Since the #call - * function could be called many times, calculate the data from the target geometry once and store + * function could be called many times, calculate the data from the source geometry once and store * it for later. */ class NearestTransferFunction : public fn::MultiFunction { - GeometrySet target_; + GeometrySet source_; GField src_field_; AttributeDomain domain_; @@ -472,7 +472,7 @@ class NearestTransferFunction : public fn::MultiFunction { bool use_mesh_; bool use_points_; - /* Store data from the target as a virtual array, since we may only access a few indices. */ + /* Store data from the source as a virtual array, since we may only access a few indices. */ std::optional<GeometryComponentFieldContext> mesh_context_; std::unique_ptr<FieldEvaluator> mesh_evaluator_; const GVArray *mesh_data_; @@ -483,16 +483,16 @@ class NearestTransferFunction : public fn::MultiFunction { public: NearestTransferFunction(GeometrySet geometry, GField src_field, AttributeDomain domain) - : target_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) + : source_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) { - target_.ensure_owns_direct_data(); + source_.ensure_owns_direct_data(); signature_ = this->create_signature(); this->set_signature(&signature_); - this->use_mesh_ = component_is_available(target_, GEO_COMPONENT_TYPE_MESH, domain_); - this->use_points_ = component_is_available(target_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_); + this->use_mesh_ = component_is_available(source_, GEO_COMPONENT_TYPE_MESH, domain_); + this->use_points_ = component_is_available(source_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_); - this->evaluate_target_field(); + this->evaluate_source_field(); } fn::MFSignature create_signature() @@ -513,8 +513,8 @@ class NearestTransferFunction : public fn::MultiFunction { return; } - const Mesh *mesh = use_mesh_ ? target_.get_mesh_for_read() : nullptr; - const PointCloud *pointcloud = use_points_ ? target_.get_pointcloud_for_read() : nullptr; + const Mesh *mesh = use_mesh_ ? source_.get_mesh_for_read() : nullptr; + const PointCloud *pointcloud = use_points_ ? source_.get_pointcloud_for_read() : nullptr; const int tot_samples = mask.min_array_size(); @@ -590,10 +590,10 @@ class NearestTransferFunction : public fn::MultiFunction { } private: - void evaluate_target_field() + void evaluate_source_field() { if (use_mesh_) { - const MeshComponent &mesh = *target_.get_component_for_read<MeshComponent>(); + const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>(); const int domain_size = mesh.attribute_domain_size(domain_); mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size); @@ -603,7 +603,7 @@ class NearestTransferFunction : public fn::MultiFunction { } if (use_points_) { - const PointCloudComponent &points = *target_.get_component_for_read<PointCloudComponent>(); + const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>(); const int domain_size = points.attribute_domain_size(domain_); point_context_.emplace(GeometryComponentFieldContext(points, domain_)); point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_size); @@ -614,7 +614,7 @@ class NearestTransferFunction : public fn::MultiFunction { } }; -static const GeometryComponent *find_target_component(const GeometrySet &geometry, +static const GeometryComponent *find_source_component(const GeometrySet &geometry, const AttributeDomain domain) { /* Choose the other component based on a consistent order, rather than some more complicated @@ -634,7 +634,7 @@ static const GeometryComponent *find_target_component(const GeometrySet &geometr /** * The index-based transfer theoretically does not need realized data when there is only one - * instance geometry set in the target. A future optimization could be removing that limitation + * instance geometry set in the source. A future optimization could be removing that limitation * internally. */ class IndexTransferFunction : public fn::MultiFunction { @@ -670,7 +670,7 @@ class IndexTransferFunction : public fn::MultiFunction { void evaluate_field() { - const GeometryComponent *component = find_target_component(src_geometry_, domain_); + const GeometryComponent *component = find_source_component(src_geometry_, domain_); if (component == nullptr) { return; } @@ -772,7 +772,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (mesh == nullptr) { if (!geometry.is_empty()) { params.error_message_add(NodeWarningType::Error, - TIP_("The target geometry must contain a mesh")); + TIP_("The source geometry must contain a mesh")); } return return_default(); } @@ -780,7 +780,7 @@ static void node_geo_exec(GeoNodeExecParams params) /* Don't add a warning for empty meshes. */ if (mesh->totvert != 0) { params.error_message_add(NodeWarningType::Error, - TIP_("The target mesh must have faces")); + TIP_("The source mesh must have faces")); } return return_default(); } @@ -794,7 +794,7 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { params.error_message_add(NodeWarningType::Error, - TIP_("The target geometry must contain a mesh or a point cloud")); + TIP_("The source geometry must contain a mesh or a point cloud")); return return_default(); } auto fn = std::make_unique<NearestTransferFunction>( diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index e78c4d7bc35..ab1afeb39f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -67,7 +67,7 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh, BM_elem_flag_set(BM_face_at_index(bm, i_face), BM_ELEM_TAG, true); } - BM_mesh_triangulate(bm, quad_method, ngon_method, min_vertices, true, NULL, NULL, NULL); + BM_mesh_triangulate(bm, quad_method, ngon_method, min_vertices, true, nullptr, nullptr, nullptr); Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, &mesh); BM_mesh_free(bm); BKE_mesh_normals_tag_dirty(result); diff --git a/source/blender/nodes/intern/node_exec.cc b/source/blender/nodes/intern/node_exec.cc index 4ff662036c3..788d938ca6f 100644 --- a/source/blender/nodes/intern/node_exec.cc +++ b/source/blender/nodes/intern/node_exec.cc @@ -35,7 +35,7 @@ #include "node_exec.h" #include "node_util.h" -int node_exec_socket_use_stack(bNodeSocket *sock) +static int node_exec_socket_use_stack(bNodeSocket *sock) { /* NOTE: INT supported as FLOAT. Only for EEVEE. */ return ELEM(sock->type, SOCK_INT, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER); @@ -276,60 +276,3 @@ void ntree_exec_end(bNodeTreeExec *exec) MEM_freeN(exec); } - -/**** Material/Texture trees ****/ - -bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread) -{ - ListBase *lb = &exec->threadstack[thread]; - bNodeThreadStack *nts; - - for (nts = (bNodeThreadStack *)lb->first; nts; nts = nts->next) { - if (!nts->used) { - nts->used = true; - break; - } - } - - if (!nts) { - nts = MEM_cnew<bNodeThreadStack>("bNodeThreadStack"); - nts->stack = (bNodeStack *)MEM_dupallocN(exec->stack); - nts->used = true; - BLI_addtail(lb, nts); - } - - return nts; -} - -void ntreeReleaseThreadStack(bNodeThreadStack *nts) -{ - nts->used = false; -} - -bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *callerdata, int thread) -{ - bNodeStack *nsin[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */ - bNodeStack *nsout[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */ - bNodeExec *nodeexec; - bNode *node; - int n; - - /* nodes are presorted, so exec is in order of list */ - - for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; n++, nodeexec++) { - node = nodeexec->node; - if (node->need_exec) { - node_get_stack(node, nts->stack, nsin, nsout); - /* Handle muted nodes... - * If the mute func is not set, assume the node should never be muted, - * and hence execute it! - */ - if (node->typeinfo->exec_fn && !(node->flag & NODE_MUTED)) { - node->typeinfo->exec_fn(callerdata, thread, node, &nodeexec->data, nsin, nsout); - } - } - } - - /* signal to that all went OK, for render */ - return true; -} diff --git a/source/blender/nodes/intern/node_exec.h b/source/blender/nodes/intern/node_exec.h index b2e1c6564b6..115389afd67 100644 --- a/source/blender/nodes/intern/node_exec.h +++ b/source/blender/nodes/intern/node_exec.h @@ -71,9 +71,6 @@ typedef struct bNodeThreadStack { bool used; } bNodeThreadStack; -/** Supported socket types in old nodes. */ -int node_exec_socket_use_stack(struct bNodeSocket *sock); - /** For a given socket, find the actual stack entry. */ struct bNodeStack *node_get_socket_stack(struct bNodeStack *stack, struct bNodeSocket *sock); void node_get_stack(struct bNode *node, @@ -86,23 +83,6 @@ struct bNodeTreeExec *ntree_exec_begin(struct bNodeExecContext *context, bNodeInstanceKey parent_key); void ntree_exec_end(struct bNodeTreeExec *exec); -struct bNodeThreadStack *ntreeGetThreadStack(struct bNodeTreeExec *exec, int thread); -void ntreeReleaseThreadStack(struct bNodeThreadStack *nts); -bool ntreeExecThreadNodes(struct bNodeTreeExec *exec, - struct bNodeThreadStack *nts, - void *callerdata, - int thread); - -struct bNodeTreeExec *ntreeShaderBeginExecTree_internal(struct bNodeExecContext *context, - struct bNodeTree *ntree, - bNodeInstanceKey parent_key); -void ntreeShaderEndExecTree_internal(struct bNodeTreeExec *exec); - -struct bNodeTreeExec *ntreeTexBeginExecTree_internal(struct bNodeExecContext *context, - struct bNodeTree *ntree, - bNodeInstanceKey parent_key); -void ntreeTexEndExecTree_internal(struct bNodeTreeExec *exec); - #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 4cf18429e0d..598b0c81b51 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -93,6 +93,7 @@ set(SRC nodes/node_shader_output_material.cc nodes/node_shader_output_world.cc nodes/node_shader_particle_info.cc + nodes/node_shader_point_info.cc nodes/node_shader_rgb.cc nodes/node_shader_rgb_to_bw.cc nodes/node_shader_script.cc @@ -157,10 +158,6 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() diff --git a/source/blender/nodes/shader/node_shader_util.hh b/source/blender/nodes/shader/node_shader_util.hh index 5a5b4f613f3..31229c8693b 100644 --- a/source/blender/nodes/shader/node_shader_util.hh +++ b/source/blender/nodes/shader/node_shader_util.hh @@ -101,6 +101,11 @@ void node_shader_gpu_tex_mapping(struct GPUMaterial *mat, struct GPUNodeStack *in, struct GPUNodeStack *out); +struct bNodeTreeExec *ntreeShaderBeginExecTree_internal(struct bNodeExecContext *context, + struct bNodeTree *ntree, + bNodeInstanceKey parent_key); +void ntreeShaderEndExecTree_internal(struct bNodeTreeExec *exec); + void ntreeExecGPUNodes(struct bNodeTreeExec *exec, struct GPUMaterial *mat, struct bNode *output_node); diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index bce59a60033..6570ffcac83 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -49,6 +49,7 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, CurveMapping *cumap = (CurveMapping *)node->storage; + BKE_curvemapping_init(cumap); BKE_curvemapping_table_RGBA(cumap, &array, &size); GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); @@ -344,6 +345,7 @@ static int gpu_shader_curve_float(GPUMaterial *mat, CurveMapping *cumap = (CurveMapping *)node->storage; + BKE_curvemapping_init(cumap); BKE_curvemapping_table_F(cumap, &array, &size); GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc index e22a825c066..12c29c40b1d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc @@ -28,7 +28,6 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Length")); b.add_output<decl::Float>(N_("Thickness")); b.add_output<decl::Vector>(N_("Tangent Normal")); - // b.add_output<decl::Float>(N_("Fade")); b.add_output<decl::Float>(N_("Random")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index bc7ca661a77..254ab9f866d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -66,6 +66,16 @@ static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), Po } } +static int node_shader_map_range_ui_class(const bNode *node) +{ + const NodeMapRange &storage = node_storage(*node); + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + if (data_type == CD_PROP_FLOAT3) { + return NODE_CLASS_OP_VECTOR; + } + return NODE_CLASS_CONVERTER; +} + static void node_shader_update_map_range(bNodeTree *ntree, bNode *node) { const NodeMapRange &storage = node_storage(*node); @@ -665,6 +675,7 @@ void register_node_type_sh_map_range() sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_map_range_declare; ntype.draw_buttons = file_ns::node_shader_buts_map_range; + ntype.ui_class = file_ns::node_shader_map_range_ui_class; node_type_init(&ntype, file_ns::node_shader_init_map_range); node_type_storage( &ntype, "NodeMapRange", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/shader/nodes/node_shader_point_info.cc b/source/blender/nodes/shader/nodes/node_shader_point_info.cc new file mode 100644 index 00000000000..adc58ca065a --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_point_info.cc @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_point_info_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("Position")); + b.add_output<decl::Float>(N_("Radius")); + b.add_output<decl::Float>(N_("Random")); +} + +static int node_shader_gpu_point_info(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "node_point_info", in, out); +} + +} // namespace blender::nodes::node_shader_point_info_cc + +/* node type definition */ +void register_node_type_sh_point_info() +{ + namespace file_ns = blender::nodes::node_shader_point_info_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_POINT_INFO, "Point Info", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_point_info); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt index 053b17e4e57..e595343b7d2 100644 --- a/source/blender/nodes/texture/CMakeLists.txt +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -82,8 +82,5 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() blender_add_lib(bf_nodes_texture "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 3d914d486c3..82ed43be779 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -170,6 +170,63 @@ void register_node_tree_type_tex(void) ntreeTypeAdd(tt); } +/**** Material/Texture trees ****/ + +bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread) +{ + ListBase *lb = &exec->threadstack[thread]; + bNodeThreadStack *nts; + + for (nts = (bNodeThreadStack *)lb->first; nts; nts = nts->next) { + if (!nts->used) { + nts->used = true; + break; + } + } + + if (!nts) { + nts = MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack"); + nts->stack = (bNodeStack *)MEM_dupallocN(exec->stack); + nts->used = true; + BLI_addtail(lb, nts); + } + + return nts; +} + +void ntreeReleaseThreadStack(bNodeThreadStack *nts) +{ + nts->used = false; +} + +bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *callerdata, int thread) +{ + bNodeStack *nsin[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */ + bNodeStack *nsout[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */ + bNodeExec *nodeexec; + bNode *node; + int n; + + /* nodes are presorted, so exec is in order of list */ + + for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; n++, nodeexec++) { + node = nodeexec->node; + if (node->need_exec) { + node_get_stack(node, nts->stack, nsin, nsout); + /* Handle muted nodes... + * If the mute func is not set, assume the node should never be muted, + * and hence execute it! + */ + if (node->typeinfo->exec_fn && !(node->flag & NODE_MUTED)) { + node->typeinfo->exec_fn(callerdata, thread, node, &nodeexec->data, nsin, nsout); + } + } + } + + /* signal to that all went OK, for render */ + return true; +} + bNodeTreeExec *ntreeTexBeginExecTree_internal(bNodeExecContext *context, bNodeTree *ntree, bNodeInstanceKey parent_key) diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h index d53000058a3..7c8e39925bc 100644 --- a/source/blender/nodes/texture/node_texture_util.h +++ b/source/blender/nodes/texture/node_texture_util.h @@ -123,6 +123,18 @@ void tex_output(bNode *node, void params_from_cdata(TexParams *out, TexCallData *in); +struct bNodeThreadStack *ntreeGetThreadStack(struct bNodeTreeExec *exec, int thread); +void ntreeReleaseThreadStack(struct bNodeThreadStack *nts); +bool ntreeExecThreadNodes(struct bNodeTreeExec *exec, + struct bNodeThreadStack *nts, + void *callerdata, + int thread); + +struct bNodeTreeExec *ntreeTexBeginExecTree_internal(struct bNodeExecContext *context, + struct bNodeTree *ntree, + bNodeInstanceKey parent_key); +void ntreeTexEndExecTree_internal(struct bNodeTreeExec *exec); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/texture/nodes/node_texture_output.c b/source/blender/nodes/texture/nodes/node_texture_output.c index 4911ab7ba9e..59c2d2766d0 100644 --- a/source/blender/nodes/texture/nodes/node_texture_output.c +++ b/source/blender/nodes/texture/nodes/node_texture_output.c @@ -49,10 +49,10 @@ static void exec(void *data, params_from_cdata(¶ms, cdata); if (in[1] && in[1]->hasinput && !in[0]->hasinput) { - tex_input_rgba(&target->tr, in[1], ¶ms, cdata->thread); + tex_input_rgba(target->trgba, in[1], ¶ms, cdata->thread); } else { - tex_input_rgba(&target->tr, in[0], ¶ms, cdata->thread); + tex_input_rgba(target->trgba, in[0], ¶ms, cdata->thread); } } else { @@ -61,9 +61,9 @@ static void exec(void *data, TexParams params; params_from_cdata(¶ms, cdata); - tex_input_rgba(&target->tr, in[0], ¶ms, cdata->thread); + tex_input_rgba(target->trgba, in[0], ¶ms, cdata->thread); - target->tin = (target->tr + target->tg + target->tb) / 3.0f; + target->tin = (target->trgba[0] + target->trgba[1] + target->trgba[2]) / 3.0f; target->talpha = true; if (target->nor) { diff --git a/source/blender/nodes/texture/nodes/node_texture_proc.c b/source/blender/nodes/texture/nodes/node_texture_proc.c index 8c294b5954d..38e4f0268e8 100644 --- a/source/blender/nodes/texture/nodes/node_texture_proc.c +++ b/source/blender/nodes/texture/nodes/node_texture_proc.c @@ -72,7 +72,7 @@ static void do_proc(float *result, } if (textype & TEX_RGB) { - copy_v4_v4(result, &texres.tr); + copy_v4_v4(result, texres.trgba); } else { copy_v4_v4(result, col1); diff --git a/source/blender/nodes/texture/nodes/node_texture_texture.c b/source/blender/nodes/texture/nodes/node_texture_texture.c index 083ae67ccb6..f8f8f25cb5a 100644 --- a/source/blender/nodes/texture/nodes/node_texture_texture.c +++ b/source/blender/nodes/texture/nodes/node_texture_texture.c @@ -71,7 +71,7 @@ static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor textype = multitex_nodes(nodetex, co, dxt, dyt, p->osatex, &texres, thread, 0, p->mtex, NULL); if (textype & TEX_RGB) { - copy_v4_v4(out, &texres.tr); + copy_v4_v4(out, texres.trgba); } else { copy_v4_v4(out, col1); diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index 2e684faf61b..11e3ad0bcf4 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -94,7 +94,7 @@ static int foreach_libblock_id_user_map_callback(LibraryIDLinkCallbackData *cb_d } if (cb_flag & IDWALK_CB_LOOPBACK) { - /* We skip loop-back pointers like Object.proxy_from or Key.from here, + /* We skip loop-back pointers like Key.from here, * since it's some internal pointer which is not relevant info for py/API level. */ return IDWALK_RET_NOP; } diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c index d3ec54fc12d..d24e2a77a75 100644 --- a/source/blender/python/intern/bpy_rna_operator.c +++ b/source/blender/python/intern/bpy_rna_operator.c @@ -103,7 +103,7 @@ PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc, static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args) { - const ssize_t args_len = PyTuple_GET_SIZE(args); + const Py_ssize_t args_len = PyTuple_GET_SIZE(args); if (args_len == 0) { PyErr_SetString(PyExc_ValueError, "poll_message_set(message, ...): requires a message argument"); diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index a7c1b12982c..a49fc21d7ff 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -95,9 +95,5 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib_nolist(bf_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index 072fc7363b2..c1392b24023 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -310,7 +310,7 @@ void RE_SetOverrideCamera(struct Render *re, struct Object *cam_ob); * * \note call this after #RE_InitState(). */ -void RE_SetCamera(struct Render *re, struct Object *cam_ob); +void RE_SetCamera(struct Render *re, const struct Object *cam_ob); /** * Get current view and window transform. @@ -454,12 +454,14 @@ struct RenderPass *RE_pass_find_by_type(volatile struct RenderLayer *rl, #define RE_BAKE_DISPLACEMENT 1 #define RE_BAKE_AO 2 -void RE_GetCameraWindow(struct Render *re, struct Object *camera, float mat[4][4]); +void RE_GetCameraWindow(struct Render *re, const struct Object *camera, float mat[4][4]); /** * Must be called after #RE_GetCameraWindow(), does not change `re->winmat`. */ -void RE_GetCameraWindowWithOverscan(struct Render *re, float overscan, float r_winmat[4][4]); -void RE_GetCameraModelMatrix(struct Render *re, struct Object *camera, float r_modelmat[4][4]); +void RE_GetCameraWindowWithOverscan(const struct Render *re, float overscan, float r_winmat[4][4]); +void RE_GetCameraModelMatrix(const struct Render *re, + const struct Object *camera, + float r_modelmat[4][4]); struct Scene *RE_GetScene(struct Render *re); void RE_SetScene(struct Render *re, struct Scene *sce); diff --git a/source/blender/render/RE_texture.h b/source/blender/render/RE_texture.h index d71c793f300..4d58d866e9f 100644 --- a/source/blender/render/RE_texture.h +++ b/source/blender/render/RE_texture.h @@ -99,10 +99,11 @@ void RE_point_density_fix_linking(void); /** * Texture evaluation result. - * \note `tr tg tb ta` have to remain in this order for array access. */ typedef struct TexResult { - float tin, tr, tg, tb, ta; + float tin; + float trgba[4]; + /* Is actually a boolean: When true -> use alpha, false -> set alpha to 1.0. */ int talpha; float *nor; } TexResult; diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 93d2f721cc5..883e026472b 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -512,9 +512,9 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval triangles[i].mverts[0] = &mvert[me->mloop[lt->tri[0]].v]; triangles[i].mverts[1] = &mvert[me->mloop[lt->tri[1]].v]; triangles[i].mverts[2] = &mvert[me->mloop[lt->tri[2]].v]; - triangles[i].vert_normals[0] = &vert_normals[me->mloop[lt->tri[0]].v][0]; - triangles[i].vert_normals[1] = &vert_normals[me->mloop[lt->tri[1]].v][1]; - triangles[i].vert_normals[2] = &vert_normals[me->mloop[lt->tri[2]].v][2]; + triangles[i].vert_normals[0] = vert_normals[me->mloop[lt->tri[0]].v]; + triangles[i].vert_normals[1] = vert_normals[me->mloop[lt->tri[1]].v]; + triangles[i].vert_normals[2] = vert_normals[me->mloop[lt->tri[2]].v]; triangles[i].is_smooth = (mp->flag & ME_SMOOTH) != 0; if (tangent) { diff --git a/source/blender/render/intern/initrender.c b/source/blender/render/intern/initrender.c index f696b81cffb..3bdf60c13a2 100644 --- a/source/blender/render/intern/initrender.c +++ b/source/blender/render/intern/initrender.c @@ -174,7 +174,7 @@ void RE_SetOverrideCamera(Render *re, Object *cam_ob) re->camera_override = cam_ob; } -void RE_SetCamera(Render *re, Object *cam_ob) +void RE_SetCamera(Render *re, const Object *cam_ob) { CameraParams params; @@ -194,13 +194,13 @@ void RE_SetCamera(Render *re, Object *cam_ob) re->viewplane = params.viewplane; } -void RE_GetCameraWindow(struct Render *re, struct Object *camera, float r_winmat[4][4]) +void RE_GetCameraWindow(struct Render *re, const struct Object *camera, float r_winmat[4][4]) { RE_SetCamera(re, camera); copy_m4_m4(r_winmat, re->winmat); } -void RE_GetCameraWindowWithOverscan(struct Render *re, float overscan, float r_winmat[4][4]) +void RE_GetCameraWindowWithOverscan(const struct Render *re, float overscan, float r_winmat[4][4]) { CameraParams params; params.is_ortho = re->winmat[3][3] != 0.0f; @@ -218,7 +218,7 @@ void RE_GetCameraWindowWithOverscan(struct Render *re, float overscan, float r_w copy_m4_m4(r_winmat, params.winmat); } -void RE_GetCameraModelMatrix(Render *re, struct Object *camera, float r_modelmat[4][4]) +void RE_GetCameraModelMatrix(const Render *re, const struct Object *camera, float r_modelmat[4][4]) { BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_modelmat); } diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index 5cf328a3a73..9468e4c5b0f 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -33,11 +33,14 @@ #include "BLI_math.h" #include "BLI_threads.h" +#include "BKE_DerivedMesh.h" #include "BKE_ccg.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_mesh_tangent.h" #include "BKE_modifier.h" #include "BKE_multires.h" #include "BKE_subsurf.h" @@ -488,9 +491,35 @@ static void do_multires_bake(MultiresBakeRender *bkr, void *bake_data = NULL; + Mesh *temp_mesh = BKE_mesh_new_nomain( + dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); + memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); + memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); + memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); + memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); + const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); + if (require_tangent) { if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { - DM_calc_loop_tangents(dm, true, NULL, 0); + BKE_mesh_calc_loop_tangent_ex( + dm->getVertArray(dm), + dm->getPolyArray(dm), + dm->getNumPolys(dm), + dm->getLoopArray(dm), + dm->getLoopTriArray(dm), + dm->getNumLoopTri(dm), + &dm->loopData, + true, + NULL, + 0, + vert_normals, + (const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL), + (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), + (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ + /* result */ + &dm->loopData, + dm->getNumLoops(dm), + &dm->tangent_mask); } pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); @@ -524,6 +553,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, handle->data.mpoly = mpoly; handle->data.mvert = mvert; + handle->data.vert_normals = vert_normals; handle->data.mloopuv = mloopuv; handle->data.mlooptri = mlooptri; handle->data.mloop = mloop; @@ -575,6 +605,8 @@ static void do_multires_bake(MultiresBakeRender *bkr, MEM_freeN(handles); + BKE_id_free(NULL, temp_mesh); + BKE_image_release_ibuf(ima, ibuf, NULL); } } diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 721abe45932..739202564af 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -73,6 +73,8 @@ #include "BKE_sound.h" #include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */ +#include "NOD_composite.h" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_debug.h" diff --git a/source/blender/render/intern/texture_common.h b/source/blender/render/intern/texture_common.h index 5fc3af6153f..43487439228 100644 --- a/source/blender/render/intern/texture_common.h +++ b/source/blender/render/intern/texture_common.h @@ -40,34 +40,38 @@ extern "C" { ((void)0) #define BRICONTRGB \ - texres->tr = tex->rfac * ((texres->tr - 0.5f) * tex->contrast + tex->bright - 0.5f); \ - texres->tg = tex->gfac * ((texres->tg - 0.5f) * tex->contrast + tex->bright - 0.5f); \ - texres->tb = tex->bfac * ((texres->tb - 0.5f) * tex->contrast + tex->bright - 0.5f); \ + texres->trgba[0] = tex->rfac * \ + ((texres->trgba[0] - 0.5f) * tex->contrast + tex->bright - 0.5f); \ + texres->trgba[1] = tex->gfac * \ + ((texres->trgba[1] - 0.5f) * tex->contrast + tex->bright - 0.5f); \ + texres->trgba[2] = tex->bfac * \ + ((texres->trgba[2] - 0.5f) * tex->contrast + tex->bright - 0.5f); \ if (!(tex->flag & TEX_NO_CLAMP)) { \ - if (texres->tr < 0.0f) { \ - texres->tr = 0.0f; \ + if (texres->trgba[0] < 0.0f) { \ + texres->trgba[0] = 0.0f; \ } \ - if (texres->tg < 0.0f) { \ - texres->tg = 0.0f; \ + if (texres->trgba[1] < 0.0f) { \ + texres->trgba[1] = 0.0f; \ } \ - if (texres->tb < 0.0f) { \ - texres->tb = 0.0f; \ + if (texres->trgba[2] < 0.0f) { \ + texres->trgba[2] = 0.0f; \ } \ } \ if (tex->saturation != 1.0f) { \ float _hsv[3]; \ - rgb_to_hsv(texres->tr, texres->tg, texres->tb, _hsv, _hsv + 1, _hsv + 2); \ + rgb_to_hsv(texres->trgba[0], texres->trgba[1], texres->trgba[2], _hsv, _hsv + 1, _hsv + 2); \ _hsv[1] *= tex->saturation; \ - hsv_to_rgb(_hsv[0], _hsv[1], _hsv[2], &texres->tr, &texres->tg, &texres->tb); \ + hsv_to_rgb( \ + _hsv[0], _hsv[1], _hsv[2], &texres->trgba[0], &texres->trgba[1], &texres->trgba[2]); \ if ((tex->saturation > 1.0f) && !(tex->flag & TEX_NO_CLAMP)) { \ - if (texres->tr < 0.0f) { \ - texres->tr = 0.0f; \ + if (texres->trgba[0] < 0.0f) { \ + texres->trgba[0] = 0.0f; \ } \ - if (texres->tg < 0.0f) { \ - texres->tg = 0.0f; \ + if (texres->trgba[1] < 0.0f) { \ + texres->trgba[1] = 0.0f; \ } \ - if (texres->tb < 0.0f) { \ - texres->tb = 0.0f; \ + if (texres->trgba[2] < 0.0f) { \ + texres->trgba[2] = 0.0f; \ } \ } \ } \ diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c index 6ded799b773..ad355d0cb25 100644 --- a/source/blender/render/intern/texture_image.c +++ b/source/blender/render/intern/texture_image.c @@ -108,7 +108,7 @@ int imagewrap(Tex *tex, int x, y, retval; int xi, yi; /* original values */ - texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; + texres->tin = texres->trgba[3] = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = 0.0f; /* we need to set retval OK, otherwise texture code generates normals itself... */ retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB; @@ -269,7 +269,7 @@ int imagewrap(Tex *tex, (tex->extend == TEX_EXTEND)); } else { /* no filtering */ - ibuf_get_color(&texres->tr, ibuf, x, y); + ibuf_get_color(texres->trgba, ibuf, x, y); } if (texres->nor) { @@ -281,13 +281,13 @@ int imagewrap(Tex *tex, * the normal used in the renderer points inward. It is generated * this way in calc_vertexnormals(). Should this ever change * this negate must be removed. */ - texres->nor[0] = -2.0f * (texres->tr - 0.5f); - texres->nor[1] = 2.0f * (texres->tg - 0.5f); - texres->nor[2] = 2.0f * (texres->tb - 0.5f); + texres->nor[0] = -2.0f * (texres->trgba[0] - 0.5f); + texres->nor[1] = 2.0f * (texres->trgba[1] - 0.5f); + texres->nor[2] = 2.0f * (texres->trgba[2] - 0.5f); } else { /* bump: take three samples */ - val1 = texres->tr + texres->tg + texres->tb; + val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2]; if (x < ibuf->x - 1) { float col[4]; @@ -314,26 +314,26 @@ int imagewrap(Tex *tex, } if (texres->talpha) { - texres->tin = texres->ta; + texres->tin = texres->trgba[3]; } else if (tex->imaflag & TEX_CALCALPHA) { - texres->ta = texres->tin = max_fff(texres->tr, texres->tg, texres->tb); + texres->trgba[3] = texres->tin = max_fff(texres->trgba[0], texres->trgba[1], texres->trgba[2]); } else { - texres->ta = texres->tin = 1.0; + texres->trgba[3] = texres->tin = 1.0; } if (tex->flag & TEX_NEGALPHA) { - texres->ta = 1.0f - texres->ta; + texres->trgba[3] = 1.0f - texres->trgba[3]; } /* de-premul, this is being pre-multiplied in shade_input_do_shade() * do not de-premul for generated alpha, it is already in straight */ - if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { - fx = 1.0f / texres->ta; - texres->tr *= fx; - texres->tg *= fx; - texres->tb *= fx; + if (texres->trgba[3] != 1.0f && texres->trgba[3] > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { + fx = 1.0f / texres->trgba[3]; + texres->trgba[0] *= fx; + texres->trgba[1] *= fx; + texres->trgba[2] *= fx; } if (ima) { @@ -546,10 +546,10 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres) } if (starty == endy && startx == endx) { - ibuf_get_color(&texres->tr, ibuf, startx, starty); + ibuf_get_color(texres->trgba, ibuf, startx, starty); } else { - div = texres->tr = texres->tg = texres->tb = texres->ta = 0.0; + div = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = texres->trgba[3] = 0.0; for (y = starty; y <= endy; y++) { muly = 1.0; @@ -570,11 +570,7 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres) mulx = muly; ibuf_get_color(col, ibuf, startx, y); - - texres->ta += mulx * col[3]; - texres->tr += mulx * col[0]; - texres->tg += mulx * col[1]; - texres->tb += mulx * col[2]; + madd_v4_v4fl(texres->trgba, col, mulx); div += mulx; } else { @@ -588,19 +584,14 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres) } ibuf_get_color(col, ibuf, x, y); - + /* TODO(jbakker): No need to do manual optimization. Branching is slower than multiplying + * with 1. */ if (mulx == 1.0f) { - texres->ta += col[3]; - texres->tr += col[0]; - texres->tg += col[1]; - texres->tb += col[2]; + add_v4_v4(texres->trgba, col); div += 1.0f; } else { - texres->ta += mulx * col[3]; - texres->tr += mulx * col[0]; - texres->tg += mulx * col[1]; - texres->tb += mulx * col[2]; + madd_v4_v4fl(texres->trgba, col, mulx); div += mulx; } } @@ -609,13 +600,10 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres) if (div != 0.0f) { div = 1.0f / div; - texres->tb *= div; - texres->tg *= div; - texres->tr *= div; - texres->ta *= div; + mul_v4_fl(texres->trgba, div); } else { - texres->tr = texres->tg = texres->tb = texres->ta = 0.0f; + zero_v4(texres->trgba); } } } @@ -663,7 +651,7 @@ static void boxsample(ImBuf *ibuf, alphaclip = clipx_rctf(rf, 0.0, (float)(ibuf->x)); if (alphaclip <= 0.0f) { - texres->tr = texres->tb = texres->tg = texres->ta = 0.0; + texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0; return; } } @@ -679,33 +667,33 @@ static void boxsample(ImBuf *ibuf, alphaclip *= clipy_rctf(rf, 0.0, (float)(ibuf->y)); if (alphaclip <= 0.0f) { - texres->tr = texres->tb = texres->tg = texres->ta = 0.0; + texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0; return; } } if (count > 1) { - tot = texres->tr = texres->tb = texres->tg = texres->ta = 0.0; + tot = texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0; while (count--) { boxsampleclip(ibuf, rf, &texr); opp = square_rctf(rf); tot += opp; - texres->tr += opp * texr.tr; - texres->tg += opp * texr.tg; - texres->tb += opp * texr.tb; + texres->trgba[0] += opp * texr.trgba[0]; + texres->trgba[1] += opp * texr.trgba[1]; + texres->trgba[2] += opp * texr.trgba[2]; if (texres->talpha) { - texres->ta += opp * texr.ta; + texres->trgba[3] += opp * texr.trgba[3]; } rf++; } if (tot != 0.0f) { - texres->tr /= tot; - texres->tg /= tot; - texres->tb /= tot; + texres->trgba[0] /= tot; + texres->trgba[1] /= tot; + texres->trgba[2] /= tot; if (texres->talpha) { - texres->ta /= tot; + texres->trgba[3] /= tot; } } } @@ -714,15 +702,15 @@ static void boxsample(ImBuf *ibuf, } if (texres->talpha == 0) { - texres->ta = 1.0; + texres->trgba[3] = 1.0; } if (alphaclip != 1.0f) { /* premul it all */ - texres->tr *= alphaclip; - texres->tg *= alphaclip; - texres->tb *= alphaclip; - texres->ta *= alphaclip; + texres->trgba[0] *= alphaclip; + texres->trgba[1] *= alphaclip; + texres->trgba[2] *= alphaclip; + texres->trgba[3] *= alphaclip; } } @@ -850,7 +838,7 @@ static void area_sample(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata ysam = CLAMPIS(ysam, minsam, ibuf->y * 2); xsd = 1.0f / xsam; ysd = 1.0f / ysam; - texr->tr = texr->tg = texr->tb = texr->ta = 0.0f; + texr->trgba[0] = texr->trgba[1] = texr->trgba[2] = texr->trgba[3] = 0.0f; for (ys = 0; ys < ysam; ys++) { for (xs = 0; xs < xsam; xs++) { const float su = (xs + ((ys & 1) + 0.5f) * 0.5f) * xsd - 0.5f; @@ -861,18 +849,18 @@ static void area_sample(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata tc, ibuf, pu * ibuf->x, pv * ibuf->y, AFD->intpol, AFD->extflag); clip |= out; cw += out ? 0.0f : 1.0f; - texr->tr += tc[0]; - texr->tg += tc[1]; - texr->tb += tc[2]; - texr->ta += texr->talpha ? tc[3] : 0.0f; + texr->trgba[0] += tc[0]; + texr->trgba[1] += tc[1]; + texr->trgba[2] += tc[2]; + texr->trgba[3] += texr->talpha ? tc[3] : 0.0f; } } xsd *= ysd; - texr->tr *= xsd; - texr->tg *= xsd; - texr->tb *= xsd; - /* clipping can be ignored if alpha used, texr->ta already includes filtered edge */ - texr->ta = texr->talpha ? texr->ta * xsd : (clip ? cw * xsd : 1.0f); + texr->trgba[0] *= xsd; + texr->trgba[1] *= xsd; + texr->trgba[2] *= xsd; + /* clipping can be ignored if alpha used, texr->trgba[3] already includes filtered edge */ + texr->trgba[3] = texr->talpha ? texr->trgba[3] * xsd : (clip ? cw * xsd : 1.0f); } typedef struct ReadEWAData { @@ -901,7 +889,7 @@ static void ewa_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata_t AFD->dyt, ewa_read_pixel_cb, &data, - &texr->tr); + texr->trgba); } static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata_t *AFD) @@ -919,7 +907,7 @@ static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata /* have to use same scaling for du/dv here as for Ux/Vx/Uy/Vy (*after* D calc.) */ du *= AFD->dusc; dv *= AFD->dvsc; - d = texr->tr = texr->tb = texr->tg = texr->ta = 0.0f; + d = texr->trgba[0] = texr->trgba[2] = texr->trgba[1] = texr->trgba[3] = 0.0f; for (n = -maxn; n <= maxn; n += 2) { float tc[4]; const float hn = n * 0.5f; @@ -934,19 +922,20 @@ static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata tc, ibuf, ibuf->x * u, ibuf->y * v, AFD->intpol, AFD->extflag); /* TXF alpha: `clip |= out;` * TXF alpha: `cw += out ? 0.0f : wt;` */ - texr->tr += tc[0] * wt; - texr->tg += tc[1] * wt; - texr->tb += tc[2] * wt; - texr->ta += texr->talpha ? tc[3] * wt : 0.0f; + texr->trgba[0] += tc[0] * wt; + texr->trgba[1] += tc[1] * wt; + texr->trgba[2] += tc[2] * wt; + texr->trgba[3] += texr->talpha ? tc[3] * wt : 0.0f; d += wt; } d = 1.0f / d; - texr->tr *= d; - texr->tg *= d; - texr->tb *= d; - /* Clipping can be ignored if alpha used, `texr->ta` already includes filtered edge */ - texr->ta = texr->talpha ? texr->ta * d : 1.0f; /* TXF alpha: `(clip ? cw*d : 1.0f);` */ + texr->trgba[0] *= d; + texr->trgba[1] *= d; + texr->trgba[2] *= d; + /* Clipping can be ignored if alpha used, `texr->trgba[3]` already includes filtered edge */ + texr->trgba[3] = texr->talpha ? texr->trgba[3] * d : + 1.0f; /* TXF alpha: `(clip ? cw*d : 1.0f);` */ } #undef EWA_MAXIDX @@ -971,10 +960,10 @@ static void alpha_clip_aniso( if (alphaclip != 1.0f) { /* premul it all */ - texres->tr *= alphaclip; - texres->tg *= alphaclip; - texres->tb *= alphaclip; - texres->ta *= alphaclip; + texres->trgba[0] *= alphaclip; + texres->trgba[1] *= alphaclip; + texres->trgba[2] *= alphaclip; + texres->trgba[3] *= alphaclip; } } } @@ -1033,7 +1022,7 @@ static int imagewraposa_aniso(Tex *tex, filterfunc = area_sample; } - texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; + texres->tin = texres->trgba[3] = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = 0.0f; /* we need to set retval OK, otherwise texture code generates normals itself... */ retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB; @@ -1332,27 +1321,27 @@ static int imagewraposa_aniso(Tex *tex, if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) { /* color & normal */ filterfunc(texres, curibuf, fx, fy, &AFD); - val1 = texres->tr + texres->tg + texres->tb; + val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2]; filterfunc(&texr, curibuf, fx + dxt[0], fy + dxt[1], &AFD); - val2 = texr.tr + texr.tg + texr.tb; + val2 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2]; filterfunc(&texr, curibuf, fx + dyt[0], fy + dyt[1], &AFD); - val3 = texr.tr + texr.tg + texr.tb; + val3 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2]; /* don't switch x or y! */ texres->nor[0] = val1 - val2; texres->nor[1] = val1 - val3; if (previbuf != curibuf) { /* interpolate */ filterfunc(&texr, previbuf, fx, fy, &AFD); /* rgb */ - texres->tr += levf * (texr.tr - texres->tr); - texres->tg += levf * (texr.tg - texres->tg); - texres->tb += levf * (texr.tb - texres->tb); - texres->ta += levf * (texr.ta - texres->ta); + texres->trgba[0] += levf * (texr.trgba[0] - texres->trgba[0]); + texres->trgba[1] += levf * (texr.trgba[1] - texres->trgba[1]); + texres->trgba[2] += levf * (texr.trgba[2] - texres->trgba[2]); + texres->trgba[3] += levf * (texr.trgba[3] - texres->trgba[3]); /* normal */ - val1 += levf * ((texr.tr + texr.tg + texr.tb) - val1); + val1 += levf * ((texr.trgba[0] + texr.trgba[1] + texr.trgba[2]) - val1); filterfunc(&texr, previbuf, fx + dxt[0], fy + dxt[1], &AFD); - val2 += levf * ((texr.tr + texr.tg + texr.tb) - val2); + val2 += levf * ((texr.trgba[0] + texr.trgba[1] + texr.trgba[2]) - val2); filterfunc(&texr, previbuf, fx + dyt[0], fy + dyt[1], &AFD); - val3 += levf * ((texr.tr + texr.tg + texr.tb) - val3); + val3 += levf * ((texr.trgba[0] + texr.trgba[1] + texr.trgba[2]) - val3); texres->nor[0] = val1 - val2; /* vals have been interpolated above! */ texres->nor[1] = val1 - val3; } @@ -1361,10 +1350,10 @@ static int imagewraposa_aniso(Tex *tex, filterfunc(texres, curibuf, fx, fy, &AFD); if (previbuf != curibuf) { /* interpolate */ filterfunc(&texr, previbuf, fx, fy, &AFD); - texres->tr += levf * (texr.tr - texres->tr); - texres->tg += levf * (texr.tg - texres->tg); - texres->tb += levf * (texr.tb - texres->tb); - texres->ta += levf * (texr.ta - texres->ta); + texres->trgba[0] += levf * (texr.trgba[0] - texres->trgba[0]); + texres->trgba[1] += levf * (texr.trgba[1] - texres->trgba[1]); + texres->trgba[2] += levf * (texr.trgba[2] - texres->trgba[2]); + texres->trgba[3] += levf * (texr.trgba[3] - texres->trgba[3]); } if (tex->texfilter != TXF_EWA) { @@ -1402,11 +1391,11 @@ static int imagewraposa_aniso(Tex *tex, if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) { /* color & normal */ filterfunc(texres, ibuf, fx, fy, &AFD); - val1 = texres->tr + texres->tg + texres->tb; + val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2]; filterfunc(&texr, ibuf, fx + dxt[0], fy + dxt[1], &AFD); - val2 = texr.tr + texr.tg + texr.tb; + val2 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2]; filterfunc(&texr, ibuf, fx + dyt[0], fy + dyt[1], &AFD); - val3 = texr.tr + texr.tg + texr.tb; + val3 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2]; /* don't switch x or y! */ texres->nor[0] = val1 - val2; texres->nor[1] = val1 - val3; @@ -1420,13 +1409,14 @@ static int imagewraposa_aniso(Tex *tex, } if (tex->imaflag & TEX_CALCALPHA) { - texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb); + texres->trgba[3] = texres->tin = texres->trgba[3] * + max_fff(texres->trgba[0], texres->trgba[1], texres->trgba[2]); } else { - texres->tin = texres->ta; + texres->tin = texres->trgba[3]; } if (tex->flag & TEX_NEGALPHA) { - texres->ta = 1.0f - texres->ta; + texres->trgba[3] = 1.0f - texres->trgba[3]; } if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { /* normal from color */ @@ -1436,9 +1426,9 @@ static int imagewraposa_aniso(Tex *tex, * the normal used in the renderer points inward. It is generated * this way in calc_vertexnormals(). Should this ever change * this negate must be removed. */ - texres->nor[0] = -2.0f * (texres->tr - 0.5f); - texres->nor[1] = 2.0f * (texres->tg - 0.5f); - texres->nor[2] = 2.0f * (texres->tb - 0.5f); + texres->nor[0] = -2.0f * (texres->trgba[0] - 0.5f); + texres->nor[1] = 2.0f * (texres->trgba[1] - 0.5f); + texres->nor[2] = 2.0f * (texres->trgba[2] - 0.5f); } /* de-premul, this is being pre-multiplied in shade_input_do_shade() @@ -1449,11 +1439,11 @@ static int imagewraposa_aniso(Tex *tex, /* brecht: tried to fix this, see "TXF alpha" comments */ /* do not de-premul for generated alpha, it is already in straight */ - if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { - fx = 1.0f / texres->ta; - texres->tr *= fx; - texres->tg *= fx; - texres->tb *= fx; + if (texres->trgba[3] != 1.0f && texres->trgba[3] > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { + fx = 1.0f / texres->trgba[3]; + texres->trgba[0] *= fx; + texres->trgba[1] *= fx; + texres->trgba[2] *= fx; } if (ima) { @@ -1490,7 +1480,7 @@ int imagewraposa(Tex *tex, return imagewraposa_aniso(tex, ima, ibuf, texvec, dxt, dyt, texres, pool, skip_load_image); } - texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; + texres->tin = texres->trgba[3] = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = 0.0f; /* we need to set retval OK, otherwise texture code generates normals itself... */ retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB; @@ -1795,7 +1785,7 @@ int imagewraposa(Tex *tex, boxsample( curibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend); - val1 = texres->tr + texres->tg + texres->tb; + val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2]; boxsample(curibuf, fx - minx + dxt[0], fy - miny + dxt[1], @@ -1804,7 +1794,7 @@ int imagewraposa(Tex *tex, &texr, imaprepeat, imapextend); - val2 = texr.tr + texr.tg + texr.tb; + val2 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2]; boxsample(curibuf, fx - minx + dyt[0], fy - miny + dyt[1], @@ -1813,7 +1803,7 @@ int imagewraposa(Tex *tex, &texr, imaprepeat, imapextend); - val3 = texr.tr + texr.tg + texr.tb; + val3 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2]; /* don't switch x or y! */ texres->nor[0] = (val1 - val2); @@ -1827,20 +1817,20 @@ int imagewraposa(Tex *tex, /* calc rgb */ dx = 2.0f * (pixsize - maxd) / pixsize; if (dx >= 1.0f) { - texres->ta = texr.ta; - texres->tb = texr.tb; - texres->tg = texr.tg; - texres->tr = texr.tr; + texres->trgba[3] = texr.trgba[3]; + texres->trgba[2] = texr.trgba[2]; + texres->trgba[1] = texr.trgba[1]; + texres->trgba[0] = texr.trgba[0]; } else { dy = 1.0f - dx; - texres->tb = dy * texres->tb + dx * texr.tb; - texres->tg = dy * texres->tg + dx * texr.tg; - texres->tr = dy * texres->tr + dx * texr.tr; - texres->ta = dy * texres->ta + dx * texr.ta; + texres->trgba[2] = dy * texres->trgba[2] + dx * texr.trgba[2]; + texres->trgba[1] = dy * texres->trgba[1] + dx * texr.trgba[1]; + texres->trgba[0] = dy * texres->trgba[0] + dx * texr.trgba[0]; + texres->trgba[3] = dy * texres->trgba[3] + dx * texr.trgba[3]; } - val1 = dy * val1 + dx * (texr.tr + texr.tg + texr.tb); + val1 = dy * val1 + dx * (texr.trgba[0] + texr.trgba[1] + texr.trgba[2]); boxsample(previbuf, fx - minx + dxt[0], fy - miny + dxt[1], @@ -1849,7 +1839,7 @@ int imagewraposa(Tex *tex, &texr, imaprepeat, imapextend); - val2 = dy * val2 + dx * (texr.tr + texr.tg + texr.tb); + val2 = dy * val2 + dx * (texr.trgba[0] + texr.trgba[1] + texr.trgba[2]); boxsample(previbuf, fx - minx + dyt[0], fy - miny + dyt[1], @@ -1858,17 +1848,17 @@ int imagewraposa(Tex *tex, &texr, imaprepeat, imapextend); - val3 = dy * val3 + dx * (texr.tr + texr.tg + texr.tb); + val3 = dy * val3 + dx * (texr.trgba[0] + texr.trgba[1] + texr.trgba[2]); texres->nor[0] = (val1 - val2); /* vals have been interpolated above! */ texres->nor[1] = (val1 - val3); if (dx < 1.0f) { dy = 1.0f - dx; - texres->tb = dy * texres->tb + dx * texr.tb; - texres->tg = dy * texres->tg + dx * texr.tg; - texres->tr = dy * texres->tr + dx * texr.tr; - texres->ta = dy * texres->ta + dx * texr.ta; + texres->trgba[2] = dy * texres->trgba[2] + dx * texr.trgba[2]; + texres->trgba[1] = dy * texres->trgba[1] + dx * texr.trgba[1]; + texres->trgba[0] = dy * texres->trgba[0] + dx * texr.trgba[0]; + texres->trgba[3] = dy * texres->trgba[3] + dx * texr.trgba[3]; } } texres->nor[0] *= bumpscale; @@ -1888,17 +1878,17 @@ int imagewraposa(Tex *tex, fx = 2.0f * (pixsize - maxd) / pixsize; if (fx >= 1.0f) { - texres->ta = texr.ta; - texres->tb = texr.tb; - texres->tg = texr.tg; - texres->tr = texr.tr; + texres->trgba[3] = texr.trgba[3]; + texres->trgba[2] = texr.trgba[2]; + texres->trgba[1] = texr.trgba[1]; + texres->trgba[0] = texr.trgba[0]; } else { fy = 1.0f - fx; - texres->tb = fy * texres->tb + fx * texr.tb; - texres->tg = fy * texres->tg + fx * texr.tg; - texres->tr = fy * texres->tr + fx * texr.tr; - texres->ta = fy * texres->ta + fx * texr.ta; + texres->trgba[2] = fy * texres->trgba[2] + fx * texr.trgba[2]; + texres->trgba[1] = fy * texres->trgba[1] + fx * texr.trgba[1]; + texres->trgba[0] = fy * texres->trgba[0] + fx * texr.trgba[0]; + texres->trgba[3] = fy * texres->trgba[3] + fx * texr.trgba[3]; } } } @@ -1917,7 +1907,7 @@ int imagewraposa(Tex *tex, if (texres->nor && (tex->imaflag & TEX_NORMALMAP) == 0) { boxsample(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend); - val1 = texres->tr + texres->tg + texres->tb; + val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2]; boxsample(ibuf, fx - minx + dxt[0], fy - miny + dxt[1], @@ -1926,7 +1916,7 @@ int imagewraposa(Tex *tex, &texr, imaprepeat, imapextend); - val2 = texr.tr + texr.tg + texr.tb; + val2 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2]; boxsample(ibuf, fx - minx + dyt[0], fy - miny + dyt[1], @@ -1935,7 +1925,7 @@ int imagewraposa(Tex *tex, &texr, imaprepeat, imapextend); - val3 = texr.tr + texr.tg + texr.tb; + val3 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2]; /* don't switch x or y! */ texres->nor[0] = (val1 - val2); @@ -1947,14 +1937,15 @@ int imagewraposa(Tex *tex, } if (tex->imaflag & TEX_CALCALPHA) { - texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb); + texres->trgba[3] = texres->tin = texres->trgba[3] * + max_fff(texres->trgba[0], texres->trgba[1], texres->trgba[2]); } else { - texres->tin = texres->ta; + texres->tin = texres->trgba[3]; } if (tex->flag & TEX_NEGALPHA) { - texres->ta = 1.0f - texres->ta; + texres->trgba[3] = 1.0f - texres->trgba[3]; } if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { @@ -1963,15 +1954,15 @@ int imagewraposa(Tex *tex, * It needs to be done because in Blender the normal used in the renderer points inward. * It is generated this way in #calc_vertexnormals(). * Should this ever change this negate must be removed. */ - texres->nor[0] = -2.0f * (texres->tr - 0.5f); - texres->nor[1] = 2.0f * (texres->tg - 0.5f); - texres->nor[2] = 2.0f * (texres->tb - 0.5f); + texres->nor[0] = -2.0f * (texres->trgba[0] - 0.5f); + texres->nor[1] = 2.0f * (texres->trgba[1] - 0.5f); + texres->nor[2] = 2.0f * (texres->trgba[2] - 0.5f); } /* de-premul, this is being pre-multiplied in shade_input_do_shade() */ /* do not de-premul for generated alpha, it is already in straight */ - if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { - mul_v3_fl(&texres->tr, 1.0f / texres->ta); + if (texres->trgba[3] != 1.0f && texres->trgba[3] > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { + mul_v3_fl(texres->trgba, 1.0f / texres->trgba[3]); } if (ima) { @@ -1996,7 +1987,7 @@ void image_sample( texres.talpha = true; /* boxsample expects to be initialized */ boxsample(ibuf, fx, fy, fx + dx, fy + dy, &texres, 0, 1); - copy_v4_v4(result, &texres.tr); + copy_v4_v4(result, texres.trgba); ima->flag |= IMA_USED_FOR_RENDER; @@ -2020,5 +2011,5 @@ void ibuf_sample(ImBuf *ibuf, float fx, float fy, float dx, float dy, float resu ewa_eval(&texres, ibuf, fx, fy, &AFD); - copy_v4_v4(result, &texres.tr); + copy_v4_v4(result, texres.trgba); } diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc index 0e47c2cec8a..7aff01242e3 100644 --- a/source/blender/render/intern/texture_margin.cc +++ b/source/blender/render/intern/texture_margin.cc @@ -43,20 +43,22 @@ #include "RE_texture_margin.h" #include <algorithm> -#include <math.h> +#include <cmath> #include <valarray> namespace blender::render::texturemargin { -/* The map class contains both a pixel map which maps out polygon indices for all UV-polygons and +/** + * The map class contains both a pixel map which maps out polygon indices for all UV-polygons and * adjacency tables. */ class TextureMarginMap { - static const int directions[4][2]; + static const int directions[8][2]; + static const int distances[8]; - /* Maps UV-edges to their corresponding UV-edge. */ + /** Maps UV-edges to their corresponding UV-edge. */ Vector<int> loop_adjacency_map_; - /* Maps UV-edges to their corresponding polygon. */ + /** Maps UV-edges to their corresponding polygon. */ Vector<int> loop_to_poly_map_; int w_, h_; @@ -121,18 +123,17 @@ class TextureMarginMap { void rasterize_tri(float *v1, float *v2, float *v3, uint32_t value, char *mask) { /* NOTE: This is not thread safe, because the value to be written by the rasterizer is - * a class member. If this is ever made multi-threaded each thread needs to get it's own. */ + * a class member. If this is ever made multi-threaded each thread needs to get its own. */ value_to_store_ = value; mask_ = mask; zspan_scanconvert( &zspan_, this, &(v1[0]), &(v2[0]), &(v3[0]), TextureMarginMap::zscan_store_pixel); } - static void zscan_store_pixel(void *map, int x, int y, float, float) + static void zscan_store_pixel( + void *map, int x, int y, [[maybe_unused]] float u, [[maybe_unused]] float v) { - /* NOTE: Not thread safe, see comment above. - * - */ + /* NOTE: Not thread safe, see comment above. */ TextureMarginMap *m = static_cast<TextureMarginMap *>(map); m->set_pixel(x, y, m->value_to_store_); if (m->mask_) { @@ -141,17 +142,18 @@ class TextureMarginMap { } /* The map contains 2 kinds of pixels: DijkstraPixels and polygon indices. The top bit determines - * what kind it is. With the top bit set, it is a 'dijkstra' pixel. The bottom 3 bits encode the - * direction of the shortest path and the remaining 28 bits are used to store the distance. If + * what kind it is. With the top bit set, it is a 'dijkstra' pixel. The bottom 4 bits encode the + * direction of the shortest path and the remaining 27 bits are used to store the distance. If * the top bit is not set, the rest of the bits is used to store the polygon index. */ -#define PackDijkstraPixel(dist, dir) (0x80000000 + ((dist) << 3) + (dir)) -#define DijkstraPixelGetDistance(dp) (((dp) ^ 0x80000000) >> 3) -#define DijkstraPixelGetDirection(dp) ((dp)&0x7) +#define PackDijkstraPixel(dist, dir) (0x80000000 + ((dist) << 4) + (dir)) +#define DijkstraPixelGetDistance(dp) (((dp) ^ 0x80000000) >> 4) +#define DijkstraPixelGetDirection(dp) ((dp)&0xF) #define IsDijkstraPixel(dp) ((dp)&0x80000000) #define DijkstraPixelIsUnset(dp) ((dp) == 0xFFFFFFFF) - /* Use dijkstra's algorithm to 'grow' a border around the polygons marked in the map. + /** + * Use dijkstra's algorithm to 'grow' a border around the polygons marked in the map. * For each pixel mark which direction is the shortest way to a polygon. */ void grow_dijkstra(int margin) @@ -172,13 +174,13 @@ class TextureMarginMap { for (int y = 0; y < h_; y++) { for (int x = 0; x < w_; x++) { if (DijkstraPixelIsUnset(get_pixel(x, y))) { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 8; i++) { int xx = x - directions[i][0]; int yy = y - directions[i][1]; if (xx >= 0 && xx < w_ && yy >= 0 && yy < w_ && !IsDijkstraPixel(get_pixel(xx, yy))) { - set_pixel(x, y, PackDijkstraPixel(1, i)); - active_pixels.append(DijkstraActivePixel(1, x, y)); + set_pixel(x, y, PackDijkstraPixel(distances[i], i)); + active_pixels.append(DijkstraActivePixel(distances[i], x, y)); break; } } @@ -186,8 +188,10 @@ class TextureMarginMap { } } - // std::make_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun); - // Not strictly needed because at this point it already is a heap. + /* Not strictly needed because at this point it already is a heap. */ +#if 0 + std::make_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun); +#endif while (active_pixels.size()) { std::pop_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun); @@ -195,17 +199,16 @@ class TextureMarginMap { int dist = p.distance; - dist++; - if (dist < margin) { - for (int i = 0; i < 4; i++) { + if (dist < 2 * (margin + 1)) { + for (int i = 0; i < 8; i++) { int x = p.x + directions[i][0]; int y = p.y + directions[i][1]; if (x >= 0 && x < w_ && y >= 0 && y < h_) { uint32_t dp = get_pixel(x, y); - if (IsDijkstraPixel(dp) && (DijkstraPixelGetDistance(dp) > dist)) { - BLI_assert(abs((int)DijkstraPixelGetDirection(dp) - (int)i) != 2); - set_pixel(x, y, PackDijkstraPixel(dist, i)); - active_pixels.append(DijkstraActivePixel(dist, x, y)); + if (IsDijkstraPixel(dp) && (DijkstraPixelGetDistance(dp) > dist + distances[i])) { + BLI_assert(DijkstraPixelGetDirection(dp) != i); + set_pixel(x, y, PackDijkstraPixel(dist + distances[i], i)); + active_pixels.append(DijkstraActivePixel(dist + distances[i], x, y)); std::push_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun); } } @@ -214,7 +217,8 @@ class TextureMarginMap { } } - /* Walk over the map and for margin pixels follow the direction stored in the bottom 3 + /** + * Walk over the map and for margin pixels follow the direction stored in the bottom 3 * bits back to the polygon. * Then look up the pixel from the next polygon. */ @@ -235,7 +239,7 @@ class TextureMarginMap { xx -= directions[direction][0]; yy -= directions[direction][1]; dp = get_pixel(xx, yy); - dist--; + dist -= distances[direction]; BLI_assert(!dist || (dist == DijkstraPixelGetDistance(dp))); direction = DijkstraPixelGetDirection(dp); } @@ -248,7 +252,7 @@ class TextureMarginMap { int other_poly; bool found_pixel_in_polygon = false; - if (lookup_pixel(x, y, poly, &destX, &destY, &other_poly)) { + if (lookup_pixel_polygon_neighbourhood(x, y, &poly, &destX, &destY, &other_poly)) { for (int i = 0; i < maxPolygonSteps; i++) { /* Force to pixel grid. */ @@ -260,8 +264,12 @@ class TextureMarginMap { break; } + float dist_to_edge; /* Look up again, but starting from the polygon we were expected to land in. */ - lookup_pixel(nx, ny, other_poly, &destX, &destY, &other_poly); + if (!lookup_pixel(nx, ny, other_poly, &destX, &destY, &other_poly, &dist_to_edge)) { + found_pixel_in_polygon = false; + break; + } } if (found_pixel_in_polygon) { @@ -319,12 +327,67 @@ class TextureMarginMap { } } - /* Find which edge of the src_poly is closest to x,y. Look up it's adjacent UV-edge and polygon. + /** + * Call lookup_pixel for the start_poly. If that fails, try the adjacent polygons as well. + * Because the Dijkstra is not very exact in determining which polygon is the closest, the + * polygon we need can be the one next to the one the Dijkstra map provides. To prevent missing + * pixels also check the neighboring polygons. + */ + bool lookup_pixel_polygon_neighbourhood( + float x, float y, uint32_t *r_start_poly, float *r_destx, float *r_desty, int *r_other_poly) + { + float found_dist; + if (lookup_pixel(x, y, *r_start_poly, r_destx, r_desty, r_other_poly, &found_dist)) { + return true; + } + + int loopstart = mpoly_[*r_start_poly].loopstart; + int totloop = mpoly_[*r_start_poly].totloop; + + float destx, desty; + int foundpoly; + + float mindist = -1.f; + + /* Loop over all adjacent polygons and determine which edge is closest. + * This could be optimized by only inspecting neighbors which are on the edge of an island. + * But it seems fast enough for now and that would add a lot of complexity. */ + for (int i = 0; i < totloop; i++) { + int otherloop = loop_adjacency_map_[i + loopstart]; + + if (otherloop < 0) { + continue; + } + + uint32_t poly = loop_to_poly_map_[otherloop]; + + if (lookup_pixel(x, y, poly, &destx, &desty, &foundpoly, &found_dist)) { + if (mindist < 0.f || found_dist < mindist) { + mindist = found_dist; + *r_other_poly = foundpoly; + *r_destx = destx; + *r_desty = desty; + *r_start_poly = poly; + } + } + } + + return mindist >= 0.f; + } + + /** + * Find which edge of the src_poly is closest to x,y. Look up its adjacent UV-edge and polygon. * Then return the location of the equivalent pixel in the other polygon. * Returns true if a new pixel location was found, false if it wasn't, which can happen if the - * margin pixel is on a corner, or the UV-edge doesn't have an adjacent polygon. */ - bool lookup_pixel( - float x, float y, int src_poly, float *r_destx, float *r_desty, int *r_other_poly) + * margin pixel is on a corner, or the UV-edge doesn't have an adjacent polygon. + */ + bool lookup_pixel(float x, + float y, + int src_poly, + float *r_destx, + float *r_desty, + int *r_other_poly, + float *r_dist_to_edge) { float2 point(x, y); @@ -384,6 +447,8 @@ class TextureMarginMap { return false; } + *r_dist_to_edge = found_dist; + /* Get the 'other' edge. I.E. the UV edge from the neighbor polygon. */ int other_edge = loop_adjacency_map_[found_edge]; @@ -405,7 +470,7 @@ class TextureMarginMap { float2 other_edgepoint1 = uv_to_xy(mloopuv_[other_edge]); float2 other_edgepoint2 = uv_to_xy(mloopuv_[other_edge2]); - /* Calculate the vector from the order edges last point to it's first point. */ + /* Calculate the vector from the order edges last point to its first point. */ float2 other_ab = other_edgepoint1 - other_edgepoint2; float2 other_reflect_point = other_edgepoint2 + (found_t * other_ab); float2 perpendicular_other_ab; @@ -424,7 +489,9 @@ class TextureMarginMap { } }; // class TextureMarginMap -const int TextureMarginMap::directions[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; +const int TextureMarginMap::directions[8][2] = { + {-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}}; +const int TextureMarginMap::distances[8] = {2, 3, 2, 3, 2, 3, 2, 3}; static void generate_margin(ImBuf *ibuf, char *mask, @@ -441,17 +508,17 @@ static void generate_margin(ImBuf *ibuf, int tottri; MLoopTri const *looptri; - MLoopTri *looptri_mem = NULL; + MLoopTri *looptri_mem = nullptr; if (me) { - BLI_assert(dm == NULL); + BLI_assert(dm == nullptr); totpoly = me->totpoly; totloop = me->totloop; totedge = me->totedge; mpoly = me->mpoly; mloop = me->mloop; - if ((uv_layer == NULL) || (uv_layer[0] == '\0')) { + if ((uv_layer == nullptr) || (uv_layer[0] == '\0')) { mloopuv = static_cast<MLoopUV const *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV)); } else { @@ -467,9 +534,8 @@ static void generate_margin(ImBuf *ibuf, looptri = looptri_mem; } else { - BLI_assert(dm != NULL); - BLI_assert(me == NULL); - BLI_assert(mloopuv == NULL); + BLI_assert(dm != nullptr); + BLI_assert(me == nullptr); totpoly = dm->getNumPolys(dm); totedge = dm->getNumEdges(dm); totloop = dm->getNumLoops(dm); @@ -509,8 +575,10 @@ static void generate_margin(ImBuf *ibuf, vec[a][1] = uv[1] * (float)ibuf->y - (0.5f + 0.002f); } - BLI_assert(lt->poly < 0x80000000); // NOTE: we need the top bit for the dijkstra distance map - map.rasterize_tri(vec[0], vec[1], vec[2], lt->poly, draw_new_mask ? mask : NULL); + /* NOTE: we need the top bit for the dijkstra distance map. */ + BLI_assert(lt->poly < 0x80000000); + + map.rasterize_tri(vec[0], vec[1], vec[2], lt->poly, draw_new_mask ? mask : nullptr); } char *tmpmask = (char *)MEM_dupallocN(mask); @@ -543,7 +611,7 @@ static void generate_margin(ImBuf *ibuf, void RE_generate_texturemargin_adjacentfaces( ImBuf *ibuf, char *mask, const int margin, const Mesh *me, char const *uv_layer) { - blender::render::texturemargin::generate_margin(ibuf, mask, margin, me, NULL, uv_layer); + blender::render::texturemargin::generate_margin(ibuf, mask, margin, me, nullptr, uv_layer); } void RE_generate_texturemargin_adjacentfaces_dm(ImBuf *ibuf, @@ -551,5 +619,5 @@ void RE_generate_texturemargin_adjacentfaces_dm(ImBuf *ibuf, const int margin, DerivedMesh *dm) { - blender::render::texturemargin::generate_margin(ibuf, mask, margin, NULL, dm, NULL); + blender::render::texturemargin::generate_margin(ibuf, mask, margin, nullptr, dm, nullptr); } diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index 683260f86cb..9abaeb3739f 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -687,7 +687,7 @@ static int pointdensity(PointDensity *pd, static void pointdensity_color( PointDensity *pd, TexResult *texres, float age, const float vec[3], const float col[3]) { - texres->tr = texres->tg = texres->tb = texres->ta = 1.0f; + copy_v4_fl(texres->trgba, 1.0f); if (pd->source == TEX_PD_PSYS) { float rgba[4]; @@ -697,9 +697,9 @@ static void pointdensity_color( if (pd->coba) { if (BKE_colorband_evaluate(pd->coba, age, rgba)) { texres->talpha = true; - copy_v3_v3(&texres->tr, rgba); + copy_v3_v3(texres->trgba, rgba); texres->tin *= rgba[3]; - texres->ta = texres->tin; + texres->trgba[3] = texres->tin; } } break; @@ -709,17 +709,17 @@ static void pointdensity_color( if (pd->coba) { if (BKE_colorband_evaluate(pd->coba, speed, rgba)) { texres->talpha = true; - copy_v3_v3(&texres->tr, rgba); + copy_v3_v3(texres->trgba, rgba); texres->tin *= rgba[3]; - texres->ta = texres->tin; + texres->trgba[3] = texres->tin; } } break; } case TEX_PD_COLOR_PARTVEL: texres->talpha = true; - mul_v3_v3fl(&texres->tr, vec, pd->speed_scale); - texres->ta = texres->tin; + mul_v3_v3fl(texres->trgba, vec, pd->speed_scale); + texres->trgba[3] = texres->tin; break; case TEX_PD_COLOR_CONSTANT: default: @@ -732,24 +732,24 @@ static void pointdensity_color( switch (pd->ob_color_source) { case TEX_PD_COLOR_VERTCOL: texres->talpha = true; - copy_v3_v3(&texres->tr, col); - texres->ta = texres->tin; + copy_v3_v3(texres->trgba, col); + texres->trgba[3] = texres->tin; break; case TEX_PD_COLOR_VERTWEIGHT: texres->talpha = true; if (pd->coba && BKE_colorband_evaluate(pd->coba, col[0], rgba)) { - copy_v3_v3(&texres->tr, rgba); + copy_v3_v3(texres->trgba, rgba); texres->tin *= rgba[3]; } else { - copy_v3_v3(&texres->tr, col); + copy_v3_v3(texres->trgba, col); } - texres->ta = texres->tin; + texres->trgba[3] = texres->tin; break; case TEX_PD_COLOR_VERTNOR: texres->talpha = true; - copy_v3_v3(&texres->tr, col); - texres->ta = texres->tin; + copy_v3_v3(texres->trgba, col); + texres->trgba[3] = texres->tin; break; case TEX_PD_COLOR_CONSTANT: default: @@ -915,7 +915,7 @@ static void point_density_sample_func(void *__restrict data_v, pointdensity(pd, texvec, &texres, vec, &age, col); pointdensity_color(pd, &texres, age, vec, col); - copy_v3_v3(&values[index * 4 + 0], &texres.tr); + copy_v3_v3(&values[index * 4 + 0], texres.trgba); values[index * 4 + 3] = texres.tin; } } diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c index f563cb9f84a..ee45810f445 100644 --- a/source/blender/render/intern/texture_procedural.c +++ b/source/blender/render/intern/texture_procedural.c @@ -43,15 +43,15 @@ #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" -#include "BKE_image.h" -#include "BKE_node.h" - #include "BKE_colorband.h" +#include "BKE_image.h" #include "BKE_material.h" +#include "BKE_node.h" #include "BKE_scene.h" - #include "BKE_texture.h" +#include "NOD_texture.h" + #include "MEM_guardedalloc.h" #include "render_types.h" @@ -212,23 +212,23 @@ static int clouds(const Tex *tex, const float texvec[3], TexResult *texres) if (tex->stype == TEX_COLOR) { /* in this case, int. value should really be computed from color, * and bumpnormal from that, would be too slow, looks ok as is */ - texres->tr = texres->tin; - texres->tg = BLI_noise_generic_turbulence(tex->noisesize, - texvec[1], - texvec[0], - texvec[2], - tex->noisedepth, - (tex->noisetype != TEX_NOISESOFT), - tex->noisebasis); - texres->tb = BLI_noise_generic_turbulence(tex->noisesize, - texvec[1], - texvec[2], - texvec[0], - tex->noisedepth, - (tex->noisetype != TEX_NOISESOFT), - tex->noisebasis); + texres->trgba[0] = texres->tin; + texres->trgba[1] = BLI_noise_generic_turbulence(tex->noisesize, + texvec[1], + texvec[0], + texvec[2], + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + texres->trgba[2] = BLI_noise_generic_turbulence(tex->noisesize, + texvec[1], + texvec[2], + texvec[0], + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); BRICONTRGB; - texres->ta = 1.0; + texres->trgba[3] = 1.0; return (rv | TEX_RGB); } @@ -453,14 +453,14 @@ static int magic(const Tex *tex, const float texvec[3], TexResult *texres) y /= turb; z /= turb; } - texres->tr = 0.5f - x; - texres->tg = 0.5f - y; - texres->tb = 0.5f - z; + texres->trgba[0] = 0.5f - x; + texres->trgba[1] = 0.5f - y; + texres->trgba[2] = 0.5f - z; - texres->tin = (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb); + texres->tin = (1.0f / 3.0f) * (texres->trgba[0] + texres->trgba[1] + texres->trgba[2]); BRICONTRGB; - texres->ta = 1.0f; + texres->trgba[3] = 1.0f; return TEX_RGB; } @@ -767,21 +767,21 @@ static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres) if (tex->vn_coltype) { float ca[3]; /* cell color */ BLI_noise_cell_v3(pa[0], pa[1], pa[2], ca); - texres->tr = aw1 * ca[0]; - texres->tg = aw1 * ca[1]; - texres->tb = aw1 * ca[2]; + texres->trgba[0] = aw1 * ca[0]; + texres->trgba[1] = aw1 * ca[1]; + texres->trgba[2] = aw1 * ca[2]; BLI_noise_cell_v3(pa[3], pa[4], pa[5], ca); - texres->tr += aw2 * ca[0]; - texres->tg += aw2 * ca[1]; - texres->tb += aw2 * ca[2]; + texres->trgba[0] += aw2 * ca[0]; + texres->trgba[1] += aw2 * ca[1]; + texres->trgba[2] += aw2 * ca[2]; BLI_noise_cell_v3(pa[6], pa[7], pa[8], ca); - texres->tr += aw3 * ca[0]; - texres->tg += aw3 * ca[1]; - texres->tb += aw3 * ca[2]; + texres->trgba[0] += aw3 * ca[0]; + texres->trgba[1] += aw3 * ca[1]; + texres->trgba[2] += aw3 * ca[2]; BLI_noise_cell_v3(pa[9], pa[10], pa[11], ca); - texres->tr += aw4 * ca[0]; - texres->tg += aw4 * ca[1]; - texres->tb += aw4 * ca[2]; + texres->trgba[0] += aw4 * ca[0]; + texres->trgba[1] += aw4 * ca[1]; + texres->trgba[2] += aw4 * ca[2]; if (tex->vn_coltype >= 2) { float t1 = (da[1] - da[0]) * 10; if (t1 > 1) { @@ -793,14 +793,14 @@ static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres) else { t1 *= sc; } - texres->tr *= t1; - texres->tg *= t1; - texres->tb *= t1; + texres->trgba[0] *= t1; + texres->trgba[1] *= t1; + texres->trgba[2] *= t1; } else { - texres->tr *= sc; - texres->tg *= sc; - texres->tb *= sc; + texres->trgba[0] *= sc; + texres->trgba[1] *= sc; + texres->trgba[2] *= sc; } } @@ -821,7 +821,7 @@ static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres) if (tex->vn_coltype) { BRICONTRGB; - texres->ta = 1.0; + texres->trgba[3] = 1.0; return (rv | TEX_RGB); } @@ -1270,10 +1270,7 @@ static int multitex(Tex *tex, float col[4]; if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) { texres->talpha = true; - texres->tr = col[0]; - texres->tg = col[1]; - texres->tb = col[2]; - texres->ta = col[3]; + copy_v4_v4(texres->trgba, col); retval |= TEX_RGB; } } @@ -1330,7 +1327,7 @@ static int multitex_nodes_intern(Tex *tex, /* don't linearize float buffers, assumed to be linear */ if (ibuf != NULL && ibuf->rect_float == NULL && (rgbnor & TEX_RGB) && scene_color_manage) { - IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace); + IMB_colormanagement_colorspace_to_scene_linear_v3(texres->trgba, ibuf->rect_colorspace); } BKE_image_pool_release_ibuf(tex->ima, ibuf, pool); @@ -1375,7 +1372,7 @@ static int multitex_nodes_intern(Tex *tex, /* don't linearize float buffers, assumed to be linear */ if (ibuf != NULL && ibuf->rect_float == NULL && (rgbnor & TEX_RGB) && scene_color_manage) { - IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace); + IMB_colormanagement_colorspace_to_scene_linear_v3(texres->trgba, ibuf->rect_colorspace); } BKE_image_pool_release_ibuf(tex->ima, ibuf, pool); @@ -1768,19 +1765,14 @@ bool RE_texture_evaluate(const MTex *mtex, true); if (rgb) { - texr.tin = IMB_colormanagement_get_luminance(&texr.tr); + texr.tin = IMB_colormanagement_get_luminance(texr.trgba); } else { - texr.tr = mtex->r; - texr.tg = mtex->g; - texr.tb = mtex->b; + copy_v3_fl3(texr.trgba, mtex->r, mtex->g, mtex->b); } *r_intensity = texr.tin; - r_rgba[0] = texr.tr; - r_rgba[1] = texr.tg; - r_rgba[2] = texr.tb; - r_rgba[3] = texr.ta; + copy_v4_v4(r_rgba, texr.trgba); return (rgb != 0); } diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h index 85f44ab914f..23ad845fda1 100644 --- a/source/blender/sequencer/SEQ_add.h +++ b/source/blender/sequencer/SEQ_add.h @@ -64,7 +64,8 @@ typedef struct SeqLoadData { bool use_multiview; char views_format; struct Stereo3dFormat *stereo3d_format; - bool allow_invalid_file; /* Used by RNA API to create placeholder strips. */ + bool allow_invalid_file; /* Used by RNA API to create placeholder strips. */ + double r_video_stream_start; /* For AV synchronization. Set by `SEQ_add_movie_strip`. */ } SeqLoadData; /** @@ -108,8 +109,7 @@ struct Sequence *SEQ_add_image_strip(struct Main *bmain, struct Sequence *SEQ_add_sound_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, - struct SeqLoadData *load_data, - double audio_offset); + struct SeqLoadData *load_data); /** * Add meta strip. * @@ -133,8 +133,7 @@ struct Sequence *SEQ_add_meta_strip(struct Scene *scene, struct Sequence *SEQ_add_movie_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, - struct SeqLoadData *load_data, - double *r_start_offset); + struct SeqLoadData *load_data); /** * Add scene strip. * diff --git a/source/blender/sequencer/SEQ_proxy.h b/source/blender/sequencer/SEQ_proxy.h index 7bfe932ff1c..164b279245c 100644 --- a/source/blender/sequencer/SEQ_proxy.h +++ b/source/blender/sequencer/SEQ_proxy.h @@ -42,7 +42,8 @@ bool SEQ_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list, - struct ListBase *queue); + struct ListBase *queue, + bool build_only_on_bad_performance); void SEQ_proxy_rebuild(struct SeqIndexBuildContext *context, short *stop, short *do_update, diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c index 2cb2ba13fb5..5982f89a287 100644 --- a/source/blender/sequencer/intern/proxy.c +++ b/source/blender/sequencer/intern/proxy.c @@ -415,7 +415,8 @@ bool SEQ_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list, - ListBase *queue) + ListBase *queue, + bool build_only_on_bad_performance) { SeqIndexBuildContext *context; Sequence *nseq; @@ -476,7 +477,8 @@ bool SEQ_proxy_rebuild_context(Main *bmain, context->size_flags, context->quality, context->overwrite, - file_list); + file_list, + build_only_on_bad_performance); } if (!context->index_context) { MEM_freeN(context); @@ -601,10 +603,7 @@ void SEQ_proxy_set(struct Sequence *seq, bool value) if (value) { seq->flag |= SEQ_USE_PROXY; if (seq->strip->proxy == NULL) { - seq->strip->proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy"); - seq->strip->proxy->quality = 50; - seq->strip->proxy->build_tc_flags = SEQ_PROXY_TC_ALL; - seq->strip->proxy->build_size_flags = SEQ_PROXY_IMAGE_SIZE_25; + seq->strip->proxy = seq_strip_proxy_alloc(); } } else { diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 482425e70d3..a6c627e5ce7 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -524,8 +524,15 @@ static void sequencer_preprocess_transform_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); - const eIMBInterpolationFilterMode filter = context->for_render ? IMB_FILTER_BILINEAR : - IMB_FILTER_NEAREST; + eIMBInterpolationFilterMode filter; + const StripTransform *transform = seq->strip->transform; + if (transform->filter == SEQ_TRANSFORM_FILTER_NEAREST) { + filter = IMB_FILTER_NEAREST; + } + else { + filter = IMB_FILTER_BILINEAR; + } + IMB_transform(in, out, IMB_TRANSFORM_MODE_CROP_SRC, filter, transform_matrix, &source_crop); if (!seq_image_transform_transparency_gained(context, seq)) { diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index f0a45355143..8e824f59dda 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -66,6 +66,15 @@ /** \name Allocate / Free Functions * \{ */ +StripProxy *seq_strip_proxy_alloc(void) +{ + StripProxy *strip_proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy"); + strip_proxy->quality = 50; + strip_proxy->build_tc_flags = SEQ_PROXY_TC_ALL; + strip_proxy->tc = SEQ_PROXY_TC_RECORD_RUN; + return strip_proxy; +} + static Strip *seq_strip_alloc(int type) { Strip *strip = MEM_callocN(sizeof(Strip), "strip"); @@ -76,6 +85,7 @@ static Strip *seq_strip_alloc(int type) strip->transform->scale_y = 1; strip->transform->origin[0] = 0.5f; strip->transform->origin[1] = 0.5f; + strip->transform->filter = SEQ_TRANSFORM_FILTER_BILINEAR; strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop"); } diff --git a/source/blender/sequencer/intern/sequencer.h b/source/blender/sequencer/intern/sequencer.h index 7d7ecbc8178..2a82f966f02 100644 --- a/source/blender/sequencer/intern/sequencer.h +++ b/source/blender/sequencer/intern/sequencer.h @@ -29,12 +29,13 @@ extern "C" { struct Scene; struct Sequence; - +struct StripProxy; /** * Cache must be freed before calling this function * since it leaves the seqbase in an invalid state. */ void seq_free_sequence_recurse(struct Scene *scene, struct Sequence *seq, bool do_id_user); +struct StripProxy *seq_strip_proxy_alloc(void); #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c index 86a37aca4a9..0788003fb12 100644 --- a/source/blender/sequencer/intern/sound.c +++ b/source/blender/sequencer/intern/sound.c @@ -31,6 +31,7 @@ #include "DNA_sound_types.h" #include "BLI_listbase.h" +#include "BLI_utildefines.h" #include "BKE_main.h" #include "BKE_scene.h" @@ -56,11 +57,15 @@ static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, } } else if (seq->type == SEQ_TYPE_SOUND_RAM && seq->sound) { - const float length = BKE_sound_get_length(bmain, seq->sound); + SoundInfo info; + if (!BKE_sound_info_get(bmain, seq->sound, &info)) { + continue; + } + int old = seq->len; float fac; - seq->len = (int)ceil((double)length * FPS); + seq->len = MAX2(1, round((info.length - seq->sound->offset_time) * FPS)); fac = (float)seq->len / (float)old; old = seq->startofs; seq->startofs *= fac; diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index f342765eec9..dd8966acfd8 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -287,14 +287,24 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL } #ifdef WITH_AUDASPACE -Sequence *SEQ_add_sound_strip(Main *bmain, - Scene *scene, - ListBase *seqbase, - SeqLoadData *load_data, - const double audio_offset) + +static void seq_add_sound_av_sync(Main *bmain, Scene *scene, Sequence *seq, SeqLoadData *load_data) +{ + SoundStreamInfo sound_stream; + if (!BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_stream)) { + return; + } + + const double av_stream_offset = sound_stream.start - load_data->r_video_stream_start; + const int frame_offset = av_stream_offset * FPS; + /* Set sub-frame offset. */ + seq->sound->offset_time = ((double)frame_offset / FPS) - av_stream_offset; + SEQ_transform_translate_sequence(scene, seq, frame_offset); +} + +Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data) { bSound *sound = BKE_sound_new_file(bmain, load_data->path); /* Handles relative paths. */ - sound->offset_time = audio_offset; SoundInfo info; bool sound_loaded = BKE_sound_info_get(bmain, sound, &info); @@ -337,6 +347,8 @@ Sequence *SEQ_add_sound_strip(Main *bmain, } } + seq_add_sound_av_sync(bmain, scene, seq, load_data); + /* Set Last active directory. */ BLI_strncpy(scene->ed->act_sounddir, strip->dir, FILE_MAXDIR); seq_add_set_name(scene, seq, load_data); @@ -349,8 +361,7 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Sequence *SEQ_add_sound_strip(Main *UNUSED(bmain), Scene *UNUSED(scene), ListBase *UNUSED(seqbase), - SeqLoadData *UNUSED(load_data), - const double UNUSED(audio_offset)) + SeqLoadData *UNUSED(load_data)) { return NULL; } @@ -373,8 +384,7 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_ return seqm; } -Sequence *SEQ_add_movie_strip( - Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data, double *r_start_offset) +Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data) { char path[sizeof(load_data->path)]; BLI_strncpy(path, load_data->path, sizeof(path)); @@ -420,8 +430,8 @@ Sequence *SEQ_add_movie_strip( return NULL; } - int video_frame_offset = 0; float video_fps = 0.0f; + load_data->r_video_stream_start = 0.0; if (anim_arr[0] != NULL) { short fps_denom; @@ -437,23 +447,11 @@ Sequence *SEQ_add_movie_strip( scene->r.frs_sec_base = fps_num; } - double video_start_offset = IMD_anim_get_offset(anim_arr[0]); - int minimum_frame_offset; - - if (*r_start_offset >= 0) { - minimum_frame_offset = MIN2(video_start_offset, *r_start_offset) * FPS; - } - else { - minimum_frame_offset = video_start_offset * FPS; - } - - video_frame_offset = video_start_offset * FPS - minimum_frame_offset; - - *r_start_offset = video_start_offset; + load_data->r_video_stream_start = IMD_anim_get_offset(anim_arr[0]); } Sequence *seq = SEQ_sequence_alloc( - seqbase, load_data->start_frame + video_frame_offset, load_data->channel, SEQ_TYPE_MOVIE); + seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIE); /* Multiview settings. */ if (load_data->use_multiview) { diff --git a/source/blender/shader_fx/CMakeLists.txt b/source/blender/shader_fx/CMakeLists.txt index 0167591c3ba..ae78d9964e5 100644 --- a/source/blender/shader_fx/CMakeLists.txt +++ b/source/blender/shader_fx/CMakeLists.txt @@ -63,9 +63,5 @@ set(SRC set(LIB ) -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - blender_add_lib(bf_shader_fx "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 03b2fb49085..37b233d85d2 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -135,10 +135,6 @@ if(WITH_CYCLES) add_definitions(-DWITH_CYCLES) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 9e4d8e733a6..ff3e1b7474c 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -47,6 +47,7 @@ struct GHashIterator; struct GPUViewport; struct ID; struct IDProperty; +struct IDRemapper; struct ImBuf; struct ImageFormatData; struct Main; @@ -471,7 +472,7 @@ void WM_main_add_notifier(unsigned int type, void *reference); * Clear notifiers by reference, Used so listeners don't act on freed data. */ void WM_main_remove_notifier_reference(const void *reference); -void WM_main_remap_editor_id_reference(struct ID *old_id, struct ID *new_id); +void WM_main_remap_editor_id_reference(const struct IDRemapper *mappings); /* reports */ /** diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index d1f790ce3e2..929b39715b9 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -1048,6 +1048,10 @@ typedef struct wmDragActiveDropState { * it as needed. */ struct ARegion *region_from; + /** If `active_dropbox` is set, additional context provided by the active (i.e. hovered) button. + * Activated before context sensitive operations (polling, drawing, dropping). */ + struct bContextStore *ui_context; + /** Text to show when a dropbox poll succeeds (so the dropbox itself is available) but the * operator poll fails. Typically the message the operator set with * CTX_wm_operator_poll_msg_set(). */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index 6f10e4f3f0d..2a93e279576 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -706,7 +706,7 @@ wmKeyMap *WM_gizmogroup_setup_keymap_generic_maybe_drag(const wmGizmoGroupType * /** * Variation of #WM_gizmogroup_keymap_common but with keymap items for selection * - * TODO(campbell): move to Python. + * TODO(@campbellbarton): move to Python. * * \param name: Typically #wmGizmoGroupType.name * \param params: Typically #wmGizmoGroupType.gzmap_params @@ -719,7 +719,7 @@ static wmKeyMap *WM_gizmogroup_keymap_template_select_ex( wmKeyMap *km = WM_keymap_ensure(kc, name, params->spaceid, params->regionid); const bool do_init = BLI_listbase_is_empty(&km->items); - /* FIXME(campbell) */ + /* FIXME(@campbellbarton): Currently hard coded. */ #if 0 const int select_mouse = (U.flag & USER_LMOUSESELECT) ? LEFTMOUSE : RIGHTMOUSE; const int select_tweak = (U.flag & USER_LMOUSESELECT) ? EVT_TWEAK_L : EVT_TWEAK_R; diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index d3e682f1490..83e6e347ce7 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -38,9 +38,11 @@ #include "ED_select_utils.h" #include "ED_view3d.h" +#include "GPU_framebuffer.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_state.h" +#include "GPU_viewport.h" #include "MEM_guardedalloc.h" @@ -505,8 +507,7 @@ void WM_gizmomap_draw(wmGizmoMap *gzmap, static void gizmo_draw_select_3d_loop(const bContext *C, wmGizmo **visible_gizmos, - const int visible_gizmos_len, - bool *r_use_select_bias) + const int visible_gizmos_len) { /* TODO(campbell): this depends on depth buffer being written to, @@ -542,10 +543,6 @@ static void gizmo_draw_select_3d_loop(const bContext *C, is_depth_skip_prev = is_depth_skip; } - if (gz->select_bias != 0.0) { - *r_use_select_bias = true; - } - /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected gizmo part id */ gz->type->draw_select(C, gz, select_id << 8); @@ -563,7 +560,10 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, const int visible_gizmos_len, const bContext *C, const int co[2], - const int hotspot) + const int hotspot, + const bool use_depth_test, + const bool has_3d_select_bias, + int *r_hits) { const wmWindowManager *wm = CTX_wm_manager(C); ScrArea *area = CTX_wm_area(C); @@ -572,35 +572,81 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); rcti rect; /* Almost certainly overkill, but allow for many custom gizmos. */ - uint buffer[MAXPICKBUF]; + GPUSelectResult buffer[MAXPICKELEMS]; short hits; BLI_rcti_init_pt_radius(&rect, co, hotspot); - ED_view3d_draw_setup_view( - wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect); + /* The selection mode is assigned for the following reasons: + * + * - #GPU_SELECT_ALL: Use it to check if there is anything at the cursor location + * (only ever runs once). + * - #GPU_SELECT_PICK_NEAREST: Use if there are more than 1 item at the cursor location, + * pick the nearest one. + * - #GPU_SELECT_PICK_ALL: Use for the same purpose as #GPU_SELECT_PICK_NEAREST + * when the selection depths need to re-ordered based on a bias. + * */ + const eGPUSelectMode gpu_select_mode = + (use_depth_test ? (has_3d_select_bias ? + /* Using select bias means the depths need to be + * re-calculated based on the bias to pick the best. */ + GPU_SELECT_PICK_ALL : + /* No bias, just pick the closest. */ + GPU_SELECT_PICK_NEAREST) : + /* Fast-path (occlusion queries). */ + GPU_SELECT_ALL); + + /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is + * performed before the viewport is fully initialized (region->draw_buffer = NULL). + * When this is the case we should not use depth testing. */ + GPUViewport *gpu_viewport = WM_draw_region_get_viewport(region); + if (use_depth_test && gpu_viewport == NULL) { + return -1; + } - bool use_select_bias = false; + if (GPU_select_is_cached()) { + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0); + GPU_select_cache_load_id(); + hits = GPU_select_end(); + } + else { + /* TODO: waiting for the GPU in the middle of the event loop for every + * mouse move is bad for performance, we need to find a solution to not + * use the GPU or draw something once. (see T61474) */ + + ED_view3d_draw_setup_view( + wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect); + + /* There is no need to bind to the depth buffer outside this function + * because all future passes the will use the cached depths. */ + GPUFrameBuffer *depth_read_fb = NULL; + if (use_depth_test) { + GPUTexture *depth_tx = GPU_viewport_depth_texture(gpu_viewport); + GPU_framebuffer_ensure_config(&depth_read_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); + GPU_framebuffer_bind(depth_read_fb); + } - /* TODO: waiting for the GPU in the middle of the event loop for every - * mouse move is bad for performance, we need to find a solution to not - * use the GPU or draw something once. (see T61474) */ - GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0); - /* do the drawing */ - gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias); + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0); + gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len); + hits = GPU_select_end(); - hits = GPU_select_end(); + if (use_depth_test) { + GPU_framebuffer_restore(); + GPU_framebuffer_free(depth_read_fb); + } - if (hits > 0) { - GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits); - gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias); - GPU_select_end(); + ED_view3d_draw_setup_view( + wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL); } - ED_view3d_draw_setup_view( - wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL); + /* When selection bias is needed, this function will run again with `use_depth_test` enabled. */ + int hit_found = -1; - if (use_select_bias && (hits > 1)) { + if (has_3d_select_bias && use_depth_test && (hits > 1)) { float co_direction[3]; float co_screen[3] = {co[0], co[1], 0.0f}; ED_view3d_win_to_vector(region, (float[2]){UNPACK2(co)}, co_direction); @@ -611,15 +657,14 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin); - uint *buf_iter = buffer; - int hit_found = -1; + GPUSelectResult *buf_iter = buffer; float dot_best = FLT_MAX; - for (int i = 0; i < hits; i++, buf_iter += 4) { - BLI_assert(buf_iter[3] != -1); - wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8]; + for (int i = 0; i < hits; i++, buf_iter++) { + BLI_assert(buf_iter->id != -1); + wmGizmo *gz = visible_gizmos[buf_iter->id >> 8]; float co_3d[3]; - co_screen[2] = int_as_float(buf_iter[1]); + co_screen[2] = int_as_float(buf_iter->depth); GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d); float select_bias = gz->select_bias; if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) { @@ -629,14 +674,19 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, const float dot_test = dot_v3v3(co_3d, co_direction) - select_bias; if (dot_best > dot_test) { dot_best = dot_test; - hit_found = buf_iter[3]; + hit_found = buf_iter->id; } } - return hit_found; + } + else { + const GPUSelectResult *hit_near = GPU_select_buffer_near(buffer, hits); + if (hit_near) { + hit_found = hit_near->id; + } } - const uint *hit_near = GPU_select_buffer_near(buffer, hits); - return hit_near ? hit_near[3] : -1; + *r_hits = hits; + return hit_found; } /** @@ -659,6 +709,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, /* Search for 3D gizmo's that use the 2D callback for checking intersections. */ bool has_3d = false; + bool has_3d_select_bias = false; { for (int select_id = 0; select_id < visible_gizmos_len; select_id++) { wmGizmo *gz = visible_gizmos[select_id]; @@ -674,6 +725,9 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, } else if (gz->type->draw_select != NULL) { has_3d = true; + if (gz->select_bias != 0.0f) { + has_3d_select_bias = true; + } } } } @@ -681,17 +735,78 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, /* Search for 3D intersections if they're before 2D that have been found (if any). * This way we always use the first hit. */ if (has_3d) { + + /* NOTE(@campbellbarton): The selection logic here uses a fast-path that exits early + * where possible. This is important as this runs on cursor-motion in the 3D view-port. + * + * - First, don't use the depth buffer at all, use occlusion queries to detect any gizmos. + * If there are no gizmos or only one - early exit, otherwise. + * + * - Bind the depth buffer and and use selection picking logic. + * This is much slower than occlusion queries (since it's reading depths while drawing). + * When there is a single gizmo under the cursor (quite common), early exit, otherwise. + * + * - Perform another pass at a reduced size (see: `hotspot_radii`), + * since the result depths are cached this pass is practically free. + * + * Other notes: + * + * - If any of these passes fail, use the nearest result from the previous pass. + * + * - Drawing is only ever done twice. + */ + + /* Order largest to smallest so the first pass can be used as cache for + * later passes (when `use_depth_test == true`). */ const int hotspot_radii[] = { - 3 * U.pixelsize, - /* This runs on mouse move, careful doing too many tests! */ 10 * U.pixelsize, + /* This runs on mouse move, careful doing too many tests! */ + 3 * U.pixelsize, }; + + /* Narrowing may assign zero to `hit`, allow falling back to the previous test. */ + int hit_prev = -1; + + bool use_depth_test = false; + bool use_depth_cache = false; + for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) { - hit = gizmo_find_intersected_3d_intern( - visible_gizmos, visible_gizmos_len_trim, C, co, hotspot_radii[i]); - if (hit != -1) { + + if (use_depth_test && (use_depth_cache == false)) { + GPU_select_cache_begin(); + use_depth_cache = true; + } + + int hit_count; + hit = gizmo_find_intersected_3d_intern(visible_gizmos, + visible_gizmos_len_trim, + C, + co, + hotspot_radii[i], + use_depth_test, + has_3d_select_bias, + &hit_count); + /* Only continue searching when there are multiple options to narrow down. */ + if (hit_count < 2) { break; } + + /* Fast path for simple case, one item or nothing. */ + if (use_depth_test == false) { + /* Restart, using depth buffer (slower). */ + use_depth_test = true; + i = -1; + } + hit_prev = hit; + } + /* Narrowing the search area may yield no hits, + * in this case fall back to the previous search. */ + if (hit == -1) { + hit = hit_prev; + } + + if (use_depth_cache) { + GPU_select_cache_end(); } if (hit != -1) { diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 96cb66b44ea..3bc77db45ca 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -226,6 +226,30 @@ void wm_drags_exit(wmWindowManager *wm, wmWindow *win) } } +static bContextStore *wm_drop_ui_context_create(const bContext *C) +{ + uiBut *active_but = UI_region_active_but_get(CTX_wm_region(C)); + if (!active_but) { + return NULL; + } + + bContextStore *but_context = UI_but_context_get(active_but); + if (!but_context) { + return NULL; + } + + return CTX_store_copy(but_context); +} + +static void wm_drop_ui_context_free(bContextStore **context_store) +{ + if (!*context_store) { + return; + } + CTX_store_free(*context_store); + *context_store = NULL; +} + void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy) { drag->imb = imb; @@ -259,6 +283,7 @@ void WM_drag_free(wmDrag *drag) if (drag->flags & WM_DRAG_FREE_DATA) { WM_drag_data_free(drag->type, drag->poin); } + wm_drop_ui_context_free(&drag->drop_state.ui_context); if (drag->drop_state.free_disabled_info) { MEM_SAFE_FREE(drag->drop_state.disabled_info); } @@ -317,6 +342,10 @@ static wmDropBox *dropbox_active(bContext *C, } const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop); + if (drag->drop_state.ui_context) { + CTX_store_set(C, drag->drop_state.ui_context); + } + if (WM_operator_poll_context(C, drop->ot, opcontext)) { return drop; } @@ -367,6 +396,10 @@ static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *even return; } + /* Update UI context, before polling so polls can query this context. */ + wm_drop_ui_context_free(&drag->drop_state.ui_context); + drag->drop_state.ui_context = wm_drop_ui_context_create(C); + wmDropBox *drop_prev = drag->drop_state.active_dropbox; wmDropBox *drop = wm_dropbox_active(C, drag, event); if (drop != drop_prev) { @@ -381,11 +414,20 @@ static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *even drag->drop_state.area_from = drop ? CTX_wm_area(C) : NULL; drag->drop_state.region_from = drop ? CTX_wm_region(C) : NULL; } + + if (!drag->drop_state.active_dropbox) { + wm_drop_ui_context_free(&drag->drop_state.ui_context); + } } void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop) { const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop); + + if (drag->drop_state.ui_context) { + CTX_store_set(C, drag->drop_state.ui_context); + } + /* Optionally copy drag information to operator properties. Don't call it if the * operator fails anyway, it might do more than just set properties (e.g. * typically import an asset). */ @@ -396,6 +438,11 @@ void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop) wm_drags_exit(CTX_wm_manager(C), CTX_wm_window(C)); } +void wm_drop_end(bContext *C, wmDrag *UNUSED(drag), wmDropBox *UNUSED(drop)) +{ + CTX_store_set(C, NULL); +} + void wm_drags_check_ops(bContext *C, const wmEvent *event) { wmWindowManager *wm = CTX_wm_manager(C); @@ -897,6 +944,7 @@ void wm_drags_draw(bContext *C, wmWindow *win) if (drag->drop_state.active_dropbox) { CTX_wm_area_set(C, drag->drop_state.area_from); CTX_wm_region_set(C, drag->drop_state.region_from); + CTX_store_set(C, drag->drop_state.ui_context); /* Drawing should be allowed to assume the context from handling and polling (that's why we * restore it above). */ @@ -915,4 +963,5 @@ void wm_drags_draw(bContext *C, wmWindow *win) GPU_blend(GPU_BLEND_NONE); CTX_wm_area_set(C, NULL); CTX_wm_region_set(C, NULL); + CTX_store_set(C, NULL); } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 28d8413fe0b..5210b315af6 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -51,6 +51,7 @@ #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_idprop.h" +#include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -312,28 +313,39 @@ void WM_main_remove_notifier_reference(const void *reference) } } -void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id) +static void wm_main_remap_assetlist(ID *old_id, ID *new_id, void *UNUSED(user_data)) +{ + ED_assetlist_storage_id_remap(old_id, new_id); +} + +static void wm_main_remap_msgbus_notify(ID *old_id, ID *new_id, void *user_data) +{ + struct wmMsgBus *mbus = user_data; + if (new_id != NULL) { + WM_msg_id_update(mbus, old_id, new_id); + } + else { + WM_msg_id_remove(mbus, old_id); + } +} + +void WM_main_remap_editor_id_reference(const struct IDRemapper *mappings) { Main *bmain = G_MAIN; LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - ED_spacedata_id_remap(area, sl, old_id, new_id); + ED_spacedata_id_remap(area, sl, mappings); } } } - ED_assetlist_storage_id_remap(old_id, new_id); + + BKE_id_remapper_iter(mappings, wm_main_remap_assetlist, NULL); wmWindowManager *wm = bmain->wm.first; if (wm && wm->message_bus) { - struct wmMsgBus *mbus = wm->message_bus; - if (new_id != NULL) { - WM_msg_id_update(mbus, old_id, new_id); - } - else { - WM_msg_id_remove(mbus, old_id); - } + BKE_id_remapper_iter(mappings, wm_main_remap_msgbus_notify, wm->message_bus); } } @@ -633,6 +645,20 @@ static int wm_event_always_pass(const wmEvent *event) return ISTIMER(event->type) || (event->type == WINDEACTIVATE); } +/** + * Debug only sanity check for the return value of event handlers. Checks that "always pass" events + * don't cause non-passing handler return values, and thus actually pass. + * + * Can't be executed if the handler just loaded a file (typically identified by `CTX_wm_window(C)` + * returning `NULL`), because the event will have been freed then. + */ +BLI_INLINE void wm_event_handler_return_value_check(const wmEvent *event, const int action) +{ + BLI_assert_msg(!wm_event_always_pass(event) || (action != WM_HANDLER_BREAK), + "Return value for events that should always pass should never be BREAK."); + UNUSED_VARS_NDEBUG(event, action); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1055,7 +1081,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons } } - /* XXX(mont29) Disabled the repeat check to address part 2 of T31840. + /* XXX(@mont29): Disabled the repeat check to address part 2 of T31840. * Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason * why this was needed, but worth to note it in case something turns bad. */ if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */) { @@ -2953,9 +2979,9 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis wmWindowManager *wm = CTX_wm_manager(C); int action = WM_HANDLER_CONTINUE; - int always_pass; if (handlers == NULL) { + wm_event_handler_return_value_check(event, action); return action; } @@ -2976,7 +3002,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis } else if (handler_base->poll == NULL || handler_base->poll(CTX_wm_region(C), event)) { /* In advance to avoid access to freed event on window close. */ - always_pass = wm_event_always_pass(event); + const int always_pass = wm_event_always_pass(event); /* Modal+blocking handler_base. */ if (handler_base->flag & WM_HANDLER_BLOCKING) { @@ -3050,6 +3076,8 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis event->customdata = NULL; event->custom = 0; + wm_drop_end(C, drag, drop); + /* XXX fileread case. */ if (CTX_wm_window(C) == NULL) { return action; @@ -3118,6 +3146,10 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis wm_cursor_arrow_move(CTX_wm_window(C), event); } + /* Do some extra sanity checking before returning the action. */ + if (CTX_wm_window(C) != NULL) { + wm_event_handler_return_value_check(event, action); + } return action; } @@ -3245,6 +3277,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } } + wm_event_handler_return_value_check(event, action); return action; } @@ -3267,14 +3300,6 @@ static bool wm_event_inside_rect(const wmEvent *event, const rcti *rect) return false; } -static bool wm_event_inside_region(const wmEvent *event, const ARegion *region) -{ - if (wm_event_always_pass(event)) { - return true; - } - return ED_region_contains_xy(region, event->xy); -} - static ScrArea *area_event_inside(bContext *C, const int xy[2]) { wmWindow *win = CTX_wm_window(C); @@ -3488,6 +3513,55 @@ static void wm_event_handle_xrevent(bContext *C, } #endif /* WITH_XR_OPENXR */ +static int wm_event_do_region_handlers(bContext *C, wmEvent *event, ARegion *region) +{ + CTX_wm_region_set(C, region); + + /* Call even on non mouse events, since the */ + wm_region_mouse_co(C, event); + + const wmWindowManager *wm = CTX_wm_manager(C); + if (!BLI_listbase_is_empty(&wm->drags)) { + /* Does polls for drop regions and checks #uiButs. */ + /* Need to be here to make sure region context is true. */ + if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) { + wm_drags_check_ops(C, event); + } + } + + return wm_handlers_do(C, event, ®ion->handlers); +} + +/** + * Send event to region handlers in \a area. + * + * Two cases: + * 1) Always pass events (#wm_event_always_pass()) are sent to all regions. + * 2) Event is passed to the region visually under the cursor (#ED_area_find_region_xy_visual()). + */ +static int wm_event_do_handlers_area_regions(bContext *C, wmEvent *event, ScrArea *area) +{ + /* Case 1. */ + if (wm_event_always_pass(event)) { + int action = WM_HANDLER_CONTINUE; + + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + action |= wm_event_do_region_handlers(C, event, region); + } + + wm_event_handler_return_value_check(event, action); + return action; + } + + /* Case 2. */ + ARegion *region_hovered = ED_area_find_region_xy_visual(area, RGN_TYPE_ANY, event->xy); + if (!region_hovered) { + return WM_HANDLER_CONTINUE; + } + + return wm_event_do_region_handlers(C, event, region_hovered); +} + void wm_event_do_handlers(bContext *C) { wmWindowManager *wm = CTX_wm_manager(C); @@ -3517,7 +3591,7 @@ void wm_event_do_handlers(bContext *C) if (scene_eval != NULL) { const int is_playing_sound = BKE_sound_scene_playing(scene_eval); - if (scene_eval->id.recalc & ID_RECALC_AUDIO_SEEK) { + if (scene_eval->id.recalc & ID_RECALC_FRAME_CHANGE) { /* Ignore seek here, the audio will be updated to the scene frame after jump during next * dependency graph update. */ } @@ -3670,36 +3744,12 @@ void wm_event_do_handlers(bContext *C) if (wm_event_inside_rect(event, &area->totrct)) { CTX_wm_area_set(C, area); - if ((action & WM_HANDLER_BREAK) == 0) { - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (wm_event_inside_region(event, region)) { - - CTX_wm_region_set(C, region); + action |= wm_event_do_handlers_area_regions(C, event, area); - /* Call even on non mouse events, since the */ - wm_region_mouse_co(C, event); - - if (!BLI_listbase_is_empty(&wm->drags)) { - /* Does polls for drop regions and checks #uiButs. */ - /* Need to be here to make sure region context is true. */ - if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) { - wm_drags_check_ops(C, event); - } - } - - action |= wm_handlers_do(C, event, ®ion->handlers); - - /* Fileread case (python), T29489. */ - if (CTX_wm_window(C) == NULL) { - wm_event_free_and_remove_from_queue_if_valid(event); - return; - } - - if (action & WM_HANDLER_BREAK) { - break; - } - } - } + /* Fileread case (python), T29489. */ + if (CTX_wm_window(C) == NULL) { + wm_event_free_and_remove_from_queue_if_valid(event); + return; } CTX_wm_region_set(C, NULL); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 1478712c3cd..9b9aa37a251 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -874,18 +874,16 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) duration_lib_override_recursive_resync_seconds); } - if (bf_reports->count.linked_proxies != 0 || - bf_reports->count.proxies_to_lib_overrides_success != 0 || + if (bf_reports->count.proxies_to_lib_overrides_success != 0 || bf_reports->count.proxies_to_lib_overrides_failures != 0) { - BKE_reportf(bf_reports->reports, - RPT_WARNING, - "Proxies are deprecated (%d proxies were automatically converted to library " - "overrides, %d proxies could not be converted and %d linked proxies were kept " - "untouched). If you need to keep proxies for the time being, please disable the " - "`Proxy to Override Auto Conversion` in Experimental user preferences", - bf_reports->count.proxies_to_lib_overrides_success, - bf_reports->count.proxies_to_lib_overrides_failures, - bf_reports->count.linked_proxies); + BKE_reportf( + bf_reports->reports, + RPT_WARNING, + "Proxies have been removed from Blender (%d proxies were automatically converted " + "to library overrides, %d proxies could not be converted and were cleared). " + "Please also consider re-saving any library .blend file with the newest Blender version.", + bf_reports->count.proxies_to_lib_overrides_success, + bf_reports->count.proxies_to_lib_overrides_failures); } if (bf_reports->count.sequence_strips_skipped != 0) { @@ -2706,7 +2704,7 @@ static char *wm_open_mainfile_description(struct bContext *UNUSED(C), BLI_stat_t stats; if (BLI_stat(path, &stats) == -1) { - return BLI_sprintfN("%s\n\n%s", path, N_("File Not Found")); + return BLI_sprintfN("%s\n\n%s", path, TIP_("File Not Found")); } /* Date. */ @@ -2716,7 +2714,7 @@ static char *wm_open_mainfile_description(struct bContext *UNUSED(C), BLI_filelist_entry_datetime_to_string( NULL, (int64_t)stats.st_mtime, false, time_st, date_st, &is_today, &is_yesterday); if (is_today || is_yesterday) { - BLI_strncpy(date_st, is_today ? N_("Today") : N_("Yesterday"), sizeof(date_st)); + BLI_strncpy(date_st, is_today ? TIP_("Today") : TIP_("Yesterday"), sizeof(date_st)); } /* Size. */ @@ -2724,7 +2722,7 @@ static char *wm_open_mainfile_description(struct bContext *UNUSED(C), BLI_filelist_entry_size_to_string(NULL, (uint64_t)stats.st_size, false, size_str); return BLI_sprintfN( - "%s\n\n%s: %s %s\n%s: %s", path, N_("Modified"), date_st, time_st, N_("Size"), size_str); + "%s\n\n%s: %s %s\n%s: %s", path, TIP_("Modified"), date_st, time_st, TIP_("Size"), size_str); } /* currently fits in a pointer */ @@ -3171,8 +3169,8 @@ static char *wm_save_as_mainfile_get_description(bContext *UNUSED(C), PointerRNA *ptr) { if (RNA_boolean_get(ptr, "copy")) { - return BLI_strdup( - "Save the current file in the desired location but do not make the saved file active"); + return BLI_strdup(TIP_( + "Save the current file in the desired location but do not make the saved file active")); } return NULL; } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 957ec7d800d..1d26c58bcf9 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -252,7 +252,7 @@ void WM_init(bContext *C, int argc, const char **argv) BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove); BKE_region_callback_refresh_tag_gizmomap_set(WM_gizmomap_tag_refresh); BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); - BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); + BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap_single); DEG_editors_set_update_cb(ED_render_id_flush_update, ED_render_scene_update); ED_spacetypes_init(); @@ -594,9 +594,7 @@ void WM_exit_ex(bContext *C, const bool do_python) DRW_opengl_context_destroy(); } -#ifdef WITH_INTERNATIONAL BLT_lang_free(); -#endif ANIM_keyingset_infos_exit(); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index a1854a8ed86..a7b5b9bddda 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1483,7 +1483,11 @@ static bool wm_window_timer(const bContext *C) wt->delta = time - wt->ltime; wt->duration += wt->delta; wt->ltime = time; - wt->ntime = wt->stime + wt->timestep * ceil(wt->duration / wt->timestep); + + wt->ntime = wt->stime; + if (wt->timestep != 0.0f) { + wt->ntime += wt->timestep * ceil(wt->duration / wt->timestep); + } if (wt->event_type == TIMERJOBS) { wm_jobs_timer(wm, wt); @@ -1596,6 +1600,7 @@ void WM_event_timer_sleep(wmWindowManager *wm, wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep) { wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer"); + BLI_assert(timestep >= 0.0f); wt->event_type = event_type; wt->ltime = PIL_check_seconds_timer(); @@ -1615,6 +1620,7 @@ wmTimer *WM_event_add_timer_notifier(wmWindowManager *wm, double timestep) { wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer"); + BLI_assert(timestep >= 0.0f); wt->event_type = TIMERNOTIFIER; wt->ltime = PIL_check_seconds_timer(); diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index 925be8ab183..252ff228bc1 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -199,6 +199,7 @@ void wm_dropbox_free(void); */ void wm_drags_exit(wmWindowManager *wm, wmWindow *win); void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop); +void wm_drop_end(bContext *C, wmDrag *drag, wmDropBox *drop); /** * Called in inner handler loop, region context. */ diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index a6685e97c4f..6fcaad92661 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -60,7 +60,7 @@ enum { /* ********** Start of Input devices. ********** */ - /* MOUSE : 0x000x, 0x001x */ + /* MOUSE: 0x000x, 0x001x */ LEFTMOUSE = 0x0001, MIDDLEMOUSE = 0x0002, RIGHTMOUSE = 0x0003, @@ -71,7 +71,7 @@ enum { /* More mouse buttons - can't use 9 and 10 here (wheel) */ BUTTON6MOUSE = 0x0012, BUTTON7MOUSE = 0x0013, - /* Extra trackpad gestures */ + /* Extra track-pad gestures. */ MOUSEPAN = 0x000e, MOUSEZOOM = 0x000f, MOUSEROTATE = 0x0010, diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 8425c872d28..61809b1719d 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -685,7 +685,11 @@ if(UNIX AND NOT APPLE) PATTERN "*.pyo" EXCLUDE # * any cache * ) # On some platforms requests does have extra dependencies. - set(_requests_deps "certifi" "chardet" "idna" "urllib3") + # + # Either 'chardet' or 'charset_normalizer" is used, depending on the + # version of Python. The code below silently skips the one that's not + # available, so we can just list both here. + set(_requests_deps "certifi" "chardet" "charset_normalizer" "idna" "urllib3") foreach(_requests_dep ${_requests_deps}) if(EXISTS ${PYTHON_REQUESTS_PATH}/${_requests_dep}) install( @@ -1086,6 +1090,13 @@ elseif(APPLE) Blender.app/Contents/ ) + if(WITH_BLENDER_THUMBNAILER) + install( + TARGETS blender-thumbnailer + DESTINATION Blender.app/Contents/MacOS/ + ) + endif() + if(WITH_OPENMP AND OPENMP_CUSTOM) install( FILES "${OpenMP_LIBRARY}" diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index d3cec093980..11bea595690 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -970,9 +970,6 @@ static const char arg_handle_debug_mode_generic_set_doc_xr_time[] = static const char arg_handle_debug_mode_generic_set_doc_jobs[] = "\n\t" "Enable time profiling for background jobs."; -static const char arg_handle_debug_mode_generic_set_doc_gpu[] = - "\n\t" - "Enable GPU debug context and information for OpenGL 4.3+."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph[] = "\n\t" "Enable all debug messages from dependency graph."; @@ -1097,6 +1094,20 @@ static int arg_handle_debug_value_set(int argc, const char **argv, void *UNUSED( return 0; } +static const char arg_handle_debug_gpu_set_doc[] = + "\n" + "\tEnable GPU debug context and information for OpenGL 4.3+."; +static int arg_handle_debug_gpu_set(int UNUSED(argc), + const char **UNUSED(argv), + void *UNUSED(data)) +{ + /* Also enable logging because that how gl errors are reported. */ + const char *gpu_filter = "gpu.*"; + CLG_type_filter_include(gpu_filter, strlen(gpu_filter)); + G.debug |= G_DEBUG_GPU; + return 0; +} + static const char arg_handle_debug_fpe_set_doc[] = "\n\t" "Enable floating-point exceptions."; @@ -2155,8 +2166,8 @@ void main_args_setup(bContext *C, bArgs *ba) "--debug-jobs", CB_EX(arg_handle_debug_mode_generic_set, jobs), (void *)G_DEBUG_JOBS); - BLI_args_add( - ba, NULL, "--debug-gpu", CB_EX(arg_handle_debug_mode_generic_set, gpu), (void *)G_DEBUG_GPU); + BLI_args_add(ba, NULL, "--debug-gpu", CB(arg_handle_debug_gpu_set), NULL); + BLI_args_add(ba, NULL, "--debug-depsgraph", diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index f3136d6c649..04fdb380da2 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -293,6 +293,13 @@ add_blender_test( --testdir "${TEST_SRC_DIR}/animation" ) +add_blender_test( + bl_rigging_symmetrize + --python ${CMAKE_CURRENT_LIST_DIR}/bl_rigging_symmetrize.py + -- + --testdir "${TEST_SRC_DIR}/animation" +) + # ------------------------------------------------------------------------------ # IO TESTS @@ -324,7 +331,10 @@ add_blender_test( ) endif() +if(FALSE) # OBJ Export tests +# Disabled because new C++ Obj exporter has C++ tests. + add_blender_test( export_obj_cube ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend @@ -345,8 +355,6 @@ add_blender_test( --md5=a733ae4fa4a591ea9b0912da3af042de --md5_method=FILE ) -# disabled until updated & working -if(FALSE) add_blender_test( export_obj_all_objects ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend @@ -692,7 +700,7 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS) foreach(render_test ${_cycles_render_tests}) # Enable just one simple test for Metal until more tests are passing. - if ((NOT (_cycles_device MATCHES "METAL")) OR (render_test MATCHES "camera")) + if((NOT (_cycles_device MATCHES "METAL")) OR (render_test MATCHES "camera")) add_python_test( cycles_${render_test}_${_cycles_device_lower} ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py @@ -772,6 +780,7 @@ set(geo_node_tests geometry mesh_primitives mesh + mesh/extrude points utilities vector diff --git a/tests/python/bl_keymap_validate.py b/tests/python/bl_keymap_validate.py index ebc35c1178c..705a026e441 100644 --- a/tests/python/bl_keymap_validate.py +++ b/tests/python/bl_keymap_validate.py @@ -162,7 +162,7 @@ def keymap_data_clean(keyconfig_data: typing.List, *, relaxed: bool) -> None: item_prop.pop("properties") # Needed so: `{"properties": ()}` matches `None` as there is no meaningful difference. - # Wruting `None` makes the most sense when explicitly written, however generated properties + # Writing `None` makes the most sense when explicitly written, however generated properties # might be empty and it's not worth adding checks in the generation logic to use `None` # just to satisfy this check. if not item_prop: diff --git a/tests/python/bl_rigging_symmetrize.py b/tests/python/bl_rigging_symmetrize.py new file mode 100644 index 00000000000..b47ace7f3f1 --- /dev/null +++ b/tests/python/bl_rigging_symmetrize.py @@ -0,0 +1,244 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +""" +blender -b -noaudio --factory-startup --python tests/python/bl_rigging_symmetrize.py -- --testdir /path/to/lib/tests/animation +""" + +import pathlib +import sys +import unittest + +import bpy + + +def check_loc_rot_scale(self, bone, exp_bone): + # Check if posistions are the same + self.assertEqualVector( + bone.head, exp_bone.head, "Head position", bone.name) + self.assertEqualVector( + bone.tail, exp_bone.tail, "Tail position", bone.name) + + # Scale + self.assertEqualVector( + bone.scale, exp_bone.scale, "Scale", bone.name) + + # Rotation + rot_mode = exp_bone.rotation_mode + self.assertEqual(bone.rotation_mode, rot_mode, "Rotations mode does not match on bone %s" % (bone.name)) + + if rot_mode == 'QUATERNION': + self.assertEqualVector( + bone.rotation_quaternion, exp_bone.rotation_quaternion, "Quaternion rotation", bone.name) + elif rot_mode == 'AXIS_ANGLE': + self.assertEqualVector( + bone.axis_angle, exp_bone.axis_angle, "Axis Angle rotation", bone.name) + else: + # Euler rotation + self.assertEqualVector( + bone.rotation_euler, exp_bone.rotation_euler, "Euler rotation", bone.name) + + +def check_parent(self, bone, exp_bone): + self.assertEqual(type(bone.parent), type(exp_bone.parent), + "Missmatching types in pose.bones[%s].parent" % (bone.name)) + self.assertTrue(bone.parent is None or bone.parent.name == exp_bone.parent.name, + "Bone parent does not match on bone %s" % (bone.name)) + + +def check_bendy_bones(self, bone, exp_bone): + bone_variables = bone.bl_rna.properties.keys() + + bendy_bone_variables = [ + var for var in bone_variables if var.startswith("bbone_")] + + for var in bendy_bone_variables: + value = getattr(bone, var) + exp_value = getattr(exp_bone, var) + + self.assertEqual(type(value), type(exp_value), + "Missmatching types in pose.bones[%s].%s" % (bone.name, var)) + + if isinstance(value, str): + self.assertEqual(value, exp_value, + "Missmatching value in pose.bones[%s].%s" % (bone.name, var)) + elif hasattr(value, "name"): + self.assertEqual(value.name, exp_value.name, + "Missmatching value in pose.bones[%s].%s" % (bone.name, var)) + else: + self.assertAlmostEqual(value, exp_value, + "Missmatching value in pose.bones[%s].%s" % (bone.name, var)) + + +def check_ik(self, bone, exp_bone): + bone_variables = bone.bl_rna.properties.keys() + prefixes = ("ik_", "lock_ik", "use_ik") + ik_bone_variables = ( + var for var in bone_variables + if var.startswith(prefixes) + ) + + for var in ik_bone_variables: + value = getattr(bone, var) + exp_value = getattr(exp_bone, var) + self.assertAlmostEqual(value, exp_value, + "Missmatching value in pose.bones[%s].%s" % (bone.name, var)) + + +def check_constraints(self, input_arm, expected_arm, bone, exp_bone): + const_len = len(bone.constraints) + expo_const_len = len(exp_bone.constraints) + + self.assertEqual(const_len, expo_const_len, + "Constraints missmatch on bone %s" % (bone.name)) + + for exp_constraint in exp_bone.constraints: + const_name = exp_constraint.name + # Make sure that the constraint exists + self.assertTrue(const_name in bone.constraints, + "Bone %s is expected to contain constraint %s, but it does not." % ( + bone.name, const_name)) + constraint = bone.constraints[const_name] + const_variables = constraint.bl_rna.properties.keys() + + for var in const_variables: + + if var == "is_override_data": + # This variable is not used for local (non linked) data. + # For local object it is not initialized, so don't check this value. + continue + + value = getattr(constraint, var) + exp_value = getattr(exp_constraint, var) + + self.assertEqual(type(value), type(exp_value), + "Missmatching constraint value types in pose.bones[%s].constraints[%s].%s" % ( + bone.name, const_name, var)) + + if isinstance(value, str): + self.assertEqual(value, exp_value, + "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % ( + bone.name, const_name, var)) + elif hasattr(value, "name"): + # Some constraints targets the armature itself, so the armature name should missmatch. + if value.name == input_arm.name and exp_value.name == expected_arm.name: + continue + + self.assertEqual(value.name, exp_value.name, + "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % ( + bone.name, const_name, var)) + + elif isinstance(value, bool): + self.assertEqual(value, exp_value, + "Missmatching constraint boolean in pose.bones[%s].constraints[%s].%s" % ( + bone.name, const_name, var)) + else: + self.assertAlmostEqual(value, exp_value, + "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % ( + bone.name, const_name, var)) + + +class AbstractAnimationTest: + @classmethod + def setUpClass(cls): + cls.testdir = args.testdir + + def setUp(self): + self.assertTrue(self.testdir.exists(), + 'Test dir %s should exist' % self.testdir) + + +class ArmatureSymmetrizeTest(AbstractAnimationTest, unittest.TestCase): + def test_symmetrize_operator(self): + """Test that the symmetrize operator is working correctly.""" + bpy.ops.wm.open_mainfile(filepath=str( + self.testdir / "symm_test.blend")) + + # T81541 (D9214) + arm = bpy.data.objects['transform_const_rig'] + expected_arm = bpy.data.objects['expected_transform_const_rig'] + self.assertEqualSymmetrize(arm, expected_arm) + + # T66751 (D6009) + arm = bpy.data.objects['dragon_rig'] + expected_arm = bpy.data.objects['expected_dragon_rig'] + self.assertEqualSymmetrize(arm, expected_arm) + + def assertEqualSymmetrize(self, input_arm, expected_arm): + + # Symmetrize our input armature + bpy.context.view_layer.objects.active = input_arm + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.armature.select_all(action='SELECT') + bpy.ops.armature.symmetrize() + bpy.ops.object.mode_set(mode='OBJECT') + + # Make sure that the bone count is the same + bone_len = len(input_arm.pose.bones) + expected_bone_len = len(expected_arm.pose.bones) + self.assertEqual(bone_len, expected_bone_len, + "Expected bone count to match") + + for exp_bone in expected_arm.pose.bones: + bone_name = exp_bone.name + # Make sure that the bone exists + self.assertTrue(bone_name in input_arm.pose.bones, + "Armature is expected to contain bone %s, but it does not." % (bone_name)) + bone = input_arm.pose.bones[bone_name] + + # Loc Rot Scale + check_loc_rot_scale(self, bone, exp_bone) + + # Parent settings + check_parent(self, bone, exp_bone) + + # Bendy Bones + check_bendy_bones(self, bone, exp_bone) + + # IK + check_ik(self, bone, exp_bone) + + # Constraints + check_constraints(self, input_arm, expected_arm, bone, exp_bone) + + def assertEqualVector(self, vec1, vec2, check_str, bone_name) -> None: + for idx, value in enumerate(vec1): + self.assertAlmostEqual( + value, vec2[idx], 3, "%s does not match with expected value on bone %s" % (check_str, bone_name)) + + +def main(): + global args + import argparse + + if '--' in sys.argv: + argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:] + else: + argv = sys.argv + + parser = argparse.ArgumentParser() + parser.add_argument('--testdir', required=True, type=pathlib.Path) + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == "__main__": + main() diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py index 98b2cd4a200..b0938c06ecf 100644 --- a/tests/python/cycles_render_tests.py +++ b/tests/python/cycles_render_tests.py @@ -11,6 +11,15 @@ from pathlib import Path # List of .blend files that are known to be failing and are not ready to be # tested, or that only make sense on some devices. Accepts regular expressions. +BLACKLIST_ALL = [ + # Blacklisted due overlapping object differences between platforms. + "hair_geom_reflection.blend", + "hair_geom_transmission.blend", + "hair_instancer_uv.blend", + "principled_hair_directcoloring.blend", + "visibility_particles.blend", +] + BLACKLIST_OSL = [ # OSL only supported on CPU. '.*_osl.blend', @@ -36,8 +45,12 @@ BLACKLIST_GPU = [ 'hair_instancer_uv.blend', 'hair_length_info.blend', 'hair_particle_random.blend', + "hair_transmission.blend", 'principled_hair_.*.blend', 'transparent_shadow_hair.*.blend', + # Inconsistent handling of overlapping objects. + "T41143.blend", + "visibility_particles.blend", ] @@ -96,7 +109,7 @@ def main(): output_dir = args.outdir[0] device = args.device[0] - blacklist = [] + blacklist = BLACKLIST_ALL if device != 'CPU': blacklist += BLACKLIST_GPU if device != 'CPU' or 'OSL' in args.blacklist: |