diff options
author | Julian Eisel <eiseljulian@gmail.com> | 2019-08-18 14:24:15 +0300 |
---|---|---|
committer | Julian Eisel <eiseljulian@gmail.com> | 2019-08-18 14:24:15 +0300 |
commit | 8209f25c3b08f726a221cbcbcfa48dbea597c772 (patch) | |
tree | 0e02482c8b6e87a75e02098c69cee051596da42e | |
parent | 4befee9e620f3c0b17711b56cc19bdebd7d401f5 (diff) | |
parent | 454b120f48201abe2e1a9ca7e5d25c28cb04013a (diff) |
Merge branch 'master' into filebrowser_redesign
497 files changed, 19307 insertions, 8304 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ced6e1d76d..16ac322ebdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,6 +182,8 @@ if(UNIX AND NOT APPLE) set(_init_SDL OFF) set(_init_FFTW3 OFF) set(_init_OPENSUBDIV OFF) + set(_init_OPENVDB OFF) + set(_init_OPENIMAGEDENOISE OFF) elseif(WIN32) set(_init_JACK OFF) elseif(APPLE) @@ -237,11 +239,12 @@ option(WITH_OPENCOLORIO "Enable OpenColorIO color management" ${_init_OPENCOLO # Compositor option(WITH_COMPOSITOR "Enable the tile based nodal compositor" ON) +option(WITH_OPENIMAGEDENOISE "Enable the OpenImageDenoise compositing node" ${_init_OPENIMAGEDENOISE}) option(WITH_OPENSUBDIV "Enable OpenSubdiv for surface subdivision" ${_init_OPENSUBDIV}) -option(WITH_OPENVDB "Enable features relying on OpenVDB" OFF) -option(WITH_OPENVDB_BLOSC "Enable blosc compression for OpenVDB, only enable if OpenVDB was built with blosc support" OFF) +option(WITH_OPENVDB "Enable features relying on OpenVDB" ${_init_OPENVDB}) +option(WITH_OPENVDB_BLOSC "Enable blosc compression for OpenVDB, only enable if OpenVDB was built with blosc support" ${_init_OPENVDB}) option(WITH_OPENVDB_3_ABI_COMPATIBLE "Assume OpenVDB library has been compiled with version 3 ABI compatibility" OFF) mark_as_advanced(WITH_OPENVDB_3_ABI_COMPATIBLE) @@ -1760,6 +1763,7 @@ if(FIRST_RUN) info_cfg_option(WITH_CYCLES) info_cfg_option(WITH_FREESTYLE) info_cfg_option(WITH_OPENCOLORIO) + info_cfg_option(WITH_OPENIMAGEDENOISE) info_cfg_option(WITH_OPENVDB) info_cfg_option(WITH_ALEMBIC) diff --git a/GNUmakefile b/GNUmakefile index 1ad7cc4fc27..4462a13207e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -231,10 +231,14 @@ endif # build tool ifneq "$(findstring ninja, $(MAKECMDGOALS))" "" - BUILD_COMMAND:=ninja BUILD_CMAKE_ARGS:=$(BUILD_CMAKE_ARGS) -G Ninja + BUILD_COMMAND:=ninja else - BUILD_COMMAND:=make -s + ifneq ("$(wildcard $(BUILD_DIR)/build.ninja)","") + BUILD_COMMAND:=ninja + else + BUILD_COMMAND:=make -s + endif endif # ----------------------------------------------------------------------------- diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index e4fe7d90176..1b387cb86a2 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -90,12 +90,14 @@ include(cmake/tbb.cmake) include(cmake/openvdb.cmake) include(cmake/python.cmake) include(cmake/python_site_packages.cmake) +include(cmake/package_python.cmake) include(cmake/numpy.cmake) if(UNIX AND NOT APPLE) # Rely on PugiXML compiled with OpenImageIO else() include(cmake/pugixml.cmake) endif() +include(cmake/openimagedenoise.cmake) if(WITH_WEBP) include(cmake/webp.cmake) diff --git a/build_files/build_environment/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake index 7fcd7e754ae..fabc055ce29 100644 --- a/build_files/build_environment/cmake/boost.cmake +++ b/build_files/build_environment/cmake/boost.cmake @@ -33,19 +33,9 @@ if(WIN32) set(BOOST_TOOLSET toolset=msvc-14.0) set(BOOST_COMPILER_STRING -vc140) endif() - set(JAM_FILE ${BUILD_DIR}/boost/src/external_boost/user-config.jam) - set(semi_path "${PATCH_DIR}/semi.txt") - FILE(TO_NATIVE_PATH ${semi_path} semi_path) - set(BOOST_CONFIGURE_COMMAND bootstrap.bat && - echo using python : ${PYTHON_OUTPUTDIR}\\python.exe > "${JAM_FILE}" && - echo. : ${BUILD_DIR}/python/src/external_python/include ${BUILD_DIR}/python/src/external_python/pc >> "${JAM_FILE}" && - echo. : ${BUILD_DIR}/python/src/external_python/pcbuild >> "${JAM_FILE}" && - type ${semi_path} >> "${JAM_FILE}" - ) + set(BOOST_CONFIGURE_COMMAND bootstrap.bat) set(BOOST_BUILD_COMMAND bjam) - #--user-config=user-config.jam set(BOOST_BUILD_OPTIONS runtime-link=static ) - #set(BOOST_WITH_PYTHON --with-python) set(BOOST_HARVEST_CMD ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/boost/lib/ ${HARVEST_TARGET}/boost/lib/ ) if(BUILD_MODE STREQUAL Release) set(BOOST_HARVEST_CMD ${BOOST_HARVEST_CMD} && ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/boost/include/boost-1_68/ ${HARVEST_TARGET}/boost/include/) @@ -82,7 +72,6 @@ set(BOOST_OPTIONS --with-serialization --with-program_options --with-iostreams - ${BOOST_WITH_PYTHON} ${BOOST_TOOLSET} ) @@ -100,10 +89,3 @@ ExternalProject_Add(external_boost BUILD_IN_SOURCE 1 INSTALL_COMMAND "${BOOST_HARVEST_CMD}" ) - -if(WIN32) - add_dependencies( - external_boost - Make_Python_Environment - ) -endif() diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake index 27bcd184c44..97e4a6b69d4 100644 --- a/build_files/build_environment/cmake/harvest.cmake +++ b/build_files/build_environment/cmake/harvest.cmake @@ -166,6 +166,8 @@ harvest(openimageio/bin openimageio/bin "maketx") harvest(openimageio/bin openimageio/bin "oiiotool") harvest(openimageio/include openimageio/include "*") harvest(openimageio/lib openimageio/lib "*.a") +harvest(openimagedenoise/include openimagedenoise/include "*") +harvest(openimagedenoise/lib openimagedenoise/lib "*.a") harvest(openjpeg/include/openjpeg-2.3 openjpeg/include "*.h") harvest(openjpeg/lib openjpeg/lib "*.a") harvest(opensubdiv/include opensubdiv/include "*.h") diff --git a/build_files/build_environment/cmake/numpy.cmake b/build_files/build_environment/cmake/numpy.cmake index 1a6ffa95ad6..abf2464e88c 100644 --- a/build_files/build_environment/cmake/numpy.cmake +++ b/build_files/build_environment/cmake/numpy.cmake @@ -17,6 +17,7 @@ # ***** END GPL LICENSE BLOCK ***** if(MSVC) + message("BIN >${PYTHON_BINARY}<") if(BUILD_MODE STREQUAL Debug) set(NUMPY_DIR_POSTFIX -pydebug) set(NUMPY_ARCHIVE_POSTFIX d) @@ -30,17 +31,6 @@ endif() set(NUMPY_POSTFIX) -if(WIN32) - set(NUMPY_INSTALL - ${CMAKE_COMMAND} -E copy_directory "${BUILD_DIR}/python/src/external_python/run/lib/site-packages/numpy/core/include/numpy" "${LIBDIR}/python/include/python${PYTHON_SHORT_VERSION}/numpy" && - ${CMAKE_COMMAND} -E chdir "${BUILD_DIR}/numpy/src/external_numpy/build/lib.${PYTHON_ARCH2}-${PYTHON_SHORT_VERSION}${NUMPY_DIR_POSTFIX}" - ${CMAKE_COMMAND} -E tar "cfvz" "${LIBDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}_numpy_${NUMPY_SHORT_VERSION}${NUMPY_ARCHIVE_POSTFIX}.tar.gz" "." - ) -else() - set(NUMPY_INSTALL echo .) - set(NUMPY_PATCH echo .) -endif() - ExternalProject_Add(external_numpy URL ${NUMPY_URI} DOWNLOAD_DIR ${DOWNLOAD_DIR} @@ -50,17 +40,10 @@ ExternalProject_Add(external_numpy CONFIGURE_COMMAND "" LOG_BUILD 1 BUILD_COMMAND ${PYTHON_BINARY} ${BUILD_DIR}/numpy/src/external_numpy/setup.py build ${NUMPY_BUILD_OPTION} install --old-and-unmanageable - INSTALL_COMMAND ${NUMPY_INSTALL} + INSTALL_COMMAND "" ) -if(WIN32) - ExternalProject_Add_Step(external_numpy after_install - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}_numpy_${NUMPY_SHORT_VERSION}${NUMPY_ARCHIVE_POSTFIX}.tar.gz ${HARVEST_TARGET}/Release/python${PYTHON_SHORT_VERSION_NO_DOTS}_numpy_${NUMPY_SHORT_VERSION}${NUMPY_ARCHIVE_POSTFIX}.tar.gz - DEPENDEES install - ) -endif() - add_dependencies( external_numpy - Make_Python_Environment + external_python ) diff --git a/build_files/build_environment/cmake/openimagedenoise.cmake b/build_files/build_environment/cmake/openimagedenoise.cmake new file mode 100644 index 00000000000..b20bb838ede --- /dev/null +++ b/build_files/build_environment/cmake/openimagedenoise.cmake @@ -0,0 +1,61 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ***** END GPL LICENSE BLOCK ***** + + +set(OIDN_EXTRA_ARGS + -DWITH_EXAMPLE=OFF + -DWITH_TEST=OFF + -DTBB_ROOT=${LIBDIR}/tbb + -DTBB_STATIC_LIB=ON + -DOIDN_STATIC_LIB=ON +) + +ExternalProject_Add(external_openimagedenoise + URL ${OIDN_URI} + DOWNLOAD_DIR ${DOWNLOAD_DIR} + URL_HASH MD5=${OIDN_HASH} + PREFIX ${BUILD_DIR}/openimagedenoise + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openimagedenoise ${DEFAULT_CMAKE_FLAGS} ${OIDN_EXTRA_ARGS} + PATCH_COMMAND ${PATCH_CMD} --verbose -p 1 -N -d ${BUILD_DIR}/openimagedenoise/src/external_openimagedenoise < ${PATCH_DIR}/openimagedenoise.diff + INSTALL_DIR ${LIBDIR}/openimagedenoise +) + +add_dependencies( + external_openimagedenoise + external_tbb +) + +if(WIN32) + if(BUILD_MODE STREQUAL Release) + ExternalProject_Add_Step(external_openimagedenoise after_install + COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/openimagedenoise/include ${HARVEST_TARGET}/openimagedenoise/include + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/openimagedenoise.lib ${HARVEST_TARGET}/openimagedenoise/lib/openimagedenoise.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/common.lib ${HARVEST_TARGET}/openimagedenoise/lib/common.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/mkldnn.lib ${HARVEST_TARGET}/openimagedenoise/lib/mkldnn.lib + DEPENDEES install + ) + endif() + if(BUILD_MODE STREQUAL Debug) + ExternalProject_Add_Step(external_openimagedenoise after_install + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/openimagedenoise.lib ${HARVEST_TARGET}/openimagedenoise/lib/openimagedenoise_d.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/common.lib ${HARVEST_TARGET}/openimagedenoise/lib/common_d.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/mkldnn.lib ${HARVEST_TARGET}/openimagedenoise/lib/mkldnn_d.lib + DEPENDEES install + ) + endif() +endif() diff --git a/build_files/build_environment/cmake/package_python.cmake b/build_files/build_environment/cmake/package_python.cmake new file mode 100644 index 00000000000..e2eda66a4e0 --- /dev/null +++ b/build_files/build_environment/cmake/package_python.cmake @@ -0,0 +1,58 @@ +# ***** 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 ***** + +if(MSVC) + set(PYTARGET ${HARVEST_TARGET}/python/${PYTHON_SHORT_VERSION_NO_DOTS}) + set(PYSRC ${LIBDIR}/python/) + + if(BUILD_MODE STREQUAL Release) + add_custom_command( + OUTPUT ${PYTARGET}/bin/python${PYTHON_POSTFIX}.exe + COMMAND echo packaging python + COMMAND echo this should ouput at ${PYTARGET}/bin/python${PYTHON_POSTFIX}.exe + COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTARGET}/libs + COMMAND ${CMAKE_COMMAND} -E copy ${PYSRC}/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}.lib ${PYTARGET}/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}.lib + COMMAND ${CMAKE_COMMAND} -E copy ${PYSRC}/python.exe ${PYTARGET}/bin/python.exe + COMMAND ${CMAKE_COMMAND} -E copy ${PYSRC}/python${PYTHON_SHORT_VERSION_NO_DOTS}.dll ${PYTARGET}/bin/python${PYTHON_SHORT_VERSION_NO_DOTS}.dll + COMMAND ${CMAKE_COMMAND} -E copy ${PYSRC}/python${PYTHON_SHORT_VERSION_NO_DOTS}.pdb ${PYTARGET}/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}.pdb + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PYSRC}/include/ ${PYTARGET}/include/ + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PYSRC}/lib/ ${PYTARGET}/lib/ + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PYSRC}/DLLs/ ${PYTARGET}/DLLs/ + COMMAND cd ${PYTARGET}/lib/ && for /d /r . %%d in (__pycache__) do @if exist "%%d" echo "%%d" && rd /s/q "%%d" + ) + add_custom_target(Package_Python ALL DEPENDS external_python external_numpy external_python_site_packages OUTPUT ${HARVEST_TARGET}/python/${PYTHON_SHORT_VERSION_NO_DOTS}/bin/python${PYTHON_POSTFIX}.exe) + endif() + + if(BUILD_MODE STREQUAL Debug) + add_custom_command( + OUTPUT ${PYTARGET}/bin/python${PYTHON_POSTFIX}.exe + COMMAND echo packaging python + COMMAND echo this should ouput at ${PYTARGET}/bin/python${PYTHON_POSTFIX}.exe + COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTARGET}/libs + COMMAND ${CMAKE_COMMAND} -E copy ${PYSRC}/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.lib ${PYTARGET}/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.lib + COMMAND ${CMAKE_COMMAND} -E copy ${PYSRC}/python${PYTHON_POSTFIX}.exe ${PYTARGET}/bin/python${PYTHON_POSTFIX}.exe + COMMAND ${CMAKE_COMMAND} -E copy ${PYSRC}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.dll ${PYTARGET}/bin/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.dll + COMMAND ${CMAKE_COMMAND} -E copy ${PYSRC}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.pdb ${PYTARGET}/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.pdb + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PYSRC}/include/ ${PYTARGET}/include/ + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PYSRC}/lib/ ${PYTARGET}/lib/ + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PYSRC}/DLLs/ ${PYTARGET}/DLLs/ + COMMAND cd ${PYTARGET}/lib/ && for /d /r . %%d in (__pycache__) do @if exist "%%d" echo "%%d" && rd /s/q "%%d" + ) + add_custom_target(Package_Python ALL DEPENDS external_python external_numpy external_python_site_packages OUTPUT ${PYTARGET}/bin/python${PYTHON_POSTFIX}.exe) + endif() +endif() diff --git a/build_files/build_environment/cmake/python.cmake b/build_files/build_environment/cmake/python.cmake index cb3aefa3a9d..ee218e9782a 100644 --- a/build_files/build_environment/cmake/python.cmake +++ b/build_files/build_environment/cmake/python.cmake @@ -19,17 +19,13 @@ set(PYTHON_POSTFIX) if(BUILD_MODE STREQUAL Debug) set(PYTHON_POSTFIX _d) + set(PYTHON_EXTRA_INSTLAL_FLAGS -d) endif() if(WIN32) - if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(SSL_POSTFIX -x64) - else() - set(SSL_POSTFIX) - endif() - - set(PYTHON_BINARY ${BUILD_DIR}/python/src/external_python/run/python${PYTHON_POSTFIX}.exe) - + set(PYTHON_BINARY_INTERNAL ${BUILD_DIR}/python/src/external_python/PCBuild/amd64/python${PYTHON_POSTFIX}.exe) + set(PYTHON_BINARY ${LIBDIR}/python/python${PYTHON_POSTFIX}.exe) + set(PYTHON_SRC ${BUILD_DIR}/python/src/external_python/) macro(cmake_to_dos_path MsysPath ResultingPath) string(REPLACE "/" "\\" ${ResultingPath} "${MsysPath}") endmacro() @@ -40,31 +36,16 @@ if(WIN32) cmake_to_dos_path(${PYTHON_EXTERNALS_FOLDER} PYTHON_EXTERNALS_FOLDER_DOS) cmake_to_dos_path(${DOWNLOADS_EXTERNALS_FOLDER} DOWNLOADS_EXTERNALS_FOLDER_DOS) - message("Python externals = ${PYTHON_EXTERNALS_FOLDER}") - message("Python externals_dos = ${PYTHON_EXTERNALS_FOLDER_DOS}") - message("Python DOWNLOADS_EXTERNALS_FOLDER = ${DOWNLOADS_EXTERNALS_FOLDER}") - message("Python DOWNLOADS_EXTERNALS_FOLDER_DOS = ${DOWNLOADS_EXTERNALS_FOLDER_DOS}") - ExternalProject_Add(external_python URL ${PYTHON_URI} DOWNLOAD_DIR ${DOWNLOAD_DIR} URL_HASH MD5=${PYTHON_HASH} PREFIX ${BUILD_DIR}/python - PATCH_COMMAND - echo mklink /D "${PYTHON_EXTERNALS_FOLDER_DOS}" "${DOWNLOADS_EXTERNALS_FOLDER_DOS}" && - mklink /D "${PYTHON_EXTERNALS_FOLDER_DOS}" "${DOWNLOADS_EXTERNALS_FOLDER_DOS}" CONFIGURE_COMMAND "" BUILD_COMMAND cd ${BUILD_DIR}/python/src/external_python/pcbuild/ && set IncludeTkinter=false && call build.bat -e -p ${PYTHON_ARCH} -c ${BUILD_MODE} - INSTALL_COMMAND COMMAND - ${CMAKE_COMMAND} -E copy ${PYTHON_OUTPUTDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.dll ${LIBDIR}/python/lib/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.dll && - ${CMAKE_COMMAND} -E copy ${PYTHON_OUTPUTDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.pdb ${LIBDIR}/python/lib/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.pdb && - ${CMAKE_COMMAND} -E copy ${PYTHON_OUTPUTDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.lib ${LIBDIR}/python/lib/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.lib && - ${CMAKE_COMMAND} -E copy ${PYTHON_OUTPUTDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.exp ${LIBDIR}/python/lib/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.exp && - ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/include ${LIBDIR}/python/include/Python${PYTHON_SHORT_VERSION} && - ${CMAKE_COMMAND} -E copy "${BUILD_DIR}/python/src/external_python/PC/pyconfig.h" ${LIBDIR}/python/include/Python${PYTHON_SHORT_VERSION}/pyconfig.h + INSTALL_COMMAND ${PYTHON_BINARY_INTERNAL} ${PYTHON_SRC}/PC/layout/main.py -b ${PYTHON_SRC}/PCbuild/amd64 -s ${PYTHON_SRC} -t ${PYTHON_SRC}/tmp/ --include-underpth --include-stable --include-pip --include-dev --include-launchers --include-symbols ${PYTHON_EXTRA_INSTLAL_FLAGS} --copy ${LIBDIR}/python ) - message("PythinRedist = ${BUILD_DIR}/python/src/external_python/redist") - message("POutput = ${PYTHON_OUTPUTDIR}") + else() if(APPLE) # disable functions that can be in 10.13 sdk but aren't available on 10.9 target @@ -107,65 +88,6 @@ else() BUILD_COMMAND ${PYTHON_CONFIGURE_ENV} && cd ${BUILD_DIR}/python/src/external_python/ && make -j${MAKE_THREADS} INSTALL_COMMAND ${PYTHON_CONFIGURE_ENV} && cd ${BUILD_DIR}/python/src/external_python/ && make install INSTALL_DIR ${LIBDIR}/python) - - add_custom_target(Make_Python_Environment ALL DEPENDS external_python) -endif() - -if(MSVC) - add_custom_command( - OUTPUT ${LIBDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.tar.gz - OUTPUT ${BUILD_DIR}/python/src/external_python/redist/bin/python${PYTHON_POSTFIX}.exe - COMMAND ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/lib ${BUILD_DIR}/python/src/external_python/redist/lib - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_asyncio${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_asyncio${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_bz2${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_bz2${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_contextvars${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_contextvars${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_ctypes${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_ctypes${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_ctypes_test${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_ctypes_test${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_decimal${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_decimal${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_distutils_findvs${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_distutils_findvs${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_elementtree${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_elementtree${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_hashlib${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_hashlib${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_lzma${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_lzma${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_msi${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_msi${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_multiprocessing${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_multiprocessing${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_overlapped${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_overlapped${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_queue${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_queue${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_socket${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_socket${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_sqlite3${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_sqlite3${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_ssl${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_ssl${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_testbuffer${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_testbuffer${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_testcapi${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_testcapi${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_testimportmultiple${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_testimportmultiple${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/_testmultiphase${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/_testmultiphase${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/pyexpat${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/pyexpat${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/python${PYTHON_POSTFIX}.exe" ${BUILD_DIR}/python/src/external_python/redist/bin/python${PYTHON_POSTFIX}.exe - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/select${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/select${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/unicodedata${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/unicodedata${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/winsound${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/winsound${PYTHON_POSTFIX}.pyd - #xxlimited is an example extension module, we don't need to ship it and debug doesn't build it - #leaving it commented out, so I won't get confused again with the next update. - #COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/xxlimited${PYTHON_POSTFIX}.pyd" ${BUILD_DIR}/python/src/external_python/redist/lib/xxlimited${PYTHON_POSTFIX}.pyd - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/libssl-1_1${SSL_POSTFIX}.dll" ${BUILD_DIR}/python/src/external_python/redist/lib/libssl-1_1${SSL_POSTFIX}.dll - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/libcrypto-1_1${SSL_POSTFIX}.dll" ${BUILD_DIR}/python/src/external_python/redist/lib/libcrypto-1_1${SSL_POSTFIX}.dll - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/sqlite3${PYTHON_POSTFIX}.dll" ${BUILD_DIR}/python/src/external_python/redist/lib/sqlite3${PYTHON_POSTFIX}.dll - COMMAND ${CMAKE_COMMAND} -E chdir "${BUILD_DIR}/python/src/external_python/redist" ${CMAKE_COMMAND} -E tar "cfvz" "${LIBDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.tar.gz" "." - COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/python/ ${HARVEST_TARGET}/python/ - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.tar.gz ${HARVEST_TARGET}/Release/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.tar.gz - ) - - add_custom_target(Package_Python ALL DEPENDS external_python ${LIBDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.tar.gz ${BUILD_DIR}/python/src/external_python/redist/bin/python${PYTHON_POSTFIX}.exe) - - add_custom_command(OUTPUT ${BUILD_DIR}/python/src/external_python/run/python${PYTHON_POSTFIX}.exe - COMMAND ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/redist ${BUILD_DIR}/python/src/external_python/run - COMMAND ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/include ${BUILD_DIR}/python/src/external_python/run/include - COMMAND ${CMAKE_COMMAND} -E copy "${BUILD_DIR}/python/src/external_python/PC/pyconfig.h" ${BUILD_DIR}/python/src/external_python/run/include/pyconfig.h - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.dll" ${BUILD_DIR}/python/src/external_python/run/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.dll - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.lib" ${BUILD_DIR}/python/src/external_python/run/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}.lib #missing postfix on purpose, distutils is not expecting it - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.lib" ${BUILD_DIR}/python/src/external_python/run/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}${PYTHON_POSTFIX}.lib #other things like numpy still want it though. - COMMAND ${CMAKE_COMMAND} -E copy "${PYTHON_OUTPUTDIR}/python${PYTHON_POSTFIX}.exe" ${BUILD_DIR}/python/src/external_python/run/python${PYTHON_POSTFIX}.exe - COMMAND ${BUILD_DIR}/python/src/external_python/run/python${PYTHON_POSTFIX}.exe -m ensurepip --upgrade - ) - add_custom_target(Make_Python_Environment ALL DEPENDS ${BUILD_DIR}/python/src/external_python/run/python${PYTHON_POSTFIX}.exe Package_Python) endif() if(UNIX) diff --git a/build_files/build_environment/cmake/python_site_packages.cmake b/build_files/build_environment/cmake/python_site_packages.cmake index 3a2901cadb7..d17f65a152b 100644 --- a/build_files/build_environment/cmake/python_site_packages.cmake +++ b/build_files/build_environment/cmake/python_site_packages.cmake @@ -15,27 +15,16 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ***** END GPL LICENSE BLOCK ***** -if(WIN32) - set(HARVEST_CMD cmd /C FOR /d /r ${BUILD_DIR}/python/src/external_python/run/lib/site-packages %d IN (__pycache__) DO @IF EXIST "%d" rd /s /q "%d" && - ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/run/lib/site-packages/idna ${HARVEST_TARGET}/Release/site-packages/idna && - ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/run/lib/site-packages/chardet ${HARVEST_TARGET}/Release/site-packages/chardet && - ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/run/lib/site-packages/urllib3 ${HARVEST_TARGET}/Release/site-packages/urllib3 && - ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/run/lib/site-packages/certifi ${HARVEST_TARGET}/Release/site-packages/certifi && - ${CMAKE_COMMAND} -E copy_directory ${BUILD_DIR}/python/src/external_python/run/lib/site-packages/requests ${HARVEST_TARGET}/Release/site-packages/requests - ) -else() - set(HARVEST_CMD echo .) -endif() ExternalProject_Add(external_python_site_packages DOWNLOAD_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" PREFIX ${BUILD_DIR}/site_packages - INSTALL_COMMAND ${PYTHON_BINARY} -m pip install idna==${IDNA_VERSION} chardet==${CHARDET_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} --no-binary :all: && ${HARVEST_CMD} + INSTALL_COMMAND ${PYTHON_BINARY} -m pip install idna==${IDNA_VERSION} chardet==${CHARDET_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} --no-binary :all: ) add_dependencies( external_python_site_packages - Make_Python_Environment + external_python ) diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake index 77f061e30d0..26c52e00c76 100644 --- a/build_files/build_environment/cmake/tbb.cmake +++ b/build_files/build_environment/cmake/tbb.cmake @@ -18,7 +18,7 @@ set(TBB_EXTRA_ARGS -DTBB_BUILD_SHARED=Off - -DTBB_BUILD_TBBMALLOC=Off + -DTBB_BUILD_TBBMALLOC=On -DTBB_BUILD_TBBMALLOC_PROXY=Off -DTBB_BUILD_STATIC=On ) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 1d3da863898..c3b713096d6 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -157,16 +157,16 @@ set(OPENVDB_VERSION 5.1.0) set(OPENVDB_URI https://github.com/dreamworksanimation/openvdb/archive/v${OPENVDB_VERSION}.tar.gz) set(OPENVDB_HASH 5310101f874dcfd2165f9cee68c22624) -set(IDNA_VERSION 2.7) +set(IDNA_VERSION 2.8) set(CHARDET_VERSION 3.0.4) -set(URLLIB3_VERSION 1.23) -set(CERTIFI_VERSION 2018.8.13) -set(REQUESTS_VERSION 2.19.1) +set(URLLIB3_VERSION 1.25.3) +set(CERTIFI_VERSION 2019.6.16) +set(REQUESTS_VERSION 2.22.0) -set(NUMPY_VERSION v1.15.0) -set(NUMPY_SHORT_VERSION 1.15) -set(NUMPY_URI https://files.pythonhosted.org/packages/3a/20/c81632328b1a4e1db65f45c0a1350a9c5341fd4bbb8ea66cdd98da56fe2e/numpy-1.15.0.zip) -set(NUMPY_HASH 20e13185089011116a98e11c9bf8aa07) +set(NUMPY_VERSION v1.17.0) +set(NUMPY_SHORT_VERSION 1.17) +set(NUMPY_URI https://files.pythonhosted.org/packages/da/32/1b8f2bb5fb50e4db68543eb85ce37b9fa6660cd05b58bddfafafa7ed62da/numpy-1.17.0.zip) +set(NUMPY_HASH aed49b31bcb44ec73b8155be78566135) set(LAME_VERSION 3.100) set(LAME_URI http://downloads.sourceforge.net/project/lame/lame/3.100/lame-${LAME_VERSION}.tar.gz) @@ -302,3 +302,7 @@ set(SQLITE_HASH fb558c49ee21a837713c4f1e7e413309aabdd9c7) set(EMBREE_VERSION 3.2.4) set(EMBREE_URI https://github.com/embree/embree/archive/v${EMBREE_VERSION}.zip) set(EMBREE_HASH 3d4a1147002ff43939d45140aa9d6fb8) + +set(OIDN_VERSION 1.0.0) +set(OIDN_URI https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.zip) +set(OIDN_HASH 19fe67b0164e8f020ac8a4f520defe60) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index e4f03480385..a9118be93c7 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -26,17 +26,17 @@ ARGS=$( \ getopt \ -o s:i:t:h \ --long source:,install:,tmp:,info:,threads:,help,show-deps,no-sudo,no-build,no-confirm,\ -with-all,with-opencollada,with-jack,with-embree,\ +with-all,with-opencollada,with-jack,with-embree,with-oidn,\ ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,\ force-all,force-python,force-numpy,force-boost,\ force-ocio,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\ -force-ffmpeg,force-opencollada,force-alembic,force-embree,\ +force-ffmpeg,force-opencollada,force-alembic,force-embree,force-oidn,\ build-all,build-python,build-numpy,build-boost,\ build-ocio,build-openexr,build-oiio,build-llvm,build-osl,build-osd,build-openvdb,\ -build-ffmpeg,build-opencollada,build-alembic,build-embree,\ +build-ffmpeg,build-opencollada,build-alembic,build-embree,build-oidn,\ skip-python,skip-numpy,skip-boost,\ skip-ocio,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-osd,skip-openvdb,\ -skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree \ +skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree,skip-oidn \ -- "$@" \ ) @@ -57,6 +57,7 @@ WITH_ALL=false # Do not yet enable opencollada or embree, use --with-opencollada/--with-embree (or --with-all) option to try it. WITH_OPENCOLLADA=false WITH_EMBREE=false +WITH_OIDN=false THREADS=$(nproc) @@ -69,6 +70,7 @@ Number of threads for building: \$THREADS (automatically detected, use --threads Full install: \$WITH_ALL (use --with-all option to enable it). Building OpenCOLLADA: \$WITH_OPENCOLLADA (use --with-opencollada option to enable it). Building Embree: \$WITH_EMBREE (use --with-embree option to enable it). +Building OpenImageDenoise: \$WITH_OIDN (use --with-oidn option to enable it). Example: Full install without OpenCOLLADA: --with-all --skip-opencollada @@ -118,6 +120,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: --with-embree Build and install the Embree libraries. + --with-oidn + Build and install the OpenImageDenoise libraries. + --with-jack Install the jack libraries. @@ -185,6 +190,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: --build-embree Force the build of Embree. + --build-oidn + Force the build of OpenImageDenoise. + --build-ffmpeg Force the build of FFMpeg. @@ -240,6 +248,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: --force-embree Force the rebuild of Embree. + --force-oidn + Force the rebuild of OpenImageDenoise. + --force-ffmpeg Force the rebuild of FFMpeg. @@ -288,6 +299,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: --skip-Embree Unconditionally skip Embree installation/building. + --skip-oidn + Unconditionally skip OpenImageDenoise installation/building. + --skip-ffmpeg Unconditionally skip FFMpeg installation/building.\"" @@ -309,7 +323,7 @@ PYTHON_FORCE_BUILD=false PYTHON_FORCE_REBUILD=false PYTHON_SKIP=false -NUMPY_VERSION="1.15.0" +NUMPY_VERSION="1.17.0" NUMPY_VERSION_MIN="1.8" NUMPY_FORCE_BUILD=false NUMPY_FORCE_REBUILD=false @@ -390,6 +404,11 @@ EMBREE_FORCE_BUILD=false EMBREE_FORCE_REBUILD=false EMBREE_SKIP=false +OIDN_VERSION="1.0.0" +OIDN_FORCE_BUILD=false +OIDN_FORCE_REBUILD=false +OIDN_SKIP=false + FFMPEG_VERSION="4.0.2" FFMPEG_VERSION_MIN="2.8.4" FFMPEG_FORCE_BUILD=false @@ -526,6 +545,9 @@ while true; do --with-embree) WITH_EMBREE=true; shift; continue ;; + --with-oidn) + WITH_OIDN=true; shift; continue + ;; --with-jack) WITH_JACK=true; shift; continue; ;; @@ -572,6 +594,7 @@ while true; do OPENVDB_FORCE_BUILD=true OPENCOLLADA_FORCE_BUILD=true EMBREE_FORCE_BUILD=true + OIDN_FORCE_BUILD=true FFMPEG_FORCE_BUILD=true ALEMBIC_FORCE_BUILD=true shift; continue @@ -616,6 +639,9 @@ while true; do --build-embree) EMBREE_FORCE_BUILD=true; shift; continue ;; + --build-oidn) + OIDN_FORCE_BUILD=true; shift; continue + ;; --build-ffmpeg) FFMPEG_FORCE_BUILD=true; shift; continue ;; @@ -635,6 +661,7 @@ while true; do OPENVDB_FORCE_REBUILD=true OPENCOLLADA_FORCE_REBUILD=true EMBREE_FORCE_REBUILD=true + OIDN_FORCE_REBUILD=true FFMPEG_FORCE_REBUILD=true ALEMBIC_FORCE_REBUILD=true shift; continue @@ -677,6 +704,9 @@ while true; do --force-embree) EMBREE_FORCE_REBUILD=true; shift; continue ;; + --force-oidn) + OIDN_FORCE_REBUILD=true; shift; continue + ;; --force-ffmpeg) FFMPEG_FORCE_REBUILD=true; shift; continue ;; @@ -719,6 +749,9 @@ while true; do --skip-embree) EMBREE_SKIP=true; shift; continue ;; + --skip-oidn) + OIDN_SKIP=true; shift; continue + ;; --skip-ffmpeg) FFMPEG_SKIP=true; shift; continue ;; @@ -746,6 +779,9 @@ fi if [ "$WITH_ALL" = true -a "$EMBREE_SKIP" = false ]; then WITH_EMBREE=true fi +if [ "$WITH_ALL" = true -a "$OIDN_SKIP" = false ]; then + WITH_OIDN=true +fi if [ "$WITH_ALL" = true ]; then WITH_JACK=true fi @@ -840,6 +876,11 @@ EMBREE_SOURCE=( "https://github.com/embree/embree/archive/v${EMBREE_VERSION}.tar #~ EMBREE_REPO_UID="4a12bfed63c90e85b6eab98b8cdd8dd2a3ba5809" #~ EMBREE_REPO_BRANCH="master" +OIDN_USE_REPO=false +OIDN_SOURCE=( "https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.tar.gz" ) +#~ OIDN_SOURCE_REPO=( "https://github.com/OpenImageDenoise/oidn.git" ) +#~ OIDN_REPO_UID="dabfd9c80101edae9d25a710160d12d6d963c591" +#~ OIDN_REPO_BRANCH="master" FFMPEG_SOURCE=( "http://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.bz2" ) @@ -882,6 +923,7 @@ You may also want to build them yourself (optional ones are [between brackets]): * [OpenVDB $OPENVDB_VERSION_MIN] (from $OPENVDB_SOURCE), [Blosc $OPENVDB_BLOSC_VERSION] (from $OPENVDB_BLOSC_SOURCE). * [OpenCollada $OPENCOLLADA_VERSION] (from $OPENCOLLADA_SOURCE). * [Embree $EMBREE_VERSION] (from $EMBREE_SOURCE). + * [OpenImageDenoise $OIDN_VERSION] (from $OIDN_SOURCE). * [Alembic $ALEMBIC_VERSION] (from $ALEMBIC_SOURCE).\"" if [ "$DO_SHOW_DEPS" = true ]; then @@ -2552,6 +2594,96 @@ compile_Embree() { fi } +#### Build OpenImageDenoise #### +_init_oidn() { + _src=$SRC/oidn-$OIDN_VERSION + _git=true + _inst=$INST/oidn-$OIDN_VERSION + _inst_shortcut=$INST/oidn +} + +clean_oidn() { + _init_oidn + _clean +} + +compile_OIDN() { + if [ "$NO_BUILD" = true ]; then + WARNING "--no-build enabled, OpenImageDenoise will not be compiled!" + return + fi + + # To be changed each time we make edits that would modify the compiled results! + oidn_magic=9 + _init_oidn + + # Clean install if needed! + magic_compile_check oidn-$OIDN_VERSION $oidn_magic + if [ $? -eq 1 -o "$OIDN_FORCE_REBUILD" = true ]; then + clean_oidn + fi + + if [ ! -d $_inst ]; then + INFO "Building OpenImageDenoise-$OIDN_VERSION" + + prepare_opt + + if [ ! -d $_src ]; then + mkdir -p $SRC + if [ "OIDN_USE_REPO" = true ]; then + git clone $OIDN_SOURCE_REPO $_src + else + download OIDN_SOURCE[@] "$_src.tar.gz" + INFO "Unpacking OpenImageDenoise-$OIDN_VERSION" + tar -C $SRC -xf $_src.tar.gz + fi + fi + + cd $_src + + if [ "$OIDN_USE_REPO" = true ]; then + git pull origin $OIDN_REPO_BRANCH + + # Stick to same rev as windows' libs... + git checkout $OIDN_REPO_UID + git reset --hard + fi + + # Always refresh the whole build! + if [ -d build ]; then + rm -rf build + fi + mkdir build + cd build + + cmake_d="-D CMAKE_BUILD_TYPE=Release" + cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst" + cmake_d="$cmake_d -D WITH_EXAMPLE=OFF" + cmake_d="$cmake_d -D WITH_TEST=OFF" + cmake_d="$cmake_d -D OIDN_STATIC_LIB=ON" + + cmake $cmake_d ../ + + make -j$THREADS && make install + make clean + + if [ -d $_inst ]; then + _create_inst_shortcut + else + ERROR "OpenImageDenoise-$OIDN_VERSION failed to compile, exiting" + exit 1 + fi + + magic_compile_set oidn-$OIDN_VERSION $oidn_magic + + cd $CWD + INFO "Done compiling OpenImageDenoise-$OIDN_VERSION!" + else + INFO "Own OpenImageDenoise-$OIDN_VERSION is up to date, nothing to do!" + INFO "If you want to force rebuild of this lib, use the --force-oidn option." + fi +} + #### Build FFMPEG #### _init_ffmpeg() { _src=$SRC/ffmpeg-$FFMPEG_VERSION @@ -3148,6 +3280,24 @@ install_DEB() { fi fi + if [ "$WITH_OIDN" = true ]; then + _do_compile_oidn=false + PRINT "" + if [ "$OIDN_SKIP" = true ]; then + WARNING "Skipping OpenImgeDenoise installation, as requested..." + elif [ "$OIDN_FORCE_BUILD" = true ]; then + INFO "Forced OpenImageDenoise building, as requested..." + _do_compile_oidn=true + else + # No package currently! + _do_compile_oidn=true + fi + + if [ "$_do_compile_oidn" = true ]; then + compile_OIDN + fi + fi + PRINT "" if [ "$FFMPEG_SKIP" = true ]; then WARNING "Skipping FFMpeg installation, as requested..." @@ -3722,6 +3872,24 @@ install_RPM() { fi fi + if [ "$WITH_OIDN" = true ]; then + _do_compile_oidn=false + PRINT "" + if [ "$OIDN_SKIP" = true ]; then + WARNING "Skipping OpenImgeDenoise installation, as requested..." + elif [ "$OIDN_FORCE_BUILD" = true ]; then + INFO "Forced OpenImageDenoise building, as requested..." + _do_compile_oidn=true + else + # No package currently! + _do_compile_oidn=true + fi + + if [ "$_do_compile_oidn" = true ]; then + compile_OIDN + fi + fi + PRINT "" if [ "$FFMPEG_SKIP" = true ]; then WARNING "Skipping FFMpeg installation, as requested..." @@ -4186,6 +4354,24 @@ install_ARCH() { fi fi + if [ "$WITH_OIDN" = true ]; then + _do_compile_oidn=false + PRINT "" + if [ "$OIDN_SKIP" = true ]; then + WARNING "Skipping OpenImgeDenoise installation, as requested..." + elif [ "$OIDN_FORCE_BUILD" = true ]; then + INFO "Forced OpenImageDenoise building, as requested..." + _do_compile_oidn=true + else + # No package currently! + _do_compile_oidn=true + fi + + if [ "$_do_compile_oidn" = true ]; then + compile_OIDN + fi + fi + PRINT "" if [ "$FFMPEG_SKIP" = true ]; then WARNING "Skipping FFMpeg installation, as requested..." @@ -4372,6 +4558,24 @@ install_OTHER() { fi fi + if [ "$WITH_OIDN" = true ]; then + _do_compile_oidn=false + PRINT "" + if [ "$OIDN_SKIP" = true ]; then + WARNING "Skipping OpenImgeDenoise installation, as requested..." + elif [ "$OIDN_FORCE_BUILD" = true ]; then + INFO "Forced OpenImageDenoise building, as requested..." + _do_compile_oidn=true + else + # No package currently! + _do_compile_oidn=true + fi + + if [ "$_do_compile_oidn" = true ]; then + compile_OIDN + fi + fi + PRINT "" if [ "$FFMPEG_SKIP" = true ]; then WARNING "Skipping FFMpeg installation, as requested..." @@ -4587,6 +4791,17 @@ print_info() { fi fi + if [ "$WITH_OIDN" = true ]; then + _1="-D WITH_OPENIMAGEDENOISE=ON" + PRINT " $_1" + _buildargs="$_buildargs $_1" + if [ -d $INST/oidn ]; then + _1="-D OPENIMAGEDENOISE_ROOT_DIR=$INST/oidn" + PRINT " $_1" + _buildargs="$_buildargs $_1" + fi + fi + if [ "$WITH_JACK" = true ]; then _1="-D WITH_JACK=ON" _2="-D WITH_JACK_DYNLOAD=ON" diff --git a/build_files/build_environment/patches/openimagedenoise.diff b/build_files/build_environment/patches/openimagedenoise.diff new file mode 100644 index 00000000000..08d7a397a6d --- /dev/null +++ b/build_files/build_environment/patches/openimagedenoise.diff @@ -0,0 +1,119 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 70ec895..e616b63 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -178,7 +178,9 @@ set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION "0") + ## Open Image Denoise examples + ## ---------------------------------------------------------------------------- + +-add_subdirectory(examples) ++if(WITH_EXAMPLE) ++ add_subdirectory(examples) ++endif() + + ## ---------------------------------------------------------------------------- + ## Open Image Denoise install and packaging +Submodule mkl-dnn contains modified content +diff --git a/mkl-dnn/cmake/TBB.cmake b/mkl-dnn/cmake/TBB.cmake +index 0711e699..c14210b6 100644 +--- a/mkl-dnn/cmake/TBB.cmake ++++ b/mkl-dnn/cmake/TBB.cmake +@@ -90,8 +90,8 @@ if(WIN32) + NO_DEFAULT_PATH + ) + set(TBB_LIB_DIR ${TBB_ROOT}/lib/${TBB_ARCH}/${TBB_VCVER}) +- find_library(TBB_LIBRARY tbb PATHS ${TBB_LIB_DIR} ${TBB_ROOT}/lib NO_DEFAULT_PATH) +- find_library(TBB_LIBRARY_MALLOC tbbmalloc PATHS ${TBB_LIB_DIR} ${TBB_ROOT}/lib NO_DEFAULT_PATH) ++ find_library(TBB_LIBRARY tbb_static PATHS ${TBB_LIB_DIR} ${TBB_ROOT}/lib NO_DEFAULT_PATH) ++ find_library(TBB_LIBRARY_MALLOC tbbmalloc_static PATHS ${TBB_LIB_DIR} ${TBB_ROOT}/lib NO_DEFAULT_PATH) + endif() + + else() +@@ -138,13 +138,13 @@ else() + set(TBB_LIBRARY_MALLOC TBB_LIBRARY_MALLOC-NOTFOUND) + if(APPLE) + find_path(TBB_INCLUDE_DIR tbb/task_scheduler_init.h PATHS ${TBB_ROOT}/include NO_DEFAULT_PATH) +- find_library(TBB_LIBRARY tbb PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH) +- find_library(TBB_LIBRARY_MALLOC tbbmalloc PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH) ++ find_library(TBB_LIBRARY tbb_static PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH) ++ find_library(TBB_LIBRARY_MALLOC tbbmalloc_static PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH) + else() + find_path(TBB_INCLUDE_DIR tbb/task_scheduler_init.h PATHS ${TBB_ROOT}/include NO_DEFAULT_PATH) + set(TBB_HINTS HINTS ${TBB_ROOT}/lib/intel64/gcc4.4 ${TBB_ROOT}/lib ${TBB_ROOT}/lib64 PATHS /usr/libx86_64-linux-gnu/) +- find_library(TBB_LIBRARY tbb ${TBB_HINTS}) +- find_library(TBB_LIBRARY_MALLOC tbbmalloc ${TBB_HINTS}) ++ find_library(TBB_LIBRARY tbb_static ${TBB_HINTS}) ++ find_library(TBB_LIBRARY_MALLOC tbbmalloc_static ${TBB_HINTS}) + endif() + endif() + +diff '--ignore-matching-lines=:' -ur '--exclude=*.svn*' -u -r +--- a/cmake/install.cmake 2019-08-12 18:02:20.794402575 +0200 ++++ b/cmake/install.cmake 2019-08-12 18:06:07.470045703 +0200 +@@ -18,6 +18,13 @@ + ## Install library + ## ---------------------------------------------------------------------------- + ++if(UNIX) ++install(FILES ++ ${CMAKE_BINARY_DIR}/libOpenImageDenoise.a ++ ${CMAKE_BINARY_DIR}/libmkldnn.a ++ ${CMAKE_BINARY_DIR}/libcommon.a ++ DESTINATION ${CMAKE_INSTALL_LIBDIR}) ++else() + install(TARGETS ${PROJECT_NAME} + EXPORT + ${PROJECT_NAME}_Export +@@ -38,6 +45,7 @@ + DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel + ) + endif() ++endif() + + ## ---------------------------------------------------------------------------- + ## Install headers +@@ -78,6 +86,7 @@ + ## Install CMake configuration files + ## ---------------------------------------------------------------------------- + ++if(NOT UNIX) + install(EXPORT ${PROJECT_NAME}_Export + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + #NAMESPACE ${PROJECT_NAME}:: +@@ -92,3 +101,4 @@ + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + COMPONENT devel + ) ++endif() +diff '--ignore-matching-lines=:' -ur '--exclude=*.svn*' -u -r +--- a/CMakeLists.txt 2019-08-12 14:22:00.974078598 +0200 ++++ b/CMakeLists.txt 2019-08-12 18:05:05.949057375 +0200 +@@ -14,7 +14,11 @@ + ## limitations under the License. ## + ## ======================================================================== ## + +-cmake_minimum_required(VERSION 3.1) ++if(UNIX) ++ cmake_minimum_required(VERSION 3.1) ++else() ++ cmake_minimum_required(VERSION 3.13) ++endif() + + set(OIDN_VERSION_MAJOR 1) + set(OIDN_VERSION_MINOR 0) +@@ -32,13 +36,8 @@ + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") + + # Build as shared or static library +-if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0") +- option(OIDN_STATIC_LIB "Build Open Image Denoise as a static library.") +- mark_as_advanced(CLEAR OIDN_STATIC_LIB) +-else() +- set(OIDN_STATIC_LIB OFF CACHE BOOL "Build Open Image Denoise as a static library." FORCE) +- mark_as_advanced(OIDN_STATIC_LIB) +-endif() ++option(OIDN_STATIC_LIB "Build Open Image Denoise as a static library.") ++mark_as_advanced(CLEAR OIDN_STATIC_LIB) + if(OIDN_STATIC_LIB) + set(OIDN_LIB_TYPE STATIC) + else() diff --git a/build_files/cmake/Modules/FindOpenImageDenoise.cmake b/build_files/cmake/Modules/FindOpenImageDenoise.cmake new file mode 100644 index 00000000000..85ba10b14e4 --- /dev/null +++ b/build_files/cmake/Modules/FindOpenImageDenoise.cmake @@ -0,0 +1,101 @@ +# - Find OpenImageDenoise library +# Find the native OpenImageDenoise includes and library +# This module defines +# OPENIMAGEDENOISE_INCLUDE_DIRS, where to find oidn.h, Set when +# OPENIMAGEDENOISE is found. +# OPENIMAGEDENOISE_LIBRARIES, libraries to link against to use OpenImageDenoise. +# OPENIMAGEDENOISE_ROOT_DIR, The base directory to search for OpenImageDenoise. +# This can also be an environment variable. +# OPENIMAGEDENOISE_FOUND, If false, do not try to use OpenImageDenoise. +# +# also defined, but not for general use are +# OPENIMAGEDENOISE_LIBRARY, where to find the OpenImageDenoise library. + +#============================================================================= +# Copyright 2019 Blender Foundation. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= + +# If OPENIMAGEDENOISE_ROOT_DIR was defined in the environment, use it. +IF(NOT OPENIMAGEDENOISE_ROOT_DIR AND NOT $ENV{OPENIMAGEDENOISE_ROOT_DIR} STREQUAL "") + SET(OPENIMAGEDENOISE_ROOT_DIR $ENV{OPENIMAGEDENOISE_ROOT_DIR}) +ENDIF() + +SET(_openimagedenoise_SEARCH_DIRS + ${OPENIMAGEDENOISE_ROOT_DIR} + /usr/local + /sw # Fink + /opt/local # DarwinPorts + /opt/lib/openimagedenoise +) + +FIND_PATH(OPENIMAGEDENOISE_INCLUDE_DIR + NAMES + OpenImageDenoise/oidn.h + HINTS + ${_openimagedenoise_SEARCH_DIRS} + PATH_SUFFIXES + include +) + +SET(_openimagedenoise_FIND_COMPONENTS + OpenImageDenoise + common + mkldnn +) + +SET(_openimagedenoise_LIBRARIES) +FOREACH(COMPONENT ${_openimagedenoise_FIND_COMPONENTS}) + STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT) + + FIND_LIBRARY(OPENIMAGEDENOISE_${UPPERCOMPONENT}_LIBRARY + NAMES + ${COMPONENT} + HINTS + ${_openimagedenoise_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + LIST(APPEND _openimagedenoise_LIBRARIES "${OPENIMAGEDENOISE_${UPPERCOMPONENT}_LIBRARY}") +ENDFOREACH() + +FIND_LIBRARY(OPENIMAGEDENOISE_LIBRARY + NAMES + OpenImageDenoise + HINTS + ${_openimagedenoise_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + +# handle the QUIETLY and REQUIRED arguments and set OPENIMAGEDENOISE_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(OPENIMAGEDENOISE DEFAULT_MSG + OPENIMAGEDENOISE_LIBRARY OPENIMAGEDENOISE_INCLUDE_DIR) + +IF(OPENIMAGEDENOISE_FOUND) + SET(OPENIMAGEDENOISE_LIBRARIES ${_openimagedenoise_LIBRARIES}) + SET(OPENIMAGEDENOISE_INCLUDE_DIRS ${OPENIMAGEDENOISE_INCLUDE_DIR}) +ELSE() + SET(OPENIMAGEDENOISE_FOUND FALSE) +ENDIF() + +MARK_AS_ADVANCED( + OPENIMAGEDENOISE_INCLUDE_DIR +) + +FOREACH(COMPONENT ${_openimagedenoise_FIND_COMPONENTS}) + STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT) + MARK_AS_ADVANCED(OPENIMAGEDENOISE_${UPPERCOMPONENT}_LIBRARY) +ENDFOREACH() + +UNSET(_openimagedenoise_SEARCH_DIRS) +UNSET(_openimagedenoise_FIND_COMPONENTS) +UNSET(_openimagedenoise_LIBRARIES) diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake index 6786cb5ce37..75c5e0f34c1 100644 --- a/build_files/cmake/config/blender_full.cmake +++ b/build_files/cmake/config/blender_full.cmake @@ -40,6 +40,7 @@ set(WITH_AUDASPACE ON CACHE BOOL "" FORCE) set(WITH_OPENAL ON CACHE BOOL "" FORCE) set(WITH_OPENCOLLADA ON CACHE BOOL "" FORCE) set(WITH_OPENCOLORIO ON CACHE BOOL "" FORCE) +set(WITH_OPENIMAGEDENOISE ON CACHE BOOL "" FORCE) set(WITH_OPENMP ON CACHE BOOL "" FORCE) set(WITH_OPENSUBDIV ON CACHE BOOL "" FORCE) set(WITH_OPENVDB ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index b85176d37f3..6596d1db674 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -45,6 +45,7 @@ set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE) set(WITH_OPENAL OFF CACHE BOOL "" FORCE) set(WITH_OPENCOLLADA OFF CACHE BOOL "" FORCE) set(WITH_OPENCOLORIO OFF CACHE BOOL "" FORCE) +set(WITH_OPENIMAGEDENOISE OFF CACHE BOOL "" FORCE) set(WITH_OPENIMAGEIO OFF CACHE BOOL "" FORCE) set(WITH_OPENMP OFF CACHE BOOL "" FORCE) set(WITH_OPENSUBDIV OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index 24032b6aed6..08218a5e57c 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -41,6 +41,7 @@ set(WITH_AUDASPACE ON CACHE BOOL "" FORCE) set(WITH_OPENAL ON CACHE BOOL "" FORCE) set(WITH_OPENCOLLADA ON CACHE BOOL "" FORCE) set(WITH_OPENCOLORIO ON CACHE BOOL "" FORCE) +set(WITH_OPENIMAGEDENOISE ON CACHE BOOL "" FORCE) set(WITH_OPENMP ON CACHE BOOL "" FORCE) set(WITH_OPENSUBDIV ON CACHE BOOL "" FORCE) set(WITH_OPENVDB ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index c2f608de921..f6756cb514c 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -352,6 +352,9 @@ function(SETUP_LIBDIRS) if(WITH_OPENIMAGEIO) link_directories(${OPENIMAGEIO_LIBPATH}) endif() + if(WITH_OPENIMAGEDENOISE) + link_directories(${OPENIMAGEDENOISE_LIBPATH}) + endif() if(WITH_OPENCOLORIO) link_directories(${OPENCOLORIO_LIBPATH}) endif() @@ -462,6 +465,9 @@ function(setup_liblinks if(WITH_OPENIMAGEIO) target_link_libraries(${target} ${OPENIMAGEIO_LIBRARIES}) endif() + if(WITH_OPENIMAGEDENOISE) + target_link_libraries(${target} ${OPENIMAGEDENOISE_LIBRARIES} ${TBB_LIBRARIES}) + endif() if(WITH_OPENCOLORIO) target_link_libraries(${target} ${OPENCOLORIO_LIBRARIES}) endif() diff --git a/build_files/cmake/packaging.cmake b/build_files/cmake/packaging.cmake index 06a97fc9abb..5ace42646c5 100644 --- a/build_files/cmake/packaging.cmake +++ b/build_files/cmake/packaging.cmake @@ -80,22 +80,28 @@ if(APPLE) endif() if(WIN32) - set(CPACK_PACKAGE_INSTALL_DIRECTORY "Blender Foundation/Blender") - set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "Blender Foundation/Blender") + set(CPACK_PACKAGE_INSTALL_DIRECTORY "Blender Foundation/Blender ${MAJOR_VERSION}.${MINOR_VERSION}") + set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "Blender Foundation/Blender ${MAJOR_VERSION}.${MINOR_VERSION}") set(CPACK_NSIS_MUI_ICON ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.ico) set(CPACK_NSIS_COMPRESSOR "/SOLID lzma") set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/release/text/GPL3-license.txt) set(CPACK_WIX_PRODUCT_ICON ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.ico) - set(CPACK_WIX_UPGRADE_GUID "B767E4FD-7DE7-4094-B051-3AE62E13A17A") + + set(BLENDER_NAMESPACE_GUID "507F933F-5898-404A-9A05-18282FD491A6") + + string(UUID CPACK_WIX_UPGRADE_GUID + NAMESPACE ${BLENDER_NAMESPACE_GUID} + NAME ${CPACK_PACKAGE_INSTALL_DIRECTORY} + TYPE SHA1 UPPER + ) set(CPACK_WIX_TEMPLATE ${LIBDIR}/package/installer_wix/WIX.template) set(CPACK_WIX_UI_BANNER ${LIBDIR}/package/installer_wix/WIX_UI_BANNER.bmp) set(CPACK_WIX_UI_DIALOG ${LIBDIR}/package/installer_wix/WIX_UI_DIALOG.bmp) - #force lzma instead of deflate - set(CPACK_WIX_LIGHT_EXTRA_FLAGS -dcl:high) + set(CPACK_WIX_LIGHT_EXTRA_FLAGS -dcl:medium) endif() set(CPACK_PACKAGE_EXECUTABLES "blender" "blender") diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index 882f41b3c0d..e79359e9f3b 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -382,6 +382,19 @@ if(WITH_CYCLES_EMBREE) set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -Xlinker -stack_size -Xlinker 0x100000") endif() +if(WITH_OPENIMAGEDENOISE) + find_package(OpenImageDenoise) + find_package(TBB) + + if(NOT OPENIMAGEDENOISE_FOUND) + set(WITH_OPENIMAGEDENOISE OFF) + message(STATUS "OpenImageDenoise not found") + elseif(NOT TBB_FOUND) + set(WITH_OPENIMAGEDENOISE OFF) + message(STATUS "TBB not found") + endif() +endif() + # CMake FindOpenMP doesn't know about AppleClang before 3.12, so provide custom flags. if(WITH_OPENMP) if(CMAKE_C_COMPILER_ID MATCHES "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0") diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 43d13a97a3e..1b3f9cf3fad 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -368,6 +368,15 @@ if(WITH_CYCLES_EMBREE) find_package(Embree 3.2.4 REQUIRED) endif() +if(WITH_OPENIMAGEDENOISE) + find_package_wrapper(OpenImageDenoise) + + if(NOT OPENIMAGEDENOISE_FOUND) + set(WITH_OPENIMAGEDENOISE OFF) + message(STATUS "OpenImageDenoise not found") + endif() +endif() + if(WITH_LLVM) if(EXISTS ${LIBDIR}) set(LLVM_STATIC ON) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 8286f5d80a9..42ac285f88d 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -131,8 +131,8 @@ if(MSVC_CLANG) # Clangs version of cl doesn't support all flags set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARN_FLAGS} /nologo /J /Gd /EHsc -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference") else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /nologo /J /Gd /MP /EHsc") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd /MP") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /nologo /J /Gd /MP /EHsc /bigobj") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd /MP /bigobj") endif() set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") @@ -347,15 +347,13 @@ if(WITH_PYTHON) set(PYTHON_VERSION 3.7) # CACHE STRING) string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION}) - # Use shared libs for vc2008 and vc2010 until we actually have vc2010 libs - set(PYTHON_LIBRARY ${LIBDIR}/python/lib/python${_PYTHON_VERSION_NO_DOTS}.lib) - set(PYTHON_LIBRARY_DEBUG ${LIBDIR}/python/lib/python${_PYTHON_VERSION_NO_DOTS}_d.lib) + set(PYTHON_LIBRARY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.lib) + set(PYTHON_LIBRARY_DEBUG ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}_d.lib) + set(PYTHON_INCLUDE_DIR ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/include) + set(PYTHON_NUMPY_INCLUDE_DIRS ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/lib/site-packages/numpy/core/include) + set(NUMPY_FOUND On) unset(_PYTHON_VERSION_NO_DOTS) - - # Shared includes for both vc2008 and vc2010 - set(PYTHON_INCLUDE_DIR ${LIBDIR}/python/include/python${PYTHON_VERSION}) - # uncached vars set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") set(PYTHON_LIBRARIES debug "${PYTHON_LIBRARY_DEBUG}" optimized "${PYTHON_LIBRARY}" ) @@ -420,7 +418,7 @@ endif() if(WITH_OPENIMAGEIO) windows_find_package(OpenImageIO) - set(OPENIMAGEIO ${LIBDIR}/openimageio) + set(OPENIMAGEIO ${LIBDIR}/OpenImageIO) set(OPENIMAGEIO_LIBPATH ${OPENIMAGEIO}/lib) set(OPENIMAGEIO_INCLUDE_DIRS ${OPENIMAGEIO}/include) set(OIIO_OPTIMIZED optimized ${OPENIMAGEIO_LIBPATH}/OpenImageIO.lib optimized ${OPENIMAGEIO_LIBPATH}/OpenImageIO_Util.lib) @@ -461,14 +459,14 @@ if(WITH_LLVM) endif() if(WITH_OPENCOLORIO) - set(OPENCOLORIO ${LIBDIR}/opencolorio) + set(OPENCOLORIO ${LIBDIR}/OpenColorIO) set(OPENCOLORIO_INCLUDE_DIRS ${OPENCOLORIO}/include) - set(OPENCOLORIO_LIBPATH ${LIBDIR}/opencolorio/lib) + set(OPENCOLORIO_LIBPATH ${OPENCOLORIO}/lib) set(OPENCOLORIO_LIBRARIES optimized ${OPENCOLORIO_LIBPATH}/OpenColorIO.lib optimized ${OPENCOLORIO_LIBPATH}/tinyxml.lib optimized ${OPENCOLORIO_LIBPATH}/libyaml-cpp.lib - debug ${OPENCOLORIO_LIBPATH}/OpenColorIO_d.lib + debug ${OPENCOLORIO_LIBPATH}/OpencolorIO_d.lib debug ${OPENCOLORIO_LIBPATH}/tinyxml_d.lib debug ${OPENCOLORIO_LIBPATH}/libyaml-cpp_d.lib ) @@ -479,19 +477,32 @@ if(WITH_OPENVDB) set(BLOSC_LIBRARIES optimized ${LIBDIR}/blosc/lib/libblosc.lib debug ${LIBDIR}/blosc/lib/libblosc_d.lib) set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib) set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include) - set(OPENVDB ${LIBDIR}/openvdb) - set(OPENVDB_LIBPATH ${LIBDIR}/openvdb/lib) + set(OPENVDB ${LIBDIR}/openVDB) + set(OPENVDB_LIBPATH ${OPENVDB}/lib) set(OPENVDB_INCLUDE_DIRS ${OPENVDB}/include ${TBB_INCLUDE_DIR}) set(OPENVDB_LIBRARIES optimized ${OPENVDB_LIBPATH}/openvdb.lib debug ${OPENVDB_LIBPATH}/openvdb_d.lib ${TBB_LIBRARIES} ${BLOSC_LIBRARIES}) set(OPENVDB_DEFINITIONS -DNOMINMAX) endif() +if(WITH_OPENIMAGEDENOISE) + set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib) + set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include) + set(OPENIMAGEDENOISE ${LIBDIR}/OpenImageDenoise) + set(OPENIMAGEDENOISE_LIBPATH ${LIBDIR}/OpenImageDenoise/lib) + set(OPENIMAGEDENOISE_INCLUDE_DIRS ${OPENIMAGEDENOISE}/include ${TBB_INCLUDE_DIR}) + set(OPENIMAGEDENOISE_LIBRARIES + optimized ${OPENIMAGEDENOISE_LIBPATH}/OpenImageDenoise.lib ${OPENIMAGEDENOISE_LIBPATH}/common.lib ${OPENIMAGEDENOISE_LIBPATH}/mkldnn.lib + debug ${OPENIMAGEDENOISE_LIBPATH}/OpenImageDenoise_d.lib ${OPENIMAGEDENOISE_LIBPATH}/common_d.lib ${OPENIMAGEDENOISE_LIBPATH}/mkldnn_d.lib + ${TBB_LIBRARIES}) + set(OPENIMAGEDENOISE_DEFINITIONS) +endif() + if(WITH_ALEMBIC) set(ALEMBIC ${LIBDIR}/alembic) set(ALEMBIC_INCLUDE_DIR ${ALEMBIC}/include) set(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR}) set(ALEMBIC_LIBPATH ${ALEMBIC}/lib) - set(ALEMBIC_LIBRARIES optimized ${ALEMBIC}/lib/alembic.lib debug ${ALEMBIC}/lib/alembic_d.lib) + set(ALEMBIC_LIBRARIES optimized ${ALEMBIC}/lib/Alembic.lib debug ${ALEMBIC}/lib/Alembic_d.lib) set(ALEMBIC_FOUND 1) endif() diff --git a/build_files/windows/format.cmd b/build_files/windows/format.cmd index c291dc581ae..f036257e220 100644 --- a/build_files/windows/format.cmd +++ b/build_files/windows/format.cmd @@ -9,8 +9,17 @@ exit /b 1 :detect_done echo found clang-format in %CF_PATH% -REM TODO(sergey): Switch to Python from libraries when available. -set PYTHON="python.exe" +if EXIST %BLENDER_DIR%\..\lib\win64_vc14\python\37\bin\python.exe ( + set PYTHON=%BLENDER_DIR%\..\lib\win64_vc14\python\37\bin\python.exe + goto detect_python_done +) + +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/extern/bullet2/patches/blender.patch b/extern/bullet2/patches/blender.patch index cb3bf2ba38a..b4b24fdc220 100644 --- a/extern/bullet2/patches/blender.patch +++ b/extern/bullet2/patches/blender.patch @@ -1,3 +1,16 @@ +diff --git a/extern/bullet2/src/LinearMath/btVector3.h b/extern/bullet2/src/LinearMath/btVector3.h +index 839b19c..3058195 100644 +--- a/extern/bullet2/src/LinearMath/btVector3.h ++++ b/extern/bullet2/src/LinearMath/btVector3.h +@@ -39,7 +39,7 @@ subject to the following restrictions: + #endif + + +-#define BT_SHUFFLE(x,y,z,w) ((w)<<6 | (z)<<4 | (y)<<2 | (x)) ++#define BT_SHUFFLE(x,y,z,w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff) + //#define bt_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) ) + #define bt_pshufd_ps( _a, _mask ) _mm_shuffle_ps((_a), (_a), (_mask) ) + #define bt_splat3_ps( _a, _i ) bt_pshufd_ps((_a), BT_SHUFFLE(_i,_i,_i, 3) ) diff --git a/extern/bullet2/src/LinearMath/btScalar.h b/extern/bullet2/src/LinearMath/btScalar.h --- a/extern/bullet2/src/LinearMath/btScalar.h +++ b/extern/bullet2/src/LinearMath/btScalar.h diff --git a/extern/bullet2/src/LinearMath/btVector3.h b/extern/bullet2/src/LinearMath/btVector3.h index 839b19c1449..30581953d2e 100644 --- a/extern/bullet2/src/LinearMath/btVector3.h +++ b/extern/bullet2/src/LinearMath/btVector3.h @@ -39,7 +39,7 @@ subject to the following restrictions: #endif -#define BT_SHUFFLE(x,y,z,w) ((w)<<6 | (z)<<4 | (y)<<2 | (x)) +#define BT_SHUFFLE(x,y,z,w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff) //#define bt_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) ) #define bt_pshufd_ps( _a, _mask ) _mm_shuffle_ps((_a), (_a), (_mask) ) #define bt_splat3_ps( _a, _i ) bt_pshufd_ps((_a), BT_SHUFFLE(_i,_i,_i, 3) ) diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index c672bc9f3e2..551866f7fce 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -1002,6 +1002,9 @@ Mesh *BlenderSync::sync_mesh(BL::Depsgraph &b_depsgraph, oldcurve_keys.steal_data(mesh->curve_keys); oldcurve_radius.steal_data(mesh->curve_radius); + /* ensure bvh rebuild (instead of refit) if has_voxel_attributes() changed */ + bool oldhas_voxel_attributes = mesh->has_voxel_attributes(); + mesh->clear(); mesh->used_shaders = used_shaders; mesh->name = ustring(b_ob_data.name().c_str()); @@ -1050,7 +1053,8 @@ Mesh *BlenderSync::sync_mesh(BL::Depsgraph &b_depsgraph, /* tag update */ bool rebuild = (oldtriangles != mesh->triangles) || (oldsubd_faces != mesh->subd_faces) || (oldsubd_face_corners != mesh->subd_face_corners) || - (oldcurve_keys != mesh->curve_keys) || (oldcurve_radius != mesh->curve_radius); + (oldcurve_keys != mesh->curve_keys) || (oldcurve_radius != mesh->curve_radius) || + (oldhas_voxel_attributes != mesh->has_voxel_attributes()); mesh->tag_update(scene, rebuild); diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index f952b3025f0..626a1dad7db 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -315,12 +315,21 @@ static ShaderNode *add_node(Scene *scene, else if (b_node.is_a(&RNA_ShaderNodeRGBToBW)) { node = new RGBToBWNode(); } + else if (b_node.is_a(&RNA_ShaderNodeMapRange)) { + BL::ShaderNodeMapRange b_map_range_node(b_node); + MapRangeNode *map_range_node = new MapRangeNode(); + map_range_node->clamp = b_map_range_node.clamp(); + node = map_range_node; + } + else if (b_node.is_a(&RNA_ShaderNodeClamp)) { + node = new ClampNode(); + } else if (b_node.is_a(&RNA_ShaderNodeMath)) { BL::ShaderNodeMath b_math_node(b_node); - MathNode *math = new MathNode(); - math->type = (NodeMath)b_math_node.operation(); - math->use_clamp = b_math_node.use_clamp(); - node = math; + MathNode *math_node = new MathNode(); + math_node->type = (NodeMathType)b_math_node.operation(); + math_node->use_clamp = b_math_node.use_clamp(); + node = math_node; } else if (b_node.is_a(&RNA_ShaderNodeVectorMath)) { BL::ShaderNodeVectorMath b_vector_math_node(b_node); diff --git a/intern/cycles/bvh/bvh4.cpp b/intern/cycles/bvh/bvh4.cpp index 850bdf5b8b4..b6df9024ffa 100644 --- a/intern/cycles/bvh/bvh4.cpp +++ b/intern/cycles/bvh/bvh4.cpp @@ -43,8 +43,7 @@ BVHNode *bvh_node_merge_children_recursively(const BVHNode *node) if (node->is_leaf()) { return new LeafNode(*reinterpret_cast<const LeafNode *>(node)); } - /* Collect nodes of one layer deeper, allowing us to have more childrem in - * an inner layer. */ + /* Collect nodes of one layer deeper, allowing us to have more children in an inner layer. */ assert(node->num_children() <= 2); const BVHNode *children[4]; const BVHNode *child0 = node->get_child(0); diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index cd284c06259..ab05d6ee1e9 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -179,6 +179,7 @@ set(SRC_SVM_HEADERS svm/svm_blackbody.h svm/svm_bump.h svm/svm_camera.h + svm/svm_clamp.h svm/svm_closure.h svm/svm_convert.h svm/svm_checker.h @@ -198,6 +199,7 @@ set(SRC_SVM_HEADERS svm/svm_invert.h svm/svm_light_path.h svm/svm_magic.h + svm/svm_map_range.h svm/svm_mapping.h svm/svm_math.h svm/svm_math_util.h @@ -486,15 +488,17 @@ endif() include_directories(${INC}) include_directories(SYSTEM ${INC_SYS}) -if(CMAKE_COMPILER_IS_GNUCC AND (NOT WITH_CYCLES_KERNEL_ASAN)) - # GCC hangs compiling the big kernel files with asan and release, so disable by default. - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-sanitize=all") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-sanitize=vptr") -elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") - # With OSL, Cycles disables rtti in some modules, wich then breaks at linking - # when trying to use vptr sanitizer (included into 'undefined' general option). - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-sanitize=vptr") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-sanitize=vptr") +if(WITH_COMPILER_ASAN) + if(CMAKE_COMPILER_IS_GNUCC AND (NOT WITH_CYCLES_KERNEL_ASAN)) + # GCC hangs compiling the big kernel files with asan and release, so disable by default. + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-sanitize=all") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-sanitize=vptr") + elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") + # With OSL, Cycles disables rtti in some modules, wich then breaks at linking + # when trying to use vptr sanitizer (included into 'undefined' general option). + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-sanitize=vptr") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-sanitize=vptr") + endif() endif() set_source_files_properties(kernels/cpu/kernel.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index 0da4d6bd060..4d88a822821 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -231,7 +231,7 @@ ccl_device float bssrdf_burley_eval(const float d, float r) * NOTES: * - Surface albedo is already included into sc->weight, no need to * multiply by this term here. - * - This is normalized diffuse model, so the equation is mutliplied + * - This is normalized diffuse model, so the equation is multiplied * by 2*pi, which also matches cdf(). */ float exp_r_3_d = expf(-r / (3.0f * d)); diff --git a/intern/cycles/kernel/filter/filter_transform.h b/intern/cycles/kernel/filter/filter_transform.h index 585c4b33787..880a661214e 100644 --- a/intern/cycles/kernel/filter/filter_transform.h +++ b/intern/cycles/kernel/filter/filter_transform.h @@ -70,9 +70,9 @@ ccl_device void kernel_filter_construct_transform(const float *ccl_restrict buff filter_calculate_scale(feature_scale, use_time); /* === Generate the feature transformation. === - * This transformation maps the num_features-dimentional feature space to a reduced feature - * (r-feature) space which generally has fewer dimensions. This mainly helps to prevent - * overfitting. */ + * This transformation maps the num_features-dimensional feature space to a reduced feature + * (r-feature) space which generally has fewer dimensions. + * This mainly helps to prevent over-fitting. */ float feature_matrix[DENOISE_FEATURES * DENOISE_FEATURES]; math_matrix_zero(feature_matrix, num_features); FOR_PIXEL_WINDOW @@ -85,7 +85,7 @@ ccl_device void kernel_filter_construct_transform(const float *ccl_restrict buff math_matrix_jacobi_eigendecomposition(feature_matrix, transform, num_features, 1); *rank = 0; - /* Prevent overfitting when a small window is used. */ + /* Prevent over-fitting when a small window is used. */ int max_rank = min(num_features, num_pixels / 3); if (pca_threshold < 0.0f) { float threshold_energy = 0.0f; diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 4963e012e15..ed1a60a664f 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -686,8 +686,7 @@ ccl_device_inline const ShaderClosure *shader_bsdf_pick(ShaderData *sd, float *r if (r < next_sum) { sampled = i; - /* Rescale to reuse for direction sample, to better - * preserve stratification. */ + /* Rescale to reuse for direction sample, to better preserve stratification. */ *randu = (r - partial_sum) / sc->sample_weight; break; } @@ -743,8 +742,7 @@ ccl_device_inline const ShaderClosure *shader_bssrdf_pick(ShaderData *sd, *throughput *= (sum_bsdf + sum_bssrdf) / sum_bssrdf; sampled = i; - /* Rescale to reuse for direction sample, to better - * preserve stratifaction. */ + /* Rescale to reuse for direction sample, to better preserve stratification. */ *randu = (r - partial_sum) / sc->sample_weight; break; } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index a1d950bbc70..f3f321e77dc 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1523,7 +1523,7 @@ static_assert_align(KernelShader, 16); * Queue 1 - Active rays * Queue 2 - Background queue * Queue 3 - Shadow ray cast kernel - AO - * Queeu 4 - Shadow ray cast kernel - direct lighting + * Queue 4 - Shadow ray cast kernel - direct lighting */ /* Queue names */ diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h index 4289e2bbb85..8f311baf010 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h +++ b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h @@ -19,6 +19,10 @@ CCL_NAMESPACE_BEGIN +/* Make template functions private so symbols don't conflict between kernels with different + * instruction sets. */ +namespace { + template<typename T> struct TextureInterpolator { #define SET_CUBIC_SPLINE_WEIGHTS(u, t) \ { \ @@ -523,6 +527,8 @@ ccl_device float4 kernel_tex_image_interp_3d( } } +} /* Namespace. */ + CCL_NAMESPACE_END #endif // __KERNEL_CPU_IMAGE_H__ diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt index b42b9b2fe64..63cef6e841b 100644 --- a/intern/cycles/kernel/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/shaders/CMakeLists.txt @@ -13,6 +13,7 @@ set(SRC_OSL node_bump.osl node_camera.osl node_checker_texture.osl + node_clamp.osl node_combine_rgb.osl node_combine_hsv.osl node_combine_xyz.osl @@ -46,6 +47,7 @@ set(SRC_OSL node_light_falloff.osl node_light_path.osl node_magic_texture.osl + node_map_range.osl node_mapping.osl node_math.osl node_mix.osl diff --git a/intern/cycles/kernel/shaders/node_clamp.osl b/intern/cycles/kernel/shaders/node_clamp.osl new file mode 100644 index 00000000000..87dc1ccdb12 --- /dev/null +++ b/intern/cycles/kernel/shaders/node_clamp.osl @@ -0,0 +1,22 @@ +/* + * Copyright 2011-2013 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 "stdosl.h" + +shader node_clamp(float Value = 1.0, float Min = 0.0, float Max = 1.0, output float Result = 0.0) +{ + Result = clamp(Value, Min, Max); +} diff --git a/intern/cycles/kernel/shaders/node_map_range.osl b/intern/cycles/kernel/shaders/node_map_range.osl new file mode 100644 index 00000000000..8a28edf5f35 --- /dev/null +++ b/intern/cycles/kernel/shaders/node_map_range.osl @@ -0,0 +1,29 @@ +/* + * Copyright 2011-2013 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 "stdosl.h" + +shader node_map_range(float Value = 1.0, + float FromMin = 0.0, + float FromMax = 1.0, + float ToMin = 0.0, + float ToMax = 1.0, + output float Result = 0.0) +{ + if (FromMax != FromMin) { + Result = ToMin + ((Value - FromMin) / (FromMax - FromMin)) * (ToMax - ToMin); + } +} diff --git a/intern/cycles/kernel/shaders/node_math.osl b/intern/cycles/kernel/shaders/node_math.osl index 8830339e05f..fb59c783770 100644 --- a/intern/cycles/kernel/shaders/node_math.osl +++ b/intern/cycles/kernel/shaders/node_math.osl @@ -17,57 +17,31 @@ #include "stdosl.h" float safe_divide(float a, float b) -{ - float result; - - if (b == 0.0) - result = 0.0; - else - result = a / b; - - return result; +{ + return (b != 0.0) ? a / b : 0.0; } float safe_modulo(float a, float b) { - float result; - - if (b == 0.0) - result = 0.0; - else - result = fmod(a, b); - - return result; + return (b != 0.0) ? fmod(a, b) : 0.0; } float safe_sqrt(float a) { - float result; - - if (a > 0.0) - result = sqrt(a); - else - result = 0.0; - - return result; + return (a > 0.0) ? sqrt(a) : 0.0; } float safe_log(float a, float b) { - if (a < 0.0 || b < 0.0) - return 0.0; - - return log(a) / log(b); + return (a > 0.0 && b > 0.0) ? log(a) / log(b) : 0.0; } +/* OSL asin, acos, and pow functions are safe by default. */ shader node_math(string type = "add", - int use_clamp = 0, - float Value1 = 0.0, - float Value2 = 0.0, + float Value1 = 0.5, + float Value2 = 0.5, output float Value = 0.0) { - /* OSL asin, acos, pow check for values that could give rise to nan */ - if (type == "add") Value = Value1 + Value2; else if (type == "subtract") @@ -76,47 +50,46 @@ shader node_math(string type = "add", Value = Value1 * Value2; else if (type == "divide") Value = safe_divide(Value1, Value2); - else if (type == "sine") - Value = sin(Value1); - else if (type == "cosine") - Value = cos(Value1); - else if (type == "tangent") - Value = tan(Value1); - else if (type == "arcsine") - Value = asin(Value1); - else if (type == "arccosine") - Value = acos(Value1); - else if (type == "arctangent") - Value = atan(Value1); else if (type == "power") Value = pow(Value1, Value2); else if (type == "logarithm") Value = safe_log(Value1, Value2); + else if (type == "sqrt") + Value = safe_sqrt(Value1); + else if (type == "absolute") + Value = fabs(Value1); else if (type == "minimum") Value = min(Value1, Value2); else if (type == "maximum") Value = max(Value1, Value2); - else if (type == "round") - Value = floor(Value1 + 0.5); else if (type == "less_than") Value = Value1 < Value2; else if (type == "greater_than") Value = Value1 > Value2; - else if (type == "modulo") - Value = safe_modulo(Value1, Value2); - else if (type == "absolute") - Value = fabs(Value1); - else if (type == "arctan2") - Value = atan2(Value1, Value2); + else if (type == "round") + Value = floor(Value1 + 0.5); else if (type == "floor") Value = floor(Value1); else if (type == "ceil") Value = ceil(Value1); - else if (type == "fract") + else if (type == "fraction") Value = Value1 - floor(Value1); - else if (type == "sqrt") - Value = safe_sqrt(Value1); - - if (use_clamp) - Value = clamp(Value, 0.0, 1.0); + else if (type == "modulo") + Value = safe_modulo(Value1, Value2); + else if (type == "sine") + Value = sin(Value1); + else if (type == "cosine") + Value = cos(Value1); + else if (type == "tangent") + Value = tan(Value1); + else if (type == "arcsine") + Value = asin(Value1); + else if (type == "arccosine") + Value = acos(Value1); + else if (type == "arctangent") + Value = atan(Value1); + else if (type == "arctan2") + Value = atan2(Value1, Value2); + else + warning("%s", "Unknown math operator!"); } diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 4a386afa5de..0d731d62e94 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -192,6 +192,8 @@ CCL_NAMESPACE_END #include "kernel/svm/svm_vector_transform.h" #include "kernel/svm/svm_voxel.h" #include "kernel/svm/svm_bump.h" +#include "kernel/svm/svm_map_range.h" +#include "kernel/svm/svm_clamp.h" #ifdef __SHADER_RAYTRACE__ # include "kernel/svm/svm_ao.h" @@ -486,6 +488,12 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, case NODE_BLACKBODY: svm_node_blackbody(kg, sd, stack, node.y, node.z); break; + case NODE_MAP_RANGE: + svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, &offset); + break; + case NODE_CLAMP: + svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, &offset); + break; # endif /* __EXTRA_NODES__ */ # if NODES_FEATURE(NODE_FEATURE_VOLUME) case NODE_TEX_VOXEL: diff --git a/intern/cycles/kernel/svm/svm_clamp.h b/intern/cycles/kernel/svm/svm_clamp.h new file mode 100644 index 00000000000..5ff4a599028 --- /dev/null +++ b/intern/cycles/kernel/svm/svm_clamp.h @@ -0,0 +1,41 @@ +/* + * Copyright 2011-2013 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. + */ + +CCL_NAMESPACE_BEGIN + +/* Clamp Node */ + +ccl_device void svm_node_clamp(KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint value_stack_offset, + uint parameters_stack_offsets, + uint result_stack_offset, + int *offset) +{ + uint min_stack_offset, max_stack_offset; + decode_node_uchar4(parameters_stack_offsets, &min_stack_offset, &max_stack_offset, NULL, NULL); + + uint4 defaults = read_node(kg, offset); + + float value = stack_load_float(stack, value_stack_offset); + float min = stack_load_float_default(stack, min_stack_offset, defaults.x); + float max = stack_load_float_default(stack, max_stack_offset, defaults.y); + + stack_store_float(stack, result_stack_offset, clamp(value, min, max)); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_map_range.h b/intern/cycles/kernel/svm/svm_map_range.h new file mode 100644 index 00000000000..5d542e959d1 --- /dev/null +++ b/intern/cycles/kernel/svm/svm_map_range.h @@ -0,0 +1,54 @@ +/* + * Copyright 2011-2013 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. + */ + +CCL_NAMESPACE_BEGIN + +/* Map Range Node */ + +ccl_device void svm_node_map_range(KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint value_stack_offset, + uint parameters_stack_offsets, + uint result_stack_offset, + int *offset) +{ + uint from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset; + decode_node_uchar4(parameters_stack_offsets, + &from_min_stack_offset, + &from_max_stack_offset, + &to_min_stack_offset, + &to_max_stack_offset); + + uint4 defaults = read_node(kg, offset); + + float value = stack_load_float(stack, value_stack_offset); + float from_min = stack_load_float_default(stack, from_min_stack_offset, defaults.x); + float from_max = stack_load_float_default(stack, from_max_stack_offset, defaults.y); + float to_min = stack_load_float_default(stack, to_min_stack_offset, defaults.z); + float to_max = stack_load_float_default(stack, to_max_stack_offset, defaults.w); + + float result; + if (from_max != from_min) { + result = to_min + ((value - from_min) / (from_max - from_min)) * (to_max - to_min); + } + else { + result = 0.0f; + } + stack_store_float(stack, result_stack_offset, result); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/svm_math.h index 5920913825b..402290d7218 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/svm_math.h @@ -16,24 +16,22 @@ CCL_NAMESPACE_BEGIN -/* Nodes */ - ccl_device void svm_node_math(KernelGlobals *kg, ShaderData *sd, float *stack, - uint itype, - uint f1_offset, - uint f2_offset, + uint type, + uint inputs_stack_offsets, + uint result_stack_offset, int *offset) { - NodeMath type = (NodeMath)itype; - float f1 = stack_load_float(stack, f1_offset); - float f2 = stack_load_float(stack, f2_offset); - float f = svm_math(type, f1, f2); + uint a_stack_offset, b_stack_offset; + decode_node_uchar4(inputs_stack_offsets, &a_stack_offset, &b_stack_offset, NULL, NULL); - uint4 node1 = read_node(kg, offset); + float a = stack_load_float(stack, a_stack_offset); + float b = stack_load_float(stack, b_stack_offset); + float result = svm_math((NodeMathType)type, a, b); - stack_store_float(stack, node1.y, f); + stack_store_float(stack, result_stack_offset, result); } ccl_device void svm_node_vector_math(KernelGlobals *kg, diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h index e3544515f1b..d8804226487 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/svm_math_util.h @@ -51,64 +51,60 @@ ccl_device void svm_vector_math( } } -ccl_device float svm_math(NodeMath type, float Fac1, float Fac2) +ccl_device float svm_math(NodeMathType type, float a, float b) { - float Fac; - - if (type == NODE_MATH_ADD) - Fac = Fac1 + Fac2; - else if (type == NODE_MATH_SUBTRACT) - Fac = Fac1 - Fac2; - else if (type == NODE_MATH_MULTIPLY) - Fac = Fac1 * Fac2; - else if (type == NODE_MATH_DIVIDE) - Fac = safe_divide(Fac1, Fac2); - else if (type == NODE_MATH_SINE) - Fac = sinf(Fac1); - else if (type == NODE_MATH_COSINE) - Fac = cosf(Fac1); - else if (type == NODE_MATH_TANGENT) - Fac = tanf(Fac1); - else if (type == NODE_MATH_ARCSINE) - Fac = safe_asinf(Fac1); - else if (type == NODE_MATH_ARCCOSINE) - Fac = safe_acosf(Fac1); - else if (type == NODE_MATH_ARCTANGENT) - Fac = atanf(Fac1); - else if (type == NODE_MATH_POWER) - Fac = safe_powf(Fac1, Fac2); - else if (type == NODE_MATH_LOGARITHM) - Fac = safe_logf(Fac1, Fac2); - else if (type == NODE_MATH_MINIMUM) - Fac = fminf(Fac1, Fac2); - else if (type == NODE_MATH_MAXIMUM) - Fac = fmaxf(Fac1, Fac2); - else if (type == NODE_MATH_ROUND) - Fac = floorf(Fac1 + 0.5f); - else if (type == NODE_MATH_LESS_THAN) - Fac = Fac1 < Fac2; - else if (type == NODE_MATH_GREATER_THAN) - Fac = Fac1 > Fac2; - else if (type == NODE_MATH_MODULO) - Fac = safe_modulo(Fac1, Fac2); - else if (type == NODE_MATH_ABSOLUTE) - Fac = fabsf(Fac1); - else if (type == NODE_MATH_ARCTAN2) - Fac = atan2f(Fac1, Fac2); - else if (type == NODE_MATH_FLOOR) - Fac = floorf(Fac1); - else if (type == NODE_MATH_CEIL) - Fac = ceilf(Fac1); - else if (type == NODE_MATH_FRACT) - Fac = Fac1 - floorf(Fac1); - else if (type == NODE_MATH_SQRT) - Fac = safe_sqrtf(Fac1); - else if (type == NODE_MATH_CLAMP) - Fac = saturate(Fac1); - else - Fac = 0.0f; - - return Fac; + switch (type) { + case NODE_MATH_ADD: + return a + b; + case NODE_MATH_SUBTRACT: + return a - b; + case NODE_MATH_MULTIPLY: + return a * b; + case NODE_MATH_DIVIDE: + return safe_divide(a, b); + case NODE_MATH_POWER: + return safe_powf(a, b); + case NODE_MATH_LOGARITHM: + return safe_logf(a, b); + case NODE_MATH_SQRT: + return safe_sqrtf(a); + case NODE_MATH_ABSOLUTE: + return fabsf(a); + case NODE_MATH_MINIMUM: + return fminf(a, b); + case NODE_MATH_MAXIMUM: + return fmaxf(a, b); + case NODE_MATH_LESS_THAN: + return a < b; + case NODE_MATH_GREATER_THAN: + return a > b; + case NODE_MATH_ROUND: + return floorf(a + 0.5f); + case NODE_MATH_FLOOR: + return floorf(a); + case NODE_MATH_CEIL: + return ceilf(a); + case NODE_MATH_FRACTION: + return a - floorf(a); + case NODE_MATH_MODULO: + return safe_modulo(a, b); + case NODE_MATH_SINE: + return sinf(a); + case NODE_MATH_COSINE: + return cosf(a); + case NODE_MATH_TANGENT: + return tanf(a); + case NODE_MATH_ARCSINE: + return safe_asinf(a); + case NODE_MATH_ARCCOSINE: + return safe_acosf(a); + case NODE_MATH_ARCTANGENT: + return atanf(a); + case NODE_MATH_ARCTAN2: + return atan2f(a, b); + default: + return 0.0f; + } } /* Calculate color in range 800..12000 using an approximation diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index ea92fd7ce59..6b0d10adc74 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -138,6 +138,8 @@ typedef enum ShaderNodeType { NODE_VECTOR_DISPLACEMENT, NODE_PRINCIPLED_VOLUME, NODE_IES, + NODE_MAP_RANGE, + NODE_CLAMP, } ShaderNodeType; typedef enum NodeAttributeType { @@ -242,7 +244,7 @@ typedef enum NodeMix { NODE_MIX_CLAMP /* used for the clamp UI option */ } NodeMix; -typedef enum NodeMath { +typedef enum NodeMathType { NODE_MATH_ADD, NODE_MATH_SUBTRACT, NODE_MATH_MULTIPLY, @@ -265,10 +267,9 @@ typedef enum NodeMath { NODE_MATH_ARCTAN2, NODE_MATH_FLOOR, NODE_MATH_CEIL, - NODE_MATH_FRACT, + NODE_MATH_FRACTION, NODE_MATH_SQRT, - NODE_MATH_CLAMP /* used for the clamp UI option */ -} NodeMath; +} NodeMathType; typedef enum NodeVectorMath { NODE_VECTOR_MATH_ADD, diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index 7591d9dda0c..38306a63c74 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -565,8 +565,7 @@ float3 Camera::transform_raster_to_world(float raster_x, float raster_y) BoundBox Camera::viewplane_bounds_get() { /* TODO(sergey): This is all rather stupid, but is there a way to perform - * checks we need in a more clear and smart fasion? - */ + * checks we need in a more clear and smart fashion? */ BoundBox bounds = BoundBox::empty; if (type == CAMERA_PANORAMA) { diff --git a/intern/cycles/render/constant_fold.cpp b/intern/cycles/render/constant_fold.cpp index e475ff60eef..d6fdc49434e 100644 --- a/intern/cycles/render/constant_fold.cpp +++ b/intern/cycles/render/constant_fold.cpp @@ -301,7 +301,7 @@ void ConstantFolder::fold_mix(NodeMix type, bool clamp) const } } -void ConstantFolder::fold_math(NodeMath type, bool clamp) const +void ConstantFolder::fold_math(NodeMathType type) const { ShaderInput *value1_in = node->input("Value1"); ShaderInput *value2_in = node->input("Value2"); @@ -310,25 +310,25 @@ void ConstantFolder::fold_math(NodeMath type, bool clamp) const case NODE_MATH_ADD: /* X + 0 == 0 + X == X */ if (is_zero(value1_in)) { - try_bypass_or_make_constant(value2_in, clamp); + try_bypass_or_make_constant(value2_in); } else if (is_zero(value2_in)) { - try_bypass_or_make_constant(value1_in, clamp); + try_bypass_or_make_constant(value1_in); } break; case NODE_MATH_SUBTRACT: /* X - 0 == X */ if (is_zero(value2_in)) { - try_bypass_or_make_constant(value1_in, clamp); + try_bypass_or_make_constant(value1_in); } break; case NODE_MATH_MULTIPLY: /* X * 1 == 1 * X == X */ if (is_one(value1_in)) { - try_bypass_or_make_constant(value2_in, clamp); + try_bypass_or_make_constant(value2_in); } else if (is_one(value2_in)) { - try_bypass_or_make_constant(value1_in, clamp); + try_bypass_or_make_constant(value1_in); } /* X * 0 == 0 * X == 0 */ else if (is_zero(value1_in) || is_zero(value2_in)) { @@ -338,7 +338,7 @@ void ConstantFolder::fold_math(NodeMath type, bool clamp) const case NODE_MATH_DIVIDE: /* X / 1 == X */ if (is_one(value2_in)) { - try_bypass_or_make_constant(value1_in, clamp); + try_bypass_or_make_constant(value1_in); } /* 0 / X == 0 */ else if (is_zero(value1_in)) { @@ -352,7 +352,7 @@ void ConstantFolder::fold_math(NodeMath type, bool clamp) const } /* X ^ 1 == X */ else if (is_one(value2_in)) { - try_bypass_or_make_constant(value1_in, clamp); + try_bypass_or_make_constant(value1_in); } default: break; diff --git a/intern/cycles/render/constant_fold.h b/intern/cycles/render/constant_fold.h index c14b94868dc..d223fd86197 100644 --- a/intern/cycles/render/constant_fold.h +++ b/intern/cycles/render/constant_fold.h @@ -64,7 +64,7 @@ class ConstantFolder { /* Specific nodes. */ void fold_mix(NodeMix type, bool clamp) const; - void fold_math(NodeMath type, bool clamp) const; + void fold_math(NodeMathType type) const; void fold_vector_math(NodeVectorMath type) const; }; diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 5c3f1c35bdc..8c7a21da561 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -944,7 +944,7 @@ void LightManager::tag_update(Scene * /*scene*/) need_update = true; } -int LightManager::add_ies_from_file(ustring filename) +int LightManager::add_ies_from_file(const string &filename) { string content; @@ -953,10 +953,10 @@ int LightManager::add_ies_from_file(ustring filename) content = "\n"; } - return add_ies(ustring(content)); + return add_ies(content); } -int LightManager::add_ies(ustring content) +int LightManager::add_ies(const string &content) { uint hash = hash_string(content.c_str()); diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index 79450ea5f8d..6dd23374818 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -92,8 +92,8 @@ class LightManager { ~LightManager(); /* IES texture management */ - int add_ies(ustring ies); - int add_ies_from_file(ustring filename); + int add_ies(const string &ies); + int add_ies_from_file(const string &filename); void remove_ies(int slot); void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 91c3a772537..6ac66661859 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -1091,6 +1091,17 @@ bool Mesh::has_true_displacement() const return false; } +bool Mesh::has_voxel_attributes() const +{ + foreach (const Attribute &attr, attributes.attributes) { + if (attr.element == ATTR_ELEMENT_VOXEL) { + return true; + } + } + + return false; +} + float Mesh::motion_time(int step) const { return (motion_steps > 1) ? 2.0f * step / (motion_steps - 1) - 1.0f : 0.0f; @@ -2020,15 +2031,7 @@ void MeshManager::device_update_preprocess(Device *device, Scene *scene, Progres if (need_update && mesh->has_volume) { /* Create volume meshes if there is voxel data. */ - bool has_voxel_attributes = false; - - foreach (Attribute &attr, mesh->attributes.attributes) { - if (attr.element == ATTR_ELEMENT_VOXEL) { - has_voxel_attributes = true; - } - } - - if (has_voxel_attributes) { + if (mesh->has_voxel_attributes()) { if (!volume_images_updated) { progress.set_status("Updating Meshes Volume Bounds"); device_update_volume_images(device, scene, progress); diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index 05c67ccb3b7..5bb6ab328b7 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -318,6 +318,7 @@ class Mesh : public Node { bool has_motion_blur() const; bool has_true_displacement() const; + bool has_voxel_attributes() const; /* Convert between normalized -1..1 motion time and index * in the VERTEX_MOTION attribute. */ diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 8e7969cfbaf..7d4fcba9f3b 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -1067,10 +1067,10 @@ void IESLightNode::get_slot() if (slot == -1) { if (ies.empty()) { - slot = light_manager->add_ies_from_file(filename); + slot = light_manager->add_ies_from_file(filename.string()); } else { - slot = light_manager->add_ies(ies); + slot = light_manager->add_ies(ies.string()); } } } @@ -5259,6 +5259,140 @@ void OutputNode::compile(OSLCompiler &compiler) compiler.add(this, "node_output_displacement"); } +/* Map Range Node */ + +NODE_DEFINE(MapRangeNode) +{ + NodeType *type = NodeType::add("map_range", create, NodeType::SHADER); + + SOCKET_IN_FLOAT(value, "Value", 1.0f); + SOCKET_IN_FLOAT(from_min, "From Min", 0.0f); + SOCKET_IN_FLOAT(from_max, "From Max", 1.0f); + SOCKET_IN_FLOAT(to_min, "To Min", 0.0f); + SOCKET_IN_FLOAT(to_max, "To Max", 1.0f); + + SOCKET_OUT_FLOAT(result, "Result"); + + return type; +} + +MapRangeNode::MapRangeNode() : ShaderNode(node_type) +{ +} + +void MapRangeNode::expand(ShaderGraph *graph) +{ + if (clamp) { + ShaderOutput *result_out = output("Result"); + if (!result_out->links.empty()) { + ClampNode *clamp_node = new ClampNode(); + clamp_node->min = to_min; + clamp_node->max = to_max; + graph->add(clamp_node); + graph->relink(result_out, clamp_node->output("Result")); + graph->connect(result_out, clamp_node->input("Value")); + } + } +} + +void MapRangeNode::constant_fold(const ConstantFolder &folder) +{ + if (folder.all_inputs_constant()) { + float result; + if (from_max != from_min) { + result = to_min + ((value - from_min) / (from_max - from_min)) * (to_max - to_min); + } + else { + result = 0.0f; + } + folder.make_constant(result); + } +} + +void MapRangeNode::compile(SVMCompiler &compiler) +{ + ShaderInput *value_in = input("Value"); + ShaderInput *from_min_in = input("From Min"); + ShaderInput *from_max_in = input("From Max"); + ShaderInput *to_min_in = input("To Min"); + ShaderInput *to_max_in = input("To Max"); + ShaderOutput *result_out = output("Result"); + + int value_stack_offset = compiler.stack_assign(value_in); + int from_min_stack_offset = compiler.stack_assign_if_linked(from_min_in); + int from_max_stack_offset = compiler.stack_assign_if_linked(from_max_in); + int to_min_stack_offset = compiler.stack_assign_if_linked(to_min_in); + int to_max_stack_offset = compiler.stack_assign_if_linked(to_max_in); + int result_stack_offset = compiler.stack_assign(result_out); + + compiler.add_node( + NODE_MAP_RANGE, + value_stack_offset, + compiler.encode_uchar4( + from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset), + result_stack_offset); + + compiler.add_node(__float_as_int(from_min), + __float_as_int(from_max), + __float_as_int(to_min), + __float_as_int(to_max)); +} + +void MapRangeNode::compile(OSLCompiler &compiler) +{ + compiler.add(this, "node_map_range"); +} + +/* Clamp Node */ + +NODE_DEFINE(ClampNode) +{ + NodeType *type = NodeType::add("clamp", create, NodeType::SHADER); + + SOCKET_IN_FLOAT(value, "Value", 1.0f); + SOCKET_IN_FLOAT(min, "Min", 0.0f); + SOCKET_IN_FLOAT(max, "Max", 1.0f); + + SOCKET_OUT_FLOAT(result, "Result"); + + return type; +} + +ClampNode::ClampNode() : ShaderNode(node_type) +{ +} + +void ClampNode::constant_fold(const ConstantFolder &folder) +{ + if (folder.all_inputs_constant()) { + folder.make_constant(clamp(value, min, max)); + } +} + +void ClampNode::compile(SVMCompiler &compiler) +{ + ShaderInput *value_in = input("Value"); + ShaderInput *min_in = input("Min"); + ShaderInput *max_in = input("Max"); + ShaderOutput *result_out = output("Result"); + + int value_stack_offset = compiler.stack_assign(value_in); + int min_stack_offset = compiler.stack_assign(min_in); + int max_stack_offset = compiler.stack_assign(max_in); + int result_stack_offset = compiler.stack_assign(result_out); + + compiler.add_node(NODE_CLAMP, + value_stack_offset, + compiler.encode_uchar4(min_stack_offset, max_stack_offset), + result_stack_offset); + compiler.add_node(__float_as_int(min), __float_as_int(max)); +} + +void ClampNode::compile(OSLCompiler &compiler) +{ + compiler.add(this, "node_clamp"); +} + /* Math */ NODE_DEFINE(MathNode) @@ -5288,14 +5422,12 @@ NODE_DEFINE(MathNode) type_enum.insert("arctan2", NODE_MATH_ARCTAN2); type_enum.insert("floor", NODE_MATH_FLOOR); type_enum.insert("ceil", NODE_MATH_CEIL); - type_enum.insert("fract", NODE_MATH_FRACT); + type_enum.insert("fraction", NODE_MATH_FRACTION); type_enum.insert("sqrt", NODE_MATH_SQRT); SOCKET_ENUM(type, "Type", type_enum, NODE_MATH_ADD); - SOCKET_BOOLEAN(use_clamp, "Use Clamp", false); - - SOCKET_IN_FLOAT(value1, "Value1", 0.0f); - SOCKET_IN_FLOAT(value2, "Value2", 0.0f); + SOCKET_IN_FLOAT(value1, "Value1", 0.5f); + SOCKET_IN_FLOAT(value2, "Value2", 0.5f); SOCKET_OUT_FLOAT(value, "Value"); @@ -5306,13 +5438,28 @@ MathNode::MathNode() : ShaderNode(node_type) { } +void MathNode::expand(ShaderGraph *graph) +{ + if (use_clamp) { + ShaderOutput *result_out = output("Value"); + if (!result_out->links.empty()) { + ClampNode *clamp_node = new ClampNode(); + clamp_node->min = 0.0f; + clamp_node->max = 1.0f; + graph->add(clamp_node); + graph->relink(result_out, clamp_node->output("Result")); + graph->connect(result_out, clamp_node->input("Value")); + } + } +} + void MathNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { - folder.make_constant_clamp(svm_math(type, value1, value2), use_clamp); + folder.make_constant(svm_math(type, value1, value2)); } else { - folder.fold_math(type, use_clamp); + folder.fold_math(type); } } @@ -5322,20 +5469,19 @@ void MathNode::compile(SVMCompiler &compiler) ShaderInput *value2_in = input("Value2"); ShaderOutput *value_out = output("Value"); - compiler.add_node( - NODE_MATH, type, compiler.stack_assign(value1_in), compiler.stack_assign(value2_in)); - compiler.add_node(NODE_MATH, compiler.stack_assign(value_out)); + int value1_stack_offset = compiler.stack_assign(value1_in); + int value2_stack_offset = compiler.stack_assign(value2_in); + int value_stack_offset = compiler.stack_assign(value_out); - if (use_clamp) { - compiler.add_node(NODE_MATH, NODE_MATH_CLAMP, compiler.stack_assign(value_out)); - compiler.add_node(NODE_MATH, compiler.stack_assign(value_out)); - } + compiler.add_node(NODE_MATH, + type, + compiler.encode_uchar4(value1_stack_offset, value2_stack_offset), + value_stack_offset); } void MathNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "type"); - compiler.parameter(this, "use_clamp"); compiler.add(this, "node_math"); } diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 6b21be88663..514ab3db8eb 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -1228,6 +1228,31 @@ class BlackbodyNode : public ShaderNode { float temperature; }; +class MapRangeNode : public ShaderNode { + public: + SHADER_NODE_CLASS(MapRangeNode) + void constant_fold(const ConstantFolder &folder); + virtual int get_group() + { + return NODE_GROUP_LEVEL_3; + } + void expand(ShaderGraph *graph); + + float value, from_min, from_max, to_min, to_max; + bool clamp; +}; + +class ClampNode : public ShaderNode { + public: + SHADER_NODE_CLASS(ClampNode) + void constant_fold(const ConstantFolder &folder); + virtual int get_group() + { + return NODE_GROUP_LEVEL_3; + } + float value, min, max; +}; + class MathNode : public ShaderNode { public: SHADER_NODE_CLASS(MathNode) @@ -1235,11 +1260,12 @@ class MathNode : public ShaderNode { { return NODE_GROUP_LEVEL_1; } + void expand(ShaderGraph *graph); void constant_fold(const ConstantFolder &folder); float value1; float value2; - NodeMath type; + NodeMathType type; bool use_clamp; }; diff --git a/intern/cycles/render/stats.h b/intern/cycles/render/stats.h index f1bf1903483..e45403a3754 100644 --- a/intern/cycles/render/stats.h +++ b/intern/cycles/render/stats.h @@ -29,7 +29,7 @@ CCL_NAMESPACE_BEGIN * semantic around the units of size, it just should be the same for all * entries. * - * This is a generic entry foi all size-related statistics, which helps + * This is a generic entry for all size-related statistics, which helps * avoiding duplicating code for things like sorting. */ class NamedSizeEntry { diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index 7fb92bfb862..11c758c9389 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -1003,7 +1003,7 @@ TEST_F(RenderGraph, constant_fold_math_clamp) * Includes 2 tests: constant on each side. */ static void build_math_partial_test_graph(ShaderGraphBuilder &builder, - NodeMath type, + NodeMathType type, float constval) { builder diff --git a/intern/cycles/util/util_ies.cpp b/intern/cycles/util/util_ies.cpp index 7c24a4ec28c..62d3d42186d 100644 --- a/intern/cycles/util/util_ies.cpp +++ b/intern/cycles/util/util_ies.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <algorithm> + #include "util/util_foreach.h" #include "util/util_ies.h" #include "util/util_math.h" @@ -28,7 +30,7 @@ CCL_NAMESPACE_BEGIN // issue. template class GuardedAllocator<char>; -bool IESFile::load(ustring ies) +bool IESFile::load(const string &ies) { clear(); if (!parse(ies) || !process()) { @@ -76,7 +78,7 @@ class IESTextParser { vector<char> text; char *data; - IESTextParser(ustring str) : text(str.begin(), str.end()) + IESTextParser(const string &str) : text(str.begin(), str.end()) { std::replace(text.begin(), text.end(), ',', ' '); data = strstr(&text[0], "\nTILT="); @@ -116,7 +118,7 @@ class IESTextParser { } }; -bool IESFile::parse(ustring ies) +bool IESFile::parse(const string &ies) { if (ies.empty()) { return false; diff --git a/intern/cycles/util/util_ies.h b/intern/cycles/util/util_ies.h index ab1b9ea57cf..95473103614 100644 --- a/intern/cycles/util/util_ies.h +++ b/intern/cycles/util/util_ies.h @@ -17,7 +17,7 @@ #ifndef __UTIL_IES_H__ #define __UTIL_IES_H__ -#include "util/util_param.h" +#include "util/util_string.h" #include "util/util_vector.h" CCL_NAMESPACE_BEGIN @@ -32,11 +32,11 @@ class IESFile { int packed_size(); void pack(float *data); - bool load(ustring ies); + bool load(const string &ies); void clear(); protected: - bool parse(ustring ies); + bool parse(const string &ies); bool process(); bool process_type_b(); bool process_type_c(); diff --git a/intern/cycles/util/util_vector.h b/intern/cycles/util/util_vector.h index 437478d64d3..04fb33368d9 100644 --- a/intern/cycles/util/util_vector.h +++ b/intern/cycles/util/util_vector.h @@ -27,7 +27,7 @@ CCL_NAMESPACE_BEGIN -/* Own subclass-ed vestion of std::vector. Subclass is needed because: +/* Own subclass-ed version of std::vector. Subclass is needed because: * * - Use own allocator which keeps track of used/peak memory. * - Have method to ensure capacity is re-set to 0. diff --git a/intern/elbeem/CMakeLists.txt b/intern/elbeem/CMakeLists.txt index 383cfa66c15..63a6af84323 100644 --- a/intern/elbeem/CMakeLists.txt +++ b/intern/elbeem/CMakeLists.txt @@ -120,9 +120,11 @@ else() endif() # Work around hang with GCC and ASAN. -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-sanitize=vptr") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-sanitize=vptr") +if(WITH_COMPILER_ASAN) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-sanitize=vptr") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-sanitize=vptr") + endif() endif() blender_add_lib_nolist(bf_intern_elbeem "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/intern/openvdb/CMakeLists.txt b/intern/openvdb/CMakeLists.txt index bcb1d545c94..412dade0f1a 100644 --- a/intern/openvdb/CMakeLists.txt +++ b/intern/openvdb/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC . intern + ../guardedalloc ) set(INC_SYS @@ -56,12 +57,16 @@ if(WITH_OPENVDB) intern/openvdb_dense_convert.cc intern/openvdb_reader.cc intern/openvdb_writer.cc + intern/openvdb_level_set.cc + intern/openvdb_transform.cc openvdb_capi.cc openvdb_util.cc intern/openvdb_dense_convert.h intern/openvdb_reader.h intern/openvdb_writer.h + intern/openvdb_level_set.h + intern/openvdb_transform.h openvdb_util.h ) diff --git a/intern/openvdb/intern/openvdb_level_set.cc b/intern/openvdb/intern/openvdb_level_set.cc new file mode 100644 index 00000000000..a850aae2be5 --- /dev/null +++ b/intern/openvdb/intern/openvdb_level_set.cc @@ -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) 2015 Blender Foundation. + * All rights reserved. + */ + +#include "openvdb_level_set.h" +#include "openvdb_util.h" +#include "openvdb_capi.h" +#include "MEM_guardedalloc.h" +#include "openvdb/tools/Composite.h" + +OpenVDBLevelSet::OpenVDBLevelSet() +{ + openvdb::initialize(); +} + +OpenVDBLevelSet::~OpenVDBLevelSet() +{ +} + +void OpenVDBLevelSet::mesh_to_level_set(const float *vertices, + const unsigned int *faces, + const unsigned int totvertices, + const unsigned int totfaces, + const openvdb::math::Transform::Ptr &xform) +{ + std::vector<openvdb::Vec3s> points(totvertices); + std::vector<openvdb::Vec3I> triangles(totfaces); + std::vector<openvdb::Vec4I> quads; + + for (unsigned int i = 0; i < totvertices; i++) { + points[i] = openvdb::Vec3s(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]); + } + + for (unsigned int i = 0; i < totfaces; i++) { + triangles[i] = openvdb::Vec3I(faces[i * 3], faces[i * 3 + 1], faces[i * 3 + 2]); + } + + this->grid = openvdb::tools::meshToLevelSet<openvdb::FloatGrid>( + *xform, points, triangles, quads, 1); +} + +void OpenVDBLevelSet::volume_to_mesh(OpenVDBVolumeToMeshData *mesh, + const double isovalue, + const double adaptivity, + const bool relax_disoriented_triangles) +{ + std::vector<openvdb::Vec3s> out_points; + std::vector<openvdb::Vec4I> out_quads; + std::vector<openvdb::Vec3I> out_tris; + openvdb::tools::volumeToMesh<openvdb::FloatGrid>(*this->grid, + out_points, + out_tris, + out_quads, + isovalue, + adaptivity, + relax_disoriented_triangles); + mesh->vertices = (float *)MEM_malloc_arrayN( + out_points.size(), 3 * sizeof(float), "openvdb remesher out verts"); + mesh->quads = (unsigned int *)MEM_malloc_arrayN( + out_quads.size(), 4 * sizeof(unsigned int), "openvdb remesh out quads"); + mesh->triangles = NULL; + if (out_tris.size() > 0) { + mesh->triangles = (unsigned int *)MEM_malloc_arrayN( + out_tris.size(), 3 * sizeof(unsigned int), "openvdb remesh out tris"); + } + + mesh->totvertices = out_points.size(); + mesh->tottriangles = out_tris.size(); + mesh->totquads = out_quads.size(); + + for (size_t i = 0; i < out_points.size(); i++) { + mesh->vertices[i * 3] = out_points[i].x(); + mesh->vertices[i * 3 + 1] = out_points[i].y(); + mesh->vertices[i * 3 + 2] = out_points[i].z(); + } + + for (size_t i = 0; i < out_quads.size(); i++) { + mesh->quads[i * 4] = out_quads[i].x(); + mesh->quads[i * 4 + 1] = out_quads[i].y(); + mesh->quads[i * 4 + 2] = out_quads[i].z(); + mesh->quads[i * 4 + 3] = out_quads[i].w(); + } + + for (size_t i = 0; i < out_tris.size(); i++) { + mesh->triangles[i * 3] = out_tris[i].x(); + mesh->triangles[i * 3 + 1] = out_tris[i].y(); + mesh->triangles[i * 3 + 2] = out_tris[i].z(); + } +} + +void OpenVDBLevelSet::filter(OpenVDBLevelSet_FilterType filter_type, + int width, + float distance, + OpenVDBLevelSet_FilterBias filter_bias) +{ + + if (!this->grid) { + return; + } + + if (this->grid->getGridClass() != openvdb::GRID_LEVEL_SET) { + return; + } + + openvdb::tools::LevelSetFilter<openvdb::FloatGrid> filter(*this->grid); + filter.setSpatialScheme((openvdb::math::BiasedGradientScheme)filter_bias); + switch (filter_type) { + case OPENVDB_LEVELSET_FILTER_GAUSSIAN: + filter.gaussian(width); + break; + case OPENVDB_LEVELSET_FILTER_MEDIAN: + filter.median(width); + break; + case OPENVDB_LEVELSET_FILTER_MEAN: + filter.mean(width); + break; + case OPENVDB_LEVELSET_FILTER_MEAN_CURVATURE: + filter.meanCurvature(); + break; + case OPENVDB_LEVELSET_FILTER_LAPLACIAN: + filter.laplacian(); + break; + case OPENVDB_LEVELSET_FILTER_DILATE: + filter.offset(distance); + break; + case OPENVDB_LEVELSET_FILTER_ERODE: + filter.offset(distance); + break; + case OPENVDB_LEVELSET_FILTER_NONE: + break; + } +} +openvdb::FloatGrid::Ptr OpenVDBLevelSet::CSG_operation_apply( + const openvdb::FloatGrid::Ptr &gridA, + const openvdb::FloatGrid::Ptr &gridB, + OpenVDBLevelSet_CSGOperation operation) +{ + switch (operation) { + case OPENVDB_LEVELSET_CSG_UNION: + openvdb::tools::csgUnion(*gridA, *gridB); + break; + case OPENVDB_LEVELSET_CSG_DIFFERENCE: + openvdb::tools::csgDifference(*gridA, *gridB); + break; + case OPENVDB_LEVELSET_CSG_INTERSECTION: + openvdb::tools::csgIntersection(*gridA, *gridB); + break; + } + + return gridA; +} + +const openvdb::FloatGrid::Ptr &OpenVDBLevelSet::get_grid() +{ + return this->grid; +} + +void OpenVDBLevelSet::set_grid(const openvdb::FloatGrid::Ptr &grid) +{ + this->grid = grid; +} diff --git a/intern/openvdb/intern/openvdb_level_set.h b/intern/openvdb/intern/openvdb_level_set.h new file mode 100644 index 00000000000..c2e1e582fff --- /dev/null +++ b/intern/openvdb/intern/openvdb_level_set.h @@ -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. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + */ + +#ifndef __OPENVDB_LEVEL_SET_H__ +#define __OPENVDB_LEVEL_SET_H__ + +#include <openvdb/openvdb.h> +#include <openvdb/math/FiniteDifference.h> +#include <openvdb/tools/MeshToVolume.h> +#include <openvdb/tools/VolumeToMesh.h> +#include <openvdb/tools/LevelSetFilter.h> +#include <openvdb/tools/GridTransformer.h> +#include "openvdb_capi.h" + +struct OpenVDBLevelSet { + private: + openvdb::FloatGrid::Ptr grid; + + public: + OpenVDBLevelSet(); + ~OpenVDBLevelSet(); + const openvdb::FloatGrid::Ptr &get_grid(); + void set_grid(const openvdb::FloatGrid::Ptr &grid); + + void mesh_to_level_set(const float *vertices, + const unsigned int *faces, + const unsigned int totvertices, + const unsigned int totfaces, + const openvdb::math::Transform::Ptr &transform); + + void volume_to_mesh(struct OpenVDBVolumeToMeshData *mesh, + const double isovalue, + const double adaptivity, + const bool relax_disoriented_triangles); + void filter(OpenVDBLevelSet_FilterType filter_type, + int width, + float distance, + OpenVDBLevelSet_FilterBias filter_bias); + openvdb::FloatGrid::Ptr CSG_operation_apply(const openvdb::FloatGrid::Ptr &gridA, + const openvdb::FloatGrid::Ptr &gridB, + OpenVDBLevelSet_CSGOperation operation); +}; + +#endif /* __OPENVDB_LEVEL_SET_H__ */ diff --git a/intern/openvdb/intern/openvdb_transform.cc b/intern/openvdb/intern/openvdb_transform.cc new file mode 100644 index 00000000000..4bfcf43f81a --- /dev/null +++ b/intern/openvdb/intern/openvdb_transform.cc @@ -0,0 +1,43 @@ +/* + * 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) 2015 Blender Foundation. + * All rights reserved. + */ + +#include "openvdb_transform.h" + +OpenVDBTransform::OpenVDBTransform() +{ +} + +OpenVDBTransform::~OpenVDBTransform() +{ +} + +void OpenVDBTransform::create_linear_transform(double voxel_size) +{ + this->transform = openvdb::math::Transform::createLinearTransform(voxel_size); +} + +const openvdb::math::Transform::Ptr &OpenVDBTransform::get_transform() +{ + return this->transform; +} + +void OpenVDBTransform::set_transform(const openvdb::math::Transform::Ptr &transform) +{ + this->transform = transform; +} diff --git a/intern/openvdb/intern/openvdb_transform.h b/intern/openvdb/intern/openvdb_transform.h new file mode 100644 index 00000000000..e2528cd0192 --- /dev/null +++ b/intern/openvdb/intern/openvdb_transform.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + */ + +#ifndef OPENVDB_TRANSFORM_H +#define OPENVDB_TRANSFORM_H + +#include <openvdb/openvdb.h> + +struct OpenVDBTransform { + private: + openvdb::math::Transform::Ptr transform; + + public: + OpenVDBTransform(); + ~OpenVDBTransform(); + void create_linear_transform(double voxel_size); + const openvdb::math::Transform::Ptr &get_transform(); + void set_transform(const openvdb::math::Transform::Ptr &transform); +}; + +#endif // OPENVDB_TRANSFORM_H diff --git a/intern/openvdb/openvdb_capi.cc b/intern/openvdb/openvdb_capi.cc index aff27ee6c62..22d0d20ea4e 100644 --- a/intern/openvdb/openvdb_capi.cc +++ b/intern/openvdb/openvdb_capi.cc @@ -20,16 +20,8 @@ #include "openvdb_capi.h" #include "openvdb_dense_convert.h" #include "openvdb_util.h" - -struct OpenVDBFloatGrid { - int unused; -}; -struct OpenVDBIntGrid { - int unused; -}; -struct OpenVDBVectorGrid { - int unused; -}; +#include "openvdb_level_set.h" +#include "openvdb_transform.h" int OpenVDB_getVersionHex() { @@ -238,3 +230,143 @@ void OpenVDBReader_get_meta_mat4(OpenVDBReader *reader, const char *name, float { reader->mat4sMeta(name, value); } + +OpenVDBLevelSet *OpenVDBLevelSet_create(bool initGrid, OpenVDBTransform *xform) +{ + OpenVDBLevelSet *level_set = new OpenVDBLevelSet(); + if (initGrid) { + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(); + grid->setGridClass(openvdb::GRID_LEVEL_SET); + if (xform) { + grid->setTransform(xform->get_transform()); + } + level_set->set_grid(grid); + } + return level_set; +} + +OpenVDBTransform *OpenVDBTransform_create() +{ + return new OpenVDBTransform(); +} + +void OpenVDBTransform_free(OpenVDBTransform *transform) +{ + delete transform; +} + +void OpenVDBTransform_create_linear_transform(OpenVDBTransform *transform, double voxel_size) +{ + transform->create_linear_transform(voxel_size); +} + +void OpenVDBLevelSet_free(OpenVDBLevelSet *level_set) +{ + delete level_set; +} + +void OpenVDBLevelSet_mesh_to_level_set(struct OpenVDBLevelSet *level_set, + const float *vertices, + const unsigned int *faces, + const unsigned int totvertices, + const unsigned int totfaces, + OpenVDBTransform *xform) +{ + level_set->mesh_to_level_set(vertices, faces, totvertices, totfaces, xform->get_transform()); +} + +void OpenVDBLevelSet_mesh_to_level_set_transform(struct OpenVDBLevelSet *level_set, + const float *vertices, + const unsigned int *faces, + const unsigned int totvertices, + const unsigned int totfaces, + OpenVDBTransform *transform) +{ + level_set->mesh_to_level_set(vertices, faces, totvertices, totfaces, transform->get_transform()); +} + +void OpenVDBLevelSet_volume_to_mesh(struct OpenVDBLevelSet *level_set, + struct OpenVDBVolumeToMeshData *mesh, + const double isovalue, + const double adaptivity, + const bool relax_disoriented_triangles) +{ + level_set->volume_to_mesh(mesh, isovalue, adaptivity, relax_disoriented_triangles); +} + +void OpenVDBLevelSet_filter(struct OpenVDBLevelSet *level_set, + OpenVDBLevelSet_FilterType filter_type, + int width, + float distance, + OpenVDBLevelSet_FilterBias bias) +{ + level_set->filter(filter_type, width, distance, bias); +} + +void OpenVDBLevelSet_CSG_operation(struct OpenVDBLevelSet *out, + struct OpenVDBLevelSet *gridA, + struct OpenVDBLevelSet *gridB, + OpenVDBLevelSet_CSGOperation operation) +{ + openvdb::FloatGrid::Ptr grid = out->CSG_operation_apply( + gridA->get_grid(), gridB->get_grid(), operation); + out->set_grid(grid); +} + +OpenVDBLevelSet *OpenVDBLevelSet_transform_and_resample(struct OpenVDBLevelSet *level_setA, + struct OpenVDBLevelSet *level_setB, + char sampler, + float isolevel) +{ + openvdb::FloatGrid::Ptr sourceGrid = level_setA->get_grid(); + openvdb::FloatGrid::Ptr targetGrid = level_setB->get_grid()->deepCopy(); + + const openvdb::math::Transform &sourceXform = sourceGrid->transform(), + &targetXform = targetGrid->transform(); + + // Compute a source grid to target grid transform. + // (For this example, we assume that both grids' transforms are linear, + // so that they can be represented as 4 x 4 matrices.) + openvdb::Mat4R xform = sourceXform.baseMap()->getAffineMap()->getMat4() * + targetXform.baseMap()->getAffineMap()->getMat4().inverse(); + + // Create the transformer. + openvdb::tools::GridTransformer transformer(xform); + + switch (sampler) { + case OPENVDB_LEVELSET_GRIDSAMPLER_POINT: + // Resample using nearest-neighbor interpolation. + transformer.transformGrid<openvdb::tools::PointSampler, openvdb::FloatGrid>(*sourceGrid, + *targetGrid); + // Prune the target tree for optimal sparsity. + targetGrid->tree().prune(); + break; + + case OPENVDB_LEVELSET_GRIDSAMPLER_BOX: + // Resample using trilinear interpolation. + transformer.transformGrid<openvdb::tools::BoxSampler, openvdb::FloatGrid>(*sourceGrid, + *targetGrid); + // Prune the target tree for optimal sparsity. + targetGrid->tree().prune(); + break; + + case OPENVDB_LEVELSET_GRIDSAMPLER_QUADRATIC: + // Resample using triquadratic interpolation. + transformer.transformGrid<openvdb::tools::QuadraticSampler, openvdb::FloatGrid>(*sourceGrid, + *targetGrid); + // Prune the target tree for optimal sparsity. + targetGrid->tree().prune(); + break; + + case OPENVDB_LEVELSET_GRIDSAMPLER_NONE: + break; + } + + targetGrid = openvdb::tools::levelSetRebuild(*targetGrid, isolevel, 1.0f); + openvdb::tools::pruneLevelSet(targetGrid->tree()); + + OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, NULL); + level_set->set_grid(targetGrid); + + return level_set; +} diff --git a/intern/openvdb/openvdb_capi.h b/intern/openvdb/openvdb_capi.h index 236bceea3e2..826481bde34 100644 --- a/intern/openvdb/openvdb_capi.h +++ b/intern/openvdb/openvdb_capi.h @@ -24,12 +24,80 @@ extern "C" { #endif +/* Level Set Filters */ +typedef enum OpenVDBLevelSet_FilterType { + OPENVDB_LEVELSET_FILTER_NONE = 0, + OPENVDB_LEVELSET_FILTER_GAUSSIAN = 1, + OPENVDB_LEVELSET_FILTER_MEAN = 2, + OPENVDB_LEVELSET_FILTER_MEDIAN = 3, + OPENVDB_LEVELSET_FILTER_MEAN_CURVATURE = 4, + OPENVDB_LEVELSET_FILTER_LAPLACIAN = 5, + OPENVDB_LEVELSET_FILTER_DILATE = 6, + OPENVDB_LEVELSET_FILTER_ERODE = 7, +} OpenVDBLevelSet_FilterType; + +typedef enum OpenVDBLevelSet_FilterBias { + OPENVDB_LEVELSET_FIRST_BIAS = 0, + OPENVDB_LEVELSET_SECOND_BIAS, + OPENVDB_LEVELSET_THIRD_BIAS, + OPENVDB_LEVELSET_WENO5_BIAS, + OPENVDB_LEVELSET_HJWENO5_BIAS, +} OpenVDBLevelSet_FilterBias; + +/* Level Set CSG Operations */ +typedef enum OpenVDBLevelSet_CSGOperation { + OPENVDB_LEVELSET_CSG_UNION = 0, + OPENVDB_LEVELSET_CSG_DIFFERENCE = 1, + OPENVDB_LEVELSET_CSG_INTERSECTION = 2, +} OpenVDBLevelSet_CSGOperation; + +typedef enum OpenVDBLevelSet_GridSampler { + OPENVDB_LEVELSET_GRIDSAMPLER_NONE = 0, + OPENVDB_LEVELSET_GRIDSAMPLER_POINT = 1, + OPENVDB_LEVELSET_GRIDSAMPLER_BOX = 2, + OPENVDB_LEVELSET_GRIDSAMPLER_QUADRATIC = 3, +} OpenVDBLevelSet_Gridsampler; + struct OpenVDBReader; struct OpenVDBWriter; +struct OpenVDBTransform; +struct OpenVDBLevelSet; struct OpenVDBFloatGrid; struct OpenVDBIntGrid; struct OpenVDBVectorGrid; +struct OpenVDBVolumeToMeshData { + int tottriangles; + int totquads; + int totvertices; + + float *vertices; + unsigned int *quads; + unsigned int *triangles; +}; + +struct OpenVDBRemeshData { + float *verts; + unsigned int *faces; + int totfaces; + int totverts; + + float *out_verts; + unsigned int *out_faces; + unsigned int *out_tris; + int out_totverts; + int out_totfaces; + int out_tottris; + int filter_type; + enum OpenVDBLevelSet_FilterType filter_bias; + enum OpenVDBLevelSet_FilterBias filter_width; /* Parameter for gaussian, median, mean*/ + + float voxel_size; + float isovalue; + float adaptivity; + int relax_disoriented_triangles; +}; + int OpenVDB_getVersionHex(void); enum { @@ -112,6 +180,45 @@ void OpenVDBReader_get_meta_mat4(struct OpenVDBReader *reader, const char *name, float value[4][4]); +struct OpenVDBTransform *OpenVDBTransform_create(void); +void OpenVDBTransform_free(struct OpenVDBTransform *transform); +void OpenVDBTransform_create_linear_transform(struct OpenVDBTransform *transform, + double voxel_size); + +struct OpenVDBLevelSet *OpenVDBLevelSet_create(bool initGrid, struct OpenVDBTransform *xform); +void OpenVDBLevelSet_free(struct OpenVDBLevelSet *level_set); +void OpenVDBLevelSet_mesh_to_level_set(struct OpenVDBLevelSet *level_set, + const float *vertices, + const unsigned int *faces, + const unsigned int totvertices, + const unsigned int totfaces, + struct OpenVDBTransform *xform); +void OpenVDBLevelSet_mesh_to_level_set_transform(struct OpenVDBLevelSet *level_set, + const float *vertices, + const unsigned int *faces, + const unsigned int totvertices, + const unsigned int totfaces, + struct OpenVDBTransform *transform); +void OpenVDBLevelSet_volume_to_mesh(struct OpenVDBLevelSet *level_set, + struct OpenVDBVolumeToMeshData *mesh, + const double isovalue, + const double adaptivity, + const bool relax_disoriented_triangles); +void OpenVDBLevelSet_filter(struct OpenVDBLevelSet *level_set, + OpenVDBLevelSet_FilterType filter_type, + int width, + float distance, + OpenVDBLevelSet_FilterBias bias); +void OpenVDBLevelSet_CSG_operation(struct OpenVDBLevelSet *out, + struct OpenVDBLevelSet *gridA, + struct OpenVDBLevelSet *gridB, + OpenVDBLevelSet_CSGOperation operation); + +struct OpenVDBLevelSet *OpenVDBLevelSet_transform_and_resample(struct OpenVDBLevelSet *level_setA, + struct OpenVDBLevelSet *level_setB, + char sampler, + float isolevel); + #ifdef __cplusplus } #endif diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index abd0498ba4b..e9a85517647 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -749,7 +749,8 @@ const bTheme U_theme_default = { .outline_width = 1, .facedot_size = 4, .match = RGBA(0x337f334c), - .selected_highlight = RGBA(0x314e784c), + .selected_highlight = RGBA(0x223a5bff), + .active = RGBA(0x3b5689ff), .selected_object = RGBA(0xe96a00ff), .active_object = RGBA(0xffaf29ff), .edited_object = RGBA(0x00806266), diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py index 70768a102b3..e212df17f60 100644 --- a/release/scripts/modules/addon_utils.py +++ b/release/scripts/modules/addon_utils.py @@ -42,7 +42,7 @@ addons_fake_modules = {} def _initialize(): path_list = paths() for path in path_list: - _bpy.utils._sys_path_ensure(path) + _bpy.utils._sys_path_ensure_append(path) for addon in _preferences.addons: enable(addon.module) @@ -467,7 +467,7 @@ def reset_all(*, reload_scripts=False): paths_list = paths() for path in paths_list: - _bpy.utils._sys_path_ensure(path) + _bpy.utils._sys_path_ensure_append(path) for mod_name, _mod_path in _bpy.path.module_names(path): is_enabled, is_loaded = check(mod_name) diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py index bbc0c5c8405..2cca4171193 100644 --- a/release/scripts/modules/bl_i18n_utils/utils.py +++ b/release/scripts/modules/bl_i18n_utils/utils.py @@ -834,7 +834,7 @@ class I18nMessages: def parse_messages_from_po(self, src, key=None): """ Parse a po file. - Note: This function will silently "arrange" mis-formated entries, thus using afterward write_messages() should + Note: This function will silently "arrange" mis-formatted entries, thus using afterward write_messages() should always produce a po-valid file, though not correct! """ reading_msgid = False diff --git a/release/scripts/modules/bl_i18n_utils/utils_rtl.py b/release/scripts/modules/bl_i18n_utils/utils_rtl.py index 433c7f203de..11d1da068b4 100755 --- a/release/scripts/modules/bl_i18n_utils/utils_rtl.py +++ b/release/scripts/modules/bl_i18n_utils/utils_rtl.py @@ -20,7 +20,7 @@ # <pep8 compliant> -# Preprocess right-to-left languages. +# Pre-process right-to-left languages. # You can use it either standalone, or through import_po_from_branches or # update_trunk. # diff --git a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py index e98251ecc77..bedad638dbe 100644 --- a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py +++ b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py @@ -79,7 +79,7 @@ def generate(context, space_type): use_release_confirm = True # Generate items when no keys are mapped. - use_auto_keymap_alpha = False # Map manially in the default keymap + use_auto_keymap_alpha = False # Map manually in the default key-map. use_auto_keymap_num = True # Temporary, only create so we can pass 'properties' to find_item_from_operator. diff --git a/release/scripts/modules/bpy/ops.py b/release/scripts/modules/bpy/ops.py index 0697b7fddc9..8f8f42bcd46 100644 --- a/release/scripts/modules/bpy/ops.py +++ b/release/scripts/modules/bpy/ops.py @@ -188,8 +188,8 @@ class BPyOpsSubModOp: # Get the operator from blender wm = context.window_manager - # run to account for any rna values the user changes. - # NOTE: We only update active vew layer, since that's what + # Run to account for any RNA values the user changes. + # NOTE: We only update active view-layer, since that's what # operators are supposed to operate on. There might be some # corner cases when operator need a full scene update though. BPyOpsSubModOp._view_layer_update(context) diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py index b39099158c6..04aaa7bd69d 100644 --- a/release/scripts/modules/bpy/utils/__init__.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -119,11 +119,17 @@ def _test_import(module_name, loaded_modules): return mod -def _sys_path_ensure(path): - if path not in _sys.path: # reloading would add twice +# Reloading would add twice. +def _sys_path_ensure_prepend(path): + if path not in _sys.path: _sys.path.insert(0, path) +def _sys_path_ensure_append(path): + if path not in _sys.path: + _sys.path.append(path) + + def modules_from_path(path, loaded_modules): """ Load all modules in a path and return them as a list. @@ -253,7 +259,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False): for path_subdir in _script_module_dirs: path = _os.path.join(base_path, path_subdir) if _os.path.isdir(path): - _sys_path_ensure(path) + _sys_path_ensure_prepend(path) # Only add to 'sys.modules' unless this is 'startup'. if path_subdir == "startup": @@ -385,13 +391,13 @@ def refresh_script_paths(): for path_subdir in _script_module_dirs: path = _os.path.join(base_path, path_subdir) if _os.path.isdir(path): - _sys_path_ensure(path) + _sys_path_ensure_prepend(path) for path in _addon_utils.paths(): - _sys_path_ensure(path) + _sys_path_ensure_append(path) path = _os.path.join(path, "modules") if _os.path.isdir(path): - _sys_path_ensure(path) + _sys_path_ensure_append(path) def app_template_paths(subdir=None): diff --git a/release/scripts/modules/bpy_extras/mesh_utils.py b/release/scripts/modules/bpy_extras/mesh_utils.py index 41727565cfa..1576947b8b4 100644 --- a/release/scripts/modules/bpy_extras/mesh_utils.py +++ b/release/scripts/modules/bpy_extras/mesh_utils.py @@ -241,9 +241,9 @@ def edge_loops_from_edges(mesh, edges=None): def ngon_tessellate(from_data, indices, fix_loops=True, debug_print=True): """ - Takes a polyline of indices (fgon) and returns a list of face + Takes a polyline of indices (ngon) and returns a list of face index lists. Designed to be used for importers that need indices for an - fgon to create from existing verts. + ngon to create from existing verts. :arg from_data: either a mesh, or a list/tuple of vectors. :type from_data: list or :class:`bpy.types.Mesh` diff --git a/release/scripts/modules/console/complete_namespace.py b/release/scripts/modules/console/complete_namespace.py index 862f1a21260..3d8ba6b04a2 100644 --- a/release/scripts/modules/console/complete_namespace.py +++ b/release/scripts/modules/console/complete_namespace.py @@ -73,7 +73,7 @@ def complete_indices(word, namespace, obj=None, base=None): :param namespace: namespace :type namespace: dict :param obj: object evaluated from base - :param base: substring which can be evaluated into an object + :param base: sub-string which can be evaluated into an object. :type base: str :returns: completion matches :rtype: list of str diff --git a/release/scripts/modules/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py index b865273831e..190f0282339 100644 --- a/release/scripts/modules/keyingsets_utils.py +++ b/release/scripts/modules/keyingsets_utils.py @@ -85,7 +85,7 @@ def RKS_POLL_selected_items(ksi, context): # Iterator Callbacks -# all selected objects or pose bones, depending on which we've got +# All selected objects or pose bones, depending on which we've got. def RKS_ITER_selected_item(ksi, context, ks): ob = context.active_object if ob and ob.mode == 'POSE': @@ -96,13 +96,13 @@ def RKS_ITER_selected_item(ksi, context, ks): ksi.generate(context, ks, ob) -# all selected objects only +# All selected objects only. def RKS_ITER_selected_objects(ksi, context, ks): for ob in context.selected_objects: ksi.generate(context, ks, ob) -# all seelcted bones only +# All selected bones only. def RKS_ITER_selected_bones(ksi, context, ks): for bone in context.selected_pose_bones: ksi.generate(context, ks, bone) diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 9d1ba035b3e..2ff6c3fc1b0 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -20,6 +20,15 @@ import bpy +from mathutils import Vector +from idprop.types import IDPropertyArray, IDPropertyGroup + +ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector) + +# Maximum length of an array property for which a multi-line +# edit field will be displayed in the Custom Properties panel. +MAX_DISPLAY_ROWS = 4 + def rna_idprop_ui_get(item, create=True): try: @@ -101,23 +110,47 @@ def rna_idprop_has_properties(rna_item): return (nbr_props > 1) or (nbr_props and '_RNA_UI' not in keys) +def rna_idprop_value_to_python(value): + if isinstance(value, IDPropertyArray): + return value.to_list() + elif isinstance(value, IDPropertyGroup): + return value.to_dict() + else: + return value + + +def rna_idprop_value_item_type(value): + is_array = isinstance(value, ARRAY_TYPES) and len(value) > 0 + item_value = value[0] if is_array else value + return type(item_value), is_array + + def rna_idprop_ui_prop_default_set(item, prop, value): defvalue = None try: - prop_type = type(item[prop]) + prop_type, is_array = rna_idprop_value_item_type(item[prop]) if prop_type in {int, float}: - defvalue = prop_type(value) + if is_array and isinstance(value, ARRAY_TYPES): + value = [prop_type(item) for item in value] + if any(value): + defvalue = value + else: + defvalue = prop_type(value) except KeyError: pass + except ValueError: + pass if defvalue: rna_ui = rna_idprop_ui_prop_get(item, prop, True) rna_ui["default"] = defvalue else: rna_ui = rna_idprop_ui_prop_get(item, prop) - if rna_ui and "default" in rna_ui: - del rna_ui["default"] + if rna_ui: + rna_ui.pop("default", None) + + return defvalue def rna_idprop_ui_create( @@ -126,10 +159,11 @@ def rna_idprop_ui_create( soft_min=None, soft_max=None, description=None, overridable=False, + subtype=None, ): """Create and initialize a custom property with limits, defaults and other settings.""" - proptype = type(default) + proptype, is_array = rna_idprop_value_item_type(default) # Sanitize limits if proptype is bool: @@ -159,9 +193,12 @@ def rna_idprop_ui_create( rna_ui["max"] = proptype(max) rna_ui["soft_max"] = proptype(soft_max) - if default: + if default and (not is_array or any(default)): rna_ui["default"] = default + if is_array and subtype and subtype != 'NONE': + rna_ui["subtype"] = subtype + # Assign other settings if description is not None: rna_ui["description"] = description @@ -252,7 +289,11 @@ def draw(layout, context, context_member, property_type, use_edit=True): row.label(text=key, translate=False) # explicit exception for arrays. - if to_dict or to_list: + show_array_ui = to_list and not is_rna and 0 < len(val) <= MAX_DISPLAY_ROWS + + if show_array_ui and isinstance(val[0], (int, float)): + row.prop(rna_item, '["%s"]' % escape_identifier(key), text="") + elif to_dict or to_list: row.label(text=val_draw, translate=False) else: if is_rna: diff --git a/release/scripts/presets/interface_theme/blender_light.xml b/release/scripts/presets/interface_theme/blender_light.xml index dc0bb6b629e..49b01ec3309 100644 --- a/release/scripts/presets/interface_theme/blender_light.xml +++ b/release/scripts/presets/interface_theme/blender_light.xml @@ -974,6 +974,7 @@ <ThemeOutliner match="#337f33" selected_highlight="#7a8e99" + active="#92aab7" selected_object="#ffddb3" active_object="#ffffff" edited_object="#0080624d" diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 79a006acda1..2d20bff617f 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -700,20 +700,37 @@ def km_outliner(params): items.extend([ ("outliner.highlight_update", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None), ("outliner.item_rename", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), + ("outliner.item_rename", {"type": 'F2', "value": 'PRESS'}, None), ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK'}, - {"properties": [("extend", False), ("recursive", False), ("deselect_all", not params.legacy)]}), - ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, - {"properties": [("extend", True), ("recursive", False)]}), + {"properties": [("extend", False), ("deselect_all", not params.legacy)]}), ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True}, - {"properties": [("extend", False), ("recursive", True)]}), - ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "ctrl": True}, - {"properties": [("extend", True), ("recursive", True)]}), + {"properties": [("extend", True), ("deselect_all", not params.legacy)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, + {"properties": [("extend", False), ("extend_range", True), ("deselect_all", not params.legacy)]}), ("outliner.select_box", {"type": 'B', "value": 'PRESS'}, None), - ("outliner.item_openclose", {"type": 'RET', "value": 'PRESS'}, + ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("tweak", True)]}), + ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + {"properties": [("tweak", True), ("mode", "ADD")]}), + ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + {"properties": [("tweak", True), ("mode", "SUB")]}), + ("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'UP')]}), + ("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True}, + {"properties": [("direction", 'UP'), ("extend", True)]}), + ("outliner.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'DOWN')]}), + ("outliner.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True}, + {"properties": [("direction", 'DOWN'), ("extend", True)]}), + ("outliner.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'LEFT')]}), + ("outliner.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True}, + {"properties": [("direction", 'LEFT'), ("toggle_all", True)]}), + ("outliner.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'RIGHT')]}), + ("outliner.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True}, + {"properties": [("direction", 'RIGHT'), ("toggle_all", True)]}), + ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK'}, {"properties": [("all", False)]}), - ("outliner.item_openclose", {"type": 'RET', "value": 'PRESS', "shift": True}, + ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, {"properties": [("all", True)]}), - ("outliner.item_rename", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None), + ("outliner.item_openclose", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + {"properties": [("all", False)]}), ("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None), ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None), @@ -3062,7 +3079,7 @@ def km_grease_pencil_stroke_edit_mode(params): {"properties": [("mode", 'GPENCIL_SHRINKFATTEN')]}), ("transform.transform", {"type": 'F', "value": 'PRESS', "shift": True}, {"properties": [("mode", 'GPENCIL_OPACITY')]}), - # Proportonal editing + # Proportional editing. *_template_items_proportional_editing(connected=True), # Add menu ("object.gpencil_add", {"type": 'A', "value": 'PRESS', "shift": True}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index dd168ca76ec..ac372176739 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -394,7 +394,7 @@ def km_user_interface(params): ("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "ctrl": True, "alt": True}, {"properties": [("full_path", True)]}), - # rames and drivers + # Frames and drivers. ("anim.keyframe_insert_button", {"type": 'S', "value": 'PRESS'}, None), ("anim.keyframe_delete_button", {"type": 'S', "value": 'PRESS', "alt": True}, None), ("anim.keyframe_clear_button", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None), @@ -440,24 +440,39 @@ def km_outliner(params): ) items.extend([ - op_panel("TOPBAR_PT_name", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]), + ("outliner.item_rename", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), + ("outliner.item_rename", {"type": 'RET', "value": 'PRESS'}, None), ("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None), ("outliner.highlight_update", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None), - ("outliner.item_rename", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK'}, - {"properties": [("extend", False), ("recursive", False), ("deselect_all", True)]}), - ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, - {"properties": [("extend", True), ("recursive", False)]}), + {"properties": [("extend", False), ("deselect_all", True)]}), ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True}, - {"properties": [("extend", False), ("recursive", True)]}), - ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "ctrl": True}, - {"properties": [("extend", True), ("recursive", True)]}), - ("outliner.select_box", {"type": 'Q', "value": 'PRESS'}, None), - ("outliner.item_openclose", {"type": 'RIGHT_ARROW', "value": 'PRESS'}, + {"properties": [("extend", True), ("deselect_all", True)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, + {"properties": [("extend", False), ("extend_range", True), ("deselect_all", True)]}), + ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("tweak", True)]}), + ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + {"properties": [("tweak", True), ("mode", "ADD")]}), + ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + {"properties": [("tweak", True), ("mode", "SUB")]}), + ("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'UP')]}), + ("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True}, + {"properties": [("direction", 'UP'), ("extend", True)]}), + ("outliner.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'DOWN')]}), + ("outliner.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True}, + {"properties": [("direction", 'DOWN'), ("extend", True)]}), + ("outliner.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'LEFT')]}), + ("outliner.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True}, + {"properties": [("direction", 'LEFT'), ("toggle_all", True)]}), + ("outliner.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'RIGHT')]}), + ("outliner.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True}, + {"properties": [("direction", 'RIGHT'), ("toggle_all", True)]}), + ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK'}, {"properties": [("all", False)]}), - ("outliner.item_openclose", {"type": 'LEFT_ARROW', "value": 'PRESS'}, + ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, {"properties": [("all", True)]}), - ("outliner.item_rename", {"type": 'RET', "value": 'PRESS'}, None), + ("outliner.item_openclose", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + {"properties": [("all", False)]}), ("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None), ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None), @@ -2503,7 +2518,7 @@ def km_grease_pencil_stroke_weight_mode(params): # Brush strength ("wm.radial_control", {"type": 'U', "value": 'PRESS', "shift": True}, {"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt.weight_brush.strength')]}), - # Brush sze + # Brush size. ("wm.radial_control", {"type": 'S', "value": 'PRESS'}, {"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt.weight_brush.size')]}), # Display diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py index 1470aed4d55..d55644f19c7 100644 --- a/release/scripts/startup/bl_operators/anim.py +++ b/release/scripts/startup/bl_operators/anim.py @@ -105,7 +105,7 @@ class ANIM_OT_keying_set_export(Operator): # - idtype_list is used to get the list of id-datablocks from # bpy.data.* since this info isn't available elsewhere # - id.bl_rna.name gives a name suitable for UI, - # with a capitalised first letter, but we need + # with a capitalized first letter, but we need # the plural form that's all lower case # - special handling is needed for "nested" ID-blocks # (e.g. nodetree in Material) diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index 8463277e28c..c42d5970ed9 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -575,6 +575,7 @@ class JoinUVs(Operator): # finally do the copy uv_other.data.foreach_set("uv", uv_array) + mesh_other.update() if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False) diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 034aa9fff6c..6ec6855296c 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -667,6 +667,7 @@ class PREFERENCES_OT_addon_install(Operator): info = addon_utils.module_bl_info(mod) # show the newly installed addon. + context.preferences.view.show_addons_enabled_only = False context.window_manager.addon_filter = 'All' context.window_manager.addon_search = info["name"] break @@ -796,6 +797,7 @@ class PREFERENCES_OT_addon_show(Operator): info["show_expanded"] = True context.preferences.active_section = 'ADDONS' + context.preferences.view.show_addons_enabled_only = False context.window_manager.addon_filter = 'All' context.window_manager.addon_search = info["name"] bpy.ops.screen.userpref_show('INVOKE_DEFAULT') diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 5cc4b773b54..d59ad0694db 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1085,6 +1085,14 @@ rna_is_overridable_library = BoolProperty( default=False, ) +# Most useful entries of rna_enum_property_subtype_items for number arrays: +rna_vector_subtype_items = ( + ('NONE', "Plain Data", "Data values without special behavior"), + ('COLOR', "Linear Color", "Color in the linear space"), + ('COLOR_GAMMA', "Gamma-Corrected Color", "Color in the gamma corrected space"), + ('EULER', "Euler Angles", "Euler rotation angles in radians"), + ('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"), +) class WM_OT_properties_edit(Operator): bl_idname = "wm.properties_edit" @@ -1105,6 +1113,23 @@ class WM_OT_properties_edit(Operator): description: StringProperty( name="Tooltip", ) + subtype: EnumProperty( + name="Subtype", + items=lambda self, _context: WM_OT_properties_edit.subtype_items, + ) + + subtype_items = rna_vector_subtype_items + + def _init_subtype(self, prop_type, is_array, subtype): + subtype = subtype or 'NONE' + subtype_items = rna_vector_subtype_items + + # Add a temporary enum entry to preserve unknown subtypes + if not any(subtype == item[0] for item in subtype_items): + subtype_items += ((subtype, subtype, ""),) + + WM_OT_properties_edit.subtype_items = subtype_items + self.subtype = subtype def _cmp_props_get(self): # Changing these properties will refresh the UI @@ -1139,6 +1164,8 @@ class WM_OT_properties_edit(Operator): rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear, rna_idprop_ui_prop_update, + rna_idprop_ui_prop_default_set, + rna_idprop_value_item_type, ) data_path = self.data_path @@ -1174,15 +1201,15 @@ class WM_OT_properties_edit(Operator): self._last_prop[:] = [prop] - prop_type = type(item[prop]) + prop_value = item[prop] + prop_type_new = type(prop_value) + prop_type, is_array = rna_idprop_value_item_type(prop_value) prop_ui = rna_idprop_ui_prop_get(item, prop) if prop_type in {float, int}: prop_ui["min"] = prop_type(self.min) prop_ui["max"] = prop_type(self.max) - if type(default_eval) in {float, int} and default_eval != 0: - prop_ui["default"] = prop_type(default_eval) if self.use_soft_limits: prop_ui["soft_min"] = prop_type(self.soft_min) @@ -1191,10 +1218,17 @@ class WM_OT_properties_edit(Operator): prop_ui["soft_min"] = prop_type(self.min) prop_ui["soft_max"] = prop_type(self.max) + if prop_type == float and is_array and self.subtype != 'NONE': + prop_ui["subtype"] = self.subtype + else: + prop_ui.pop("subtype", None) + prop_ui["description"] = self.description + rna_idprop_ui_prop_default_set(item, prop, default_eval) + # If we have changed the type of the property, update its potential anim curves! - if prop_type_old != prop_type: + if prop_type_old != prop_type_new: data_path = '["%s"]' % bpy.utils.escape_identifier(prop) done = set() @@ -1231,7 +1265,11 @@ class WM_OT_properties_edit(Operator): return {'FINISHED'} def invoke(self, context, _event): - from rna_prop_ui import rna_idprop_ui_prop_get + from rna_prop_ui import ( + rna_idprop_ui_prop_get, + rna_idprop_value_to_python, + rna_idprop_value_item_type + ) data_path = self.data_path @@ -1248,7 +1286,7 @@ class WM_OT_properties_edit(Operator): self.is_overridable_library = bool(eval(exec_str)) # default default value - prop_type = type(self.get_value_eval()) + prop_type, is_array = rna_idprop_value_item_type(self.get_value_eval()) if prop_type in {int, float}: self.default = str(prop_type(0)) else: @@ -1263,7 +1301,7 @@ class WM_OT_properties_edit(Operator): defval = prop_ui.get("default", None) if defval is not None: - self.default = str(defval) + self.default = str(rna_idprop_value_to_python(defval)) self.soft_min = prop_ui.get("soft_min", self.min) self.soft_max = prop_ui.get("soft_max", self.max) @@ -1272,6 +1310,12 @@ class WM_OT_properties_edit(Operator): self.max != self.soft_max ) + subtype = prop_ui.get("subtype", None) + else: + subtype = None + + self._init_subtype(prop_type, is_array, subtype) + # store for comparison self._cmp_props = self._cmp_props_get() @@ -1307,12 +1351,19 @@ class WM_OT_properties_edit(Operator): return changed def draw(self, _context): + from rna_prop_ui import ( + rna_idprop_value_item_type, + ) + layout = self.layout layout.prop(self, "property") layout.prop(self, "value") + value = self.get_value_eval() + proptype, is_array = rna_idprop_value_item_type(value) + row = layout.row() - row.enabled = type(self.get_value_eval()) in {int, float} + row.enabled = proptype in {int, float} row.prop(self, "default") row = layout.row(align=True) @@ -1330,6 +1381,9 @@ class WM_OT_properties_edit(Operator): row.prop(self, "soft_max", text="Soft Max") layout.prop(self, "description") + if is_array and proptype == float: + layout.prop(self, "subtype") + class WM_OT_properties_add(Operator): bl_idname = "wm.properties_add" diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 44229b2afdf..5daacbb2e44 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -126,8 +126,6 @@ def register(): items = [ ('All', "All", "All Add-ons"), ('User', "User", "All Add-ons Installed by User"), - ('Enabled', "Enabled", "All Enabled Add-ons"), - ('Disabled', "Disabled", "All Disabled Add-ons"), ] items_unique = set() @@ -201,7 +199,7 @@ class UI_UL_list(bpy.types.UIList): for i, item in enumerate(items): name = getattr(item, propname, None) # This is similar to a logical xor - if bool(name and fnmatch.fnmatchcase(name, pattern)) is not bool(reverse): + if bool(name and fnmatch.fnmatch(name, pattern)) is not bool(reverse): flags[i] |= bitflag return flags diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index 707b1ca3f1a..8b691ddcc2a 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -73,36 +73,44 @@ class BONE_PT_transform(BoneButtonsPanel, Panel): pchan = ob.pose.bones[bone.name] col.active = not (bone.parent and bone.use_connect) - sub = col.row(align=True) - sub.prop(pchan, "location") - sub.prop(pchan, "lock_location", text="") + row = col.row(align=True) + row.prop(pchan, "location") + row.use_property_decorate = False + row.prop(pchan, "lock_location", text="", emboss=False, icon='DECORATE_UNLOCKED') - col = layout.column() rotation_mode = pchan.rotation_mode if rotation_mode == 'QUATERNION': - sub = col.row(align=True) - sub.prop(pchan, "rotation_quaternion", text="Rotation") - subsub = sub.column(align=True) - subsub.prop(pchan, "lock_rotation_w", text="") - subsub.prop(pchan, "lock_rotation", text="") + col = layout.column() + row = col.row(align=True) + row.prop(pchan, "rotation_quaternion", text="Rotation") + sub = row.column(align=True) + sub.use_property_decorate = False + sub.prop(pchan, "lock_rotation_w", text="", emboss=False, icon='DECORATE_UNLOCKED') + sub.prop(pchan, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') elif rotation_mode == 'AXIS_ANGLE': - sub = col.row(align=True) - sub.prop(pchan, "rotation_axis_angle", text="Rotation") - subsub = sub.column(align=True) - subsub.prop(pchan, "lock_rotation_w", text="") - subsub.prop(pchan, "lock_rotation", text="") - else: - sub = col.row(align=True) - sub.prop(pchan, "rotation_euler", text="Rotation") - sub.prop(pchan, "lock_rotation", text="") + col = layout.column() + row = col.row(align=True) + row.prop(pchan, "rotation_axis_angle", text="Rotation") - col = layout.column() - sub = col.row(align=True) - sub.prop(pchan, "scale") - sub.prop(pchan, "lock_scale", text="") + sub = row.column(align=True) + sub.use_property_decorate = False + sub.prop(pchan, "lock_rotation_w", text="", emboss=False, icon='DECORATE_UNLOCKED') + sub.prop(pchan, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') + else: + col = layout.column() + row = col.row(align=True) + row.prop(pchan, "rotation_euler", text="Rotation") + row.use_property_decorate = False + row.prop(pchan, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') + row = layout.row(align=True) + row.prop(pchan, "rotation_mode", text='Mode') + row.label(text="", icon='BLANK1') col = layout.column() - col.prop(pchan, "rotation_mode") + row = col.row(align=True) + row.prop(pchan, "scale") + row.use_property_decorate = False + row.prop(pchan, "lock_scale", text="", emboss=False, icon='DECORATE_UNLOCKED') elif context.edit_bone: bone = context.edit_bone @@ -114,10 +122,6 @@ class BONE_PT_transform(BoneButtonsPanel, Panel): col.prop(bone, "roll") col.prop(bone, "lock") - col = layout.column() - col.prop(bone, "tail_radius") - col.prop(bone, "envelope_distance") - class BONE_PT_curved(BoneButtonsPanel, Panel): bl_label = "Bendy Bones" @@ -143,6 +147,10 @@ class BONE_PT_curved(BoneButtonsPanel, Panel): layout.prop(bone, "bbone_segments", text="Segments") + col = layout.column(align=True) + col.prop(bone, "bbone_x", text="Display Size X") + col.prop(bone, "bbone_z", text="Z") + topcol = layout.column() topcol.active = bone.bbone_segments > 1 diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 63e4d44eada..47c90199031 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -459,6 +459,22 @@ class DATA_PT_vertex_colors(MeshButtonsPanel, Panel): col.operator("mesh.vertex_color_add", icon='ADD', text="") col.operator("mesh.vertex_color_remove", icon='REMOVE', text="") +class DATA_PT_remesh(MeshButtonsPanel, Panel): + bl_label = "Remesh" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + col = layout.column() + + mesh = context.mesh + col.prop(mesh, "remesh_voxel_size") + col.prop(mesh, "remesh_smooth_normals") + col.prop(mesh, "remesh_preserve_paint_mask") + col.operator("object.voxel_remesh", text="Voxel Remesh") + class DATA_PT_customdata(MeshButtonsPanel, Panel): bl_label = "Geometry Data" @@ -512,6 +528,7 @@ classes = ( DATA_PT_normals, DATA_PT_normals_auto_smooth, DATA_PT_texture_space, + DATA_PT_remesh, DATA_PT_customdata, DATA_PT_custom_props_mesh, ) diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 316ce818530..1d1ee2e52be 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -1793,7 +1793,7 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): elif md.mode == 'SAMPLE': col.prop(md, "length") elif md.mode == 'MERGE': - col.prop(md, "length", text="Threshold") + col.prop(md, "distance") col = layout.column() col.separator() diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index 0e45f4c97ae..7424c090764 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -54,11 +54,9 @@ class OBJECT_PT_transform(ObjectButtonsPanel, Panel): layout = self.layout layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - ob = context.object - col = flow.column() + col = layout.column() row = col.row(align=True) row.prop(ob, "location") row.use_property_decorate = False @@ -66,7 +64,7 @@ class OBJECT_PT_transform(ObjectButtonsPanel, Panel): rotation_mode = ob.rotation_mode if rotation_mode == 'QUATERNION': - col = flow.column() + col = layout.column() row = col.row(align=True) row.prop(ob, "rotation_quaternion", text="Rotation") sub = row.column(align=True) @@ -74,7 +72,7 @@ class OBJECT_PT_transform(ObjectButtonsPanel, Panel): sub.prop(ob, "lock_rotation_w", text="", emboss=False, icon='DECORATE_UNLOCKED') sub.prop(ob, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') elif rotation_mode == 'AXIS_ANGLE': - col = flow.column() + col = layout.column() row = col.row(align=True) row.prop(ob, "rotation_axis_angle", text="Rotation") @@ -83,22 +81,21 @@ class OBJECT_PT_transform(ObjectButtonsPanel, Panel): sub.prop(ob, "lock_rotation_w", text="", emboss=False, icon='DECORATE_UNLOCKED') sub.prop(ob, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') else: - col = flow.column() + col = layout.column() row = col.row(align=True) row.prop(ob, "rotation_euler", text="Rotation") row.use_property_decorate = False row.prop(ob, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') + row = layout.row(align=True) + row.prop(ob, "rotation_mode", text="Mode") + row.label(text="", icon='BLANK1') - col = flow.column() + col = layout.column() row = col.row(align=True) row.prop(ob, "scale") row.use_property_decorate = False row.prop(ob, "lock_scale", text="", emboss=False, icon='DECORATE_UNLOCKED') - row = layout.row(align=True) - row.prop(ob, "rotation_mode") - row.label(text="", icon='BLANK1') - class OBJECT_PT_delta_transform(ObjectButtonsPanel, Panel): bl_label = "Delta Transform" diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 939102bc11b..0f926e596de 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -676,7 +676,12 @@ def node_draw_tree_view(_layout, _context): # Adapt properties editor panel to display in node editor. We have to # copy the class rather than inherit due to the way bpy registration works. def node_panel(cls): - node_cls = type('NODE_' + cls.__name__, cls.__bases__, dict(cls.__dict__)) + node_cls_dict = cls.__dict__.copy() + + # Needed for re-registration. + node_cls_dict.pop("bl_rna", None) + + node_cls = type('NODE_' + cls.__name__, cls.__bases__, node_cls_dict) node_cls.bl_space_type = 'NODE_EDITOR' node_cls.bl_region_type = 'UI' diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 6832e3d463b..7bf203d8e39 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -46,6 +46,10 @@ class OUTLINER_HT_header(Header): layout.separator_spacer() + if display_mode == 'SEQUENCE': + row = layout.row(align=True) + row.prop(space, "use_sync_select", icon="UV_SYNC_SELECT", text="") + row = layout.row(align=True) if display_mode in {'SCENES', 'VIEW_LAYER'}: row.popover( @@ -328,6 +332,10 @@ class OUTLINER_PT_filter(Panel): col.prop(space, "use_sort_alpha") layout.separator() + row = layout.row(align=True) + row.prop(space, "use_sync_select", text="Sync Selection") + layout.separator() + col = layout.column(align=True) col.label(text="Search:") col.prop(space, "use_filter_complete", text="Exact Match") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index f12dff248ed..9df37158c07 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -515,7 +515,7 @@ class SEQUENCER_MT_add_empty(Menu): class SEQUENCER_MT_add_transitions(Menu): - bl_label = "Transitions" + bl_label = "Transition" def draw(self, context): diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py index 1ffb181b219..e82c6bc5dc7 100644 --- a/release/scripts/startup/bl_ui/space_text.py +++ b/release/scripts/startup/bl_ui/space_text.py @@ -50,7 +50,11 @@ class TEXT_HT_header(Header): row = layout.row(align=True) row.prop(st, "show_line_numbers", text="") row.prop(st, "show_word_wrap", text="") - row.prop(st, "show_syntax_highlight", text="") + + is_syntax_highlight_supported = st.is_syntax_highlight_supported() + syntax = row.row(align=True) + syntax.active = is_syntax_highlight_supported + syntax.prop(st, "show_syntax_highlight", text="") if text: is_osl = text.name.endswith((".osl", ".osl")) @@ -65,6 +69,7 @@ class TEXT_HT_header(Header): row.prop(text, "use_module") row = layout.row() + row.active = is_syntax_highlight_supported row.operator("text.run_script") @@ -226,7 +231,9 @@ class TEXT_MT_view(Menu): layout.prop(st, "show_line_numbers") layout.prop(st, "show_word_wrap") - layout.prop(st, "show_syntax_highlight") + syntax = layout.column() + syntax.active = st.is_syntax_highlight_supported() + syntax.prop(st, "show_syntax_highlight") layout.prop(st, "show_line_highlight") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 13e9e5350b2..fabf8abaeab 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1503,6 +1503,16 @@ class _defs_gpencil_edit: class _defs_gpencil_sculpt: @staticmethod + def poll_select_mask(context): + if context is None: + return True + ob = context.active_object + ts = context.scene.tool_settings + return ob and ob.type == 'GPENCIL' and (ts.use_gpencil_select_mask_point or + ts.use_gpencil_select_mask_stroke or + ts.use_gpencil_select_mask_segment) + + @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( context, @@ -2026,9 +2036,13 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'SCULPT_GPENCIL': [ - *_tools_gpencil_select, - None, _defs_gpencil_sculpt.generate_from_brushes, + None, + lambda context: ( + VIEW3D_PT_tools_active._tools_gpencil_select + if _defs_gpencil_sculpt.poll_select_mask(context) + else () + ), ], 'WEIGHT_GPENCIL': [ _defs_gpencil_weight.generate_from_brushes, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 128d6100a27..ce4a6fb835e 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -86,7 +86,9 @@ class USERPREF_MT_save_load(Menu): prefs = context.preferences - layout.prop(prefs, "use_preferences_save", text="Auto-Save Preferences") + row = layout.row() + row.active = not bpy.app.use_userpref_skip_save_on_exit + row.prop(prefs, "use_preferences_save", text="Auto-Save Preferences") layout.separator() @@ -1742,6 +1744,7 @@ class USERPREF_PT_addons(Panel): row.operator("preferences.addon_refresh", icon='FILE_REFRESH', text="Refresh") row = layout.row() + row.prop(context.preferences.view, "show_addons_enabled_only") row.prop(context.window_manager, "addon_filter", text="") row.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM') @@ -1768,6 +1771,7 @@ class USERPREF_PT_addons(Panel): "(see console for details)", ) + show_enabled_only = context.preferences.view.show_addons_enabled_only filter = context.window_manager.addon_filter search = context.window_manager.addon_search.lower() support = context.window_manager.addon_support @@ -1784,13 +1788,15 @@ class USERPREF_PT_addons(Panel): continue # check if addon should be visible with current filters - if ( - (filter == "All") or - (filter == info["category"]) or - (filter == "Enabled" and is_enabled) or - (filter == "Disabled" and not is_enabled) or - (filter == "User" and (mod.__file__.startswith(addon_user_dirs))) - ): + is_visible = ( + (filter == "All") or + (filter == info["category"]) or + (filter == "User" and (mod.__file__.startswith(addon_user_dirs))) + ) + if show_enabled_only: + is_visible = is_visible and is_enabled + + if is_visible: if search and search not in info["name"].lower(): if info["author"]: if search not in info["author"].lower(): diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 65619ffd285..8bb211052da 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -599,17 +599,20 @@ class VIEW3D_HT_header(Header): sub.separator(factor=0.4) sub.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE') + # Select mode for Editing if gpd.use_stroke_edit_mode: row = layout.row(align=True) - row.prop(tool_settings, "gpencil_selectmode", text="", expand=True) + row.prop(tool_settings, "gpencil_selectmode_edit", text="", expand=True) - if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode or gpd.is_stroke_weight_mode: + # Select mode for Sculpt + if gpd.is_stroke_sculpt_mode : row = layout.row(align=True) + row.prop(tool_settings, "use_gpencil_select_mask_point", text="") + row.prop(tool_settings, "use_gpencil_select_mask_stroke", text="") + row.prop(tool_settings, "use_gpencil_select_mask_segment", text="") - if gpd.is_stroke_sculpt_mode: - row.prop(tool_settings.gpencil_sculpt, "use_select_mask", text="") - row.separator() - + if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode or gpd.is_stroke_weight_mode: + row = layout.row(align=True) row.prop(gpd, "use_multiedit", text="", icon='GP_MULTIFRAME_EDITING') sub = row.row(align=True) @@ -621,8 +624,6 @@ class VIEW3D_HT_header(Header): if gpd.use_stroke_edit_mode: row = layout.row(align=True) - row.prop(tool_settings.gpencil_sculpt, "use_select_mask", text="") - row.popover( panel="VIEW3D_PT_tools_grease_pencil_interpolate", text="Interpolate", @@ -725,13 +726,20 @@ class VIEW3D_MT_editor_menus(Menu): mode_string = context.mode edit_object = context.edit_object gp_edit = obj and obj.mode in {'EDIT_GPENCIL', 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'} + ts = context.scene.tool_settings layout.menu("VIEW3D_MT_view") # Select Menu if gp_edit: if mode_string not in {'PAINT_GPENCIL', 'WEIGHT_GPENCIL'}: - layout.menu("VIEW3D_MT_select_gpencil") + if mode_string == 'SCULPT_GPENCIL' and \ + (ts.use_gpencil_select_mask_point or + ts.use_gpencil_select_mask_stroke or + ts.use_gpencil_select_mask_segment): + layout.menu("VIEW3D_MT_select_gpencil") + elif mode_string == 'EDIT_GPENCIL': + layout.menu("VIEW3D_MT_select_gpencil") elif mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}: mesh = obj.data if mesh.use_paint_mask: @@ -4388,6 +4396,7 @@ class VIEW3D_MT_paint_gpencil(Menu): layout = self.layout layout.menu("VIEW3D_MT_gpencil_animation") + layout.menu("VIEW3D_MT_edit_gpencil_interpolate") layout.separator() @@ -4443,6 +4452,7 @@ class VIEW3D_MT_edit_gpencil(Menu): layout.separator() layout.menu("VIEW3D_MT_gpencil_animation") + layout.menu("VIEW3D_MT_edit_gpencil_interpolate") layout.separator() @@ -4598,6 +4608,16 @@ class VIEW3D_MT_edit_gpencil_showhide(Menu): layout.operator("gpencil.reveal", text="Show All Layers") +class VIEW3D_MT_edit_gpencil_interpolate(Menu): + bl_label = "Interpolate" + + def draw(self, _context): + layout = self.layout + + layout.operator("gpencil.interpolate", text="Interpolate") + layout.operator("gpencil.interpolate_sequence", text="Sequence") + + class VIEW3D_MT_object_mode_pie(Menu): bl_label = "Mode" @@ -4939,7 +4959,7 @@ class VIEW3D_PT_collections(Panel): layout.use_property_split = False view_layer = context.view_layer - # We pass index 0 here beause the index is increased + # We pass index 0 here because the index is increased # so the first real index is 1 # And we start with index as 1 because we skip the master collection self._draw_collection(layout, view_layer, view_layer.layer_collection, 0) @@ -6630,6 +6650,7 @@ classes = ( VIEW3D_MT_edit_armature_names, VIEW3D_MT_edit_armature_delete, VIEW3D_MT_edit_gpencil_transform, + VIEW3D_MT_edit_gpencil_interpolate, VIEW3D_MT_object_mode_pie, VIEW3D_MT_view_pie, VIEW3D_MT_transform_gizmo_pie, diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 13172a07cb2..5246d8fa864 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -451,6 +451,8 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): brush_basic_vpaint_settings, ) + col = layout.column() + if not self.is_popover: brush_basic_vpaint_settings(col, context, brush) @@ -1137,8 +1139,38 @@ class VIEW3D_PT_sculpt_dyntopo_remesh(Panel, View3DPaintPanel): col = flow.column() col.operator("sculpt.detail_flood_fill") -# TODO, move to space_view3d.py +class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): + bl_context = ".sculpt_mode" # dot on purpose (access from topbar) + bl_label = "Remesh" + bl_options = {'DEFAULT_CLOSED'} + bl_ui_units_x = 12 + @classmethod + def poll(cls, context): + return (context.sculpt_object and context.tool_settings.sculpt) + + def draw_header(self, context): + is_popover = self.is_popover + layout = self.layout + layout.operator( + "object.voxel_remesh", + text="", + emboss=is_popover, + ) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column() + mesh = context.active_object.data + col.prop(mesh, "remesh_voxel_size") + col.prop(mesh, "remesh_smooth_normals") + col.prop(mesh, "remesh_preserve_paint_mask") + col.operator("object.voxel_remesh", text="Remesh") + +# TODO, move to space_view3d.py class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) @@ -2150,6 +2182,7 @@ classes = ( VIEW3D_PT_sculpt_options, VIEW3D_PT_sculpt_options_unified, VIEW3D_PT_sculpt_options_gravity, + VIEW3D_PT_sculpt_voxel_remesh, VIEW3D_PT_tools_weightpaint_symmetry, VIEW3D_PT_tools_weightpaint_symmetry_for_topbar, VIEW3D_PT_tools_weightpaint_options, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 01ae9c48fd2..c6e83a646e5 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -216,7 +216,7 @@ shader_node_categories = [ NodeItem("ShaderNodeEmission", poll=eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfHair", poll=object_cycles_shader_nodes_poll), NodeItem("ShaderNodeBackground", poll=world_shader_nodes_poll), - NodeItem("ShaderNodeHoldout", poll=object_cycles_shader_nodes_poll), + NodeItem("ShaderNodeHoldout", poll=object_eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeVolumeAbsorption", poll=eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeVolumeScatter", poll=eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeVolumePrincipled"), @@ -258,6 +258,8 @@ shader_node_categories = [ NodeItem("ShaderNodeVectorTransform"), ]), ShaderNodeCategory("SH_NEW_CONVERTOR", "Converter", items=[ + NodeItem("ShaderNodeMapRange"), + NodeItem("ShaderNodeClamp"), NodeItem("ShaderNodeMath"), NodeItem("ShaderNodeValToRGB"), NodeItem("ShaderNodeRGBToBW"), @@ -350,6 +352,7 @@ compositor_node_categories = [ NodeItem("CompositorNodeDBlur"), NodeItem("CompositorNodePixelate"), NodeItem("CompositorNodeSunBeams"), + NodeItem("CompositorNodeDenoise"), ]), CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items=[ NodeItem("CompositorNodeNormal"), diff --git a/release/windows/batch/blender_debug_log.cmd b/release/windows/batch/blender_debug_log.cmd index ecb5803a5c9..2d708ea9104 100644 --- a/release/windows/batch/blender_debug_log.cmd +++ b/release/windows/batch/blender_debug_log.cmd @@ -12,5 +12,5 @@ mkdir "%temp%\blender\debug_logs" > NUL 2>&1 echo. echo Starting blender and waiting for it to exit.... set PYTHONPATH= -blender --debug --python-expr "import bpy; bpy.ops.wm.sysinfo(filepath=r'%temp%\blender\debug_logs\blender_system_info.txt')" > "%temp%\blender\debug_logs\blender_debug_output.txt" 2>&1 < %0 +blender --debug --debug-cycles --python-expr "import bpy; bpy.ops.wm.sysinfo(filepath=r'%temp%\blender\debug_logs\blender_system_info.txt')" > "%temp%\blender\debug_logs\blender_debug_output.txt" 2>&1 < %0 explorer "%temp%\blender\debug_logs" diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h index 653382017d6..696e0ff1810 100644 --- a/source/blender/alembic/ABC_alembic.h +++ b/source/blender/alembic/ABC_alembic.h @@ -117,6 +117,12 @@ struct Mesh *ABC_read_mesh(struct CacheReader *reader, const char **err_str, int flags); +bool ABC_mesh_topology_changed(struct CacheReader *reader, + struct Object *ob, + struct Mesh *existing_mesh, + const float time, + const char **err_str); + void CacheReader_incref(struct CacheReader *reader); void CacheReader_free(struct CacheReader *reader); diff --git a/source/blender/alembic/intern/abc_customdata.h b/source/blender/alembic/intern/abc_customdata.h index 0ffafa8848e..52f55a41628 100644 --- a/source/blender/alembic/intern/abc_customdata.h +++ b/source/blender/alembic/intern/abc_customdata.h @@ -28,11 +28,11 @@ #include <Alembic/AbcGeom/All.h> struct CustomData; -struct Mesh; struct MLoop; struct MLoopUV; struct MPoly; struct MVert; +struct Mesh; using Alembic::Abc::ICompoundProperty; using Alembic::Abc::OCompoundProperty; diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc index 56fb5a68402..f19e0257b1b 100644 --- a/source/blender/alembic/intern/abc_exporter.cc +++ b/source/blender/alembic/intern/abc_exporter.cc @@ -241,9 +241,9 @@ Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step) getShutterSamples(step, true, samples); - Alembic::Abc::TimeSamplingType ts( - static_cast<uint32_t>(samples.size()), - 1.0 / m_settings.scene->r.frs_sec); /* TODO(Sybren): shouldn't we use the FPS macro here? */ + /* TODO(Sybren): shouldn't we use the FPS macro here? */ + Alembic::Abc::TimeSamplingType ts(static_cast<uint32_t>(samples.size()), + 1.0 / m_settings.scene->r.frs_sec); return TimeSamplingPtr(new Alembic::Abc::TimeSampling(ts, samples)); } diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc index 6647ca83bd6..8728303923a 100644 --- a/source/blender/alembic/intern/abc_mesh.cc +++ b/source/blender/alembic/intern/abc_mesh.cc @@ -1131,6 +1131,31 @@ bool AbcMeshReader::accepts_object_type( return true; } +bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector &sample_sel) +{ + IPolyMeshSchema::Sample sample; + try { + sample = m_schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + m_schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + // A similar error in read_mesh() would just return existing_mesh. + return false; + } + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + return positions->size() != existing_mesh->totvert || + face_counts->size() != existing_mesh->totpoly || + face_indices->size() != existing_mesh->totloop; +} + Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, const ISampleSelector &sample_sel, int read_flag, @@ -1162,10 +1187,7 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, ImportSettings settings; settings.read_flag |= read_flag; - bool topology_changed = positions->size() != existing_mesh->totvert || - face_counts->size() != existing_mesh->totpoly || - face_indices->size() != existing_mesh->totloop; - if (topology_changed) { + if (topology_changed(existing_mesh, sample_sel)) { new_mesh = BKE_mesh_new_nomain_from_template( existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size()); @@ -1314,7 +1336,7 @@ static void read_subd_sample(const std::string &iobject_full_name, if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { /* Alembic's 'SubD' scheme is used to store subdivision surfaces, i.e. the pre-subdivision - * mesh. Currently we don't add a subdivison modifier when we load such data. This code is + * mesh. Currently we don't add a subdivision modifier when we load such data. This code is * assuming that the subdivided surface should be smooth, and sets a flag that will eventually * mark all polygons as such. */ abc_mesh_data.poly_flag_smooth = true; diff --git a/source/blender/alembic/intern/abc_mesh.h b/source/blender/alembic/intern/abc_mesh.h index 859ab121eb6..77686a0204e 100644 --- a/source/blender/alembic/intern/abc_mesh.h +++ b/source/blender/alembic/intern/abc_mesh.h @@ -110,6 +110,8 @@ class AbcMeshReader : public AbcObjectReader { const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char **err_str); + bool topology_changed(Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &sample_sel) override; private: void readFaceSetsSample(Main *bmain, diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc index f863fe4fee7..ebebbc0da1e 100644 --- a/source/blender/alembic/intern/abc_object.cc +++ b/source/blender/alembic/intern/abc_object.cc @@ -246,6 +246,14 @@ struct Mesh *AbcObjectReader::read_mesh(struct Mesh *existing_mesh, return existing_mesh; } +bool AbcObjectReader::topology_changed(Mesh * /*existing_mesh*/, + const Alembic::Abc::ISampleSelector & /*sample_sel*/) +{ + /* The default implementation of read_mesh() just returns the original mesh, so never changes the + * topology. */ + return false; +} + void AbcObjectReader::setupObjectTransform(const float time) { bool is_constant = false; diff --git a/source/blender/alembic/intern/abc_object.h b/source/blender/alembic/intern/abc_object.h index 537f24ab7d6..efe78b9017c 100644 --- a/source/blender/alembic/intern/abc_object.h +++ b/source/blender/alembic/intern/abc_object.h @@ -190,6 +190,8 @@ class AbcObjectReader { const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char **err_str); + virtual bool topology_changed(Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &sample_sel); /** Reads the object matrix and sets up an object transform if animated. */ void setupObjectTransform(const float time); diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc index d63b310fef7..f7ba925530b 100644 --- a/source/blender/alembic/intern/alembic_capi.cc +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -937,12 +937,7 @@ void ABC_get_transform(CacheReader *reader, float r_mat[4][4], float time, float /* ************************************************************************** */ -Mesh *ABC_read_mesh(CacheReader *reader, - Object *ob, - Mesh *existing_mesh, - const float time, - const char **err_str, - int read_flag) +static AbcObjectReader *get_abc_reader(CacheReader *reader, Object *ob, const char **err_str) { AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); IObject iobject = abc_reader->iobject(); @@ -958,12 +953,44 @@ Mesh *ABC_read_mesh(CacheReader *reader, return NULL; } + return abc_reader; +} + +static ISampleSelector sample_selector_for_time(float time) +{ /* kFloorIndex is used to be compatible with non-interpolating * properties; they use the floor. */ - ISampleSelector sample_sel(time, ISampleSelector::kFloorIndex); + return ISampleSelector(time, ISampleSelector::kFloorIndex); +} + +Mesh *ABC_read_mesh(CacheReader *reader, + Object *ob, + Mesh *existing_mesh, + const float time, + const char **err_str, + int read_flag) +{ + AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); + if (abc_reader == NULL) { + return NULL; + } + + ISampleSelector sample_sel = sample_selector_for_time(time); return abc_reader->read_mesh(existing_mesh, sample_sel, read_flag, err_str); } +bool ABC_mesh_topology_changed( + CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str) +{ + AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); + if (abc_reader == NULL) { + return NULL; + } + + ISampleSelector sample_sel = sample_selector_for_time(time); + return abc_reader->topology_changed(existing_mesh, sample_sel); +} + /* ************************************************************************** */ void CacheReader_free(CacheReader *reader) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 7283ade3ae5..6748a5324ac 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -619,8 +619,8 @@ static void blf_font_draw_buffer_ex( cbuf[0] = (unsigned char)((b_col_char[0] * a) + (cbuf[0] * (1.0f - a))); cbuf[1] = (unsigned char)((b_col_char[1] * a) + (cbuf[1] * (1.0f - a))); cbuf[2] = (unsigned char)((b_col_char[2] * a) + (cbuf[2] * (1.0f - a))); - cbuf[3] = (unsigned char)MIN2((int)cbuf[3] + (int)(a * 255), - 255); /* clamp to 255 */ + /* clamp to 255 */ + cbuf[3] = (unsigned char)MIN2((int)cbuf[3] + (int)(a * 255), 255); } } } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 66be94aaa06..c0a53cbf282 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -329,10 +329,10 @@ GlyphBLF *blf_glyph_add(FontBLF *font, unsigned int index, unsigned int c) /* Convert result from 1 bit per pixel to 8 bit per pixel */ /* Accum errors for later, fine if not interested beyond "ok vs any error" */ FT_Bitmap_New(&tempbitmap); - err += FT_Bitmap_Convert(font->ft_lib, - &slot->bitmap, - &tempbitmap, - 1); /* Does Blender use Pitch 1 always? It works so far */ + + /* Does Blender use Pitch 1 always? It works so far */ + err += FT_Bitmap_Convert(font->ft_lib, &slot->bitmap, &tempbitmap, 1); + err += FT_Bitmap_Copy(font->ft_lib, &tempbitmap, &slot->bitmap); err += FT_Bitmap_Done(font->ft_lib, &tempbitmap); } diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index ced9f4a3153..0c55ae8ee83 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ * \note Use #STRINGIFY() rather than defining with quotes. */ #define BLENDER_VERSION 281 -#define BLENDER_SUBVERSION 1 +#define BLENDER_SUBVERSION 2 /** Several breakages with 280, e.g. collections vs layers. */ #define BLENDER_MINVERSION 280 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 53976b4c1ad..755a155653b 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -137,8 +137,8 @@ void CTX_store_free(bContextStore *store); void CTX_store_free_list(ListBase *contexts); /* need to store if python is initialized or not */ -int CTX_py_init_get(bContext *C); -void CTX_py_init_set(bContext *C, int value); +bool CTX_py_init_get(bContext *C); +void CTX_py_init_set(bContext *C, bool value); void *CTX_py_dict_get(const bContext *C); void CTX_py_dict_set(bContext *C, void *value); diff --git a/source/blender/blenkernel/BKE_mask.h b/source/blender/blenkernel/BKE_mask.h index bfdeadc7f60..f87c73e35a2 100644 --- a/source/blender/blenkernel/BKE_mask.h +++ b/source/blender/blenkernel/BKE_mask.h @@ -171,7 +171,6 @@ void BKE_mask_coord_to_image(struct Image *image, /* parenting */ -void BKE_mask_evaluate_all_masks(struct Main *bmain, float ctime, const bool do_newframe); void BKE_mask_evaluate(struct Mask *mask, const float ctime, const bool do_newframe); void BKE_mask_layer_evaluate(struct MaskLayer *masklay, const float ctime, const bool do_newframe); void BKE_mask_parent_init(struct MaskParent *parent); diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index b1200c7e608..44a8f98e994 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -28,12 +28,12 @@ extern "C" { #endif -struct bNode; struct ID; struct Main; struct Material; struct Object; struct Scene; +struct bNode; /* materials */ @@ -103,9 +103,9 @@ void BKE_material_resize_id(struct Main *bmain, struct ID *id, short totcol, boo void BKE_material_append_id(struct Main *bmain, struct ID *id, struct Material *ma); struct Material *BKE_material_pop_id(struct Main *bmain, struct ID *id, - int index, - bool update_data); /* index is an int because of RNA */ -void BKE_material_clear_id(struct Main *bmain, struct ID *id, bool update_data); + /* index is an int because of RNA. */ + int index); +void BKE_material_clear_id(struct Main *bmain, struct ID *id); /* rendering */ void ramp_blend(int type, float r_col[3], const float fac, const float col[3]); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 924cfad37d6..94d118bde36 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -486,6 +486,7 @@ void BKE_mesh_calc_poly_center(const struct MPoly *mpoly, float BKE_mesh_calc_poly_area(const struct MPoly *mpoly, const struct MLoop *loopstart, const struct MVert *mvarray); +float BKE_mesh_calc_poly_uv_area(const struct MPoly *mpoly, const struct MLoopUV *uv_array); void BKE_mesh_calc_poly_angles(const struct MPoly *mpoly, const struct MLoop *loopstart, const struct MVert *mvarray, diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h new file mode 100644 index 00000000000..089e4de4709 --- /dev/null +++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 by Blender Foundation + * All rights reserved. + */ +#ifndef __BKE_REMESH_H__ +#define __BKE_REMESH_H__ + +/** \file + * \ingroup bke + */ + +#ifdef WITH_OPENVDB +# include "openvdb_capi.h" +#endif + +struct Mesh; + +/* OpenVDB Voxel Remesher */ +#ifdef WITH_OPENVDB +struct OpenVDBLevelSet *BKE_mesh_remesh_voxel_ovdb_mesh_to_level_set_create( + struct Mesh *mesh, struct OpenVDBTransform *transform); +struct Mesh *BKE_mesh_remesh_voxel_ovdb_volume_to_mesh_nomain(struct OpenVDBLevelSet *level_set, + double isovalue, + double adaptivity, + bool relax_disoriented_triangles); +#endif +struct Mesh *BKE_mesh_remesh_voxel_to_mesh_nomain(struct Mesh *mesh, float voxel_size); + +/* Data reprojection functions */ +void BKE_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source); + +#endif /* __BKE_REMESH_H__ */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 8a107aac538..7d427cb7799 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -310,9 +310,8 @@ typedef struct bNodeTreeType { /* callbacks */ void (*free_cache)(struct bNodeTree *ntree); void (*free_node_cache)(struct bNodeTree *ntree, struct bNode *node); - void (*foreach_nodeclass)(struct Scene *scene, - void *calldata, - bNodeClassCallback func); /* iteration over all node classes */ + /* Iteration over all node classes. */ + void (*foreach_nodeclass)(struct Scene *scene, void *calldata, bNodeClassCallback func); /* Check visibility in the node editor */ bool (*poll)(const struct bContext *C, struct bNodeTreeType *ntreetype); /* Select a node tree from the context */ @@ -616,6 +615,7 @@ void nodeUpdateInternalLinks(struct bNodeTree *ntree, struct bNode *node); int nodeSocketIsHidden(struct bNodeSocket *sock); void ntreeTagUsedSockets(struct bNodeTree *ntree); +void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available); /* Node Clipboard */ void BKE_node_clipboard_init(struct bNodeTree *ntree); @@ -976,6 +976,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, #define SH_NODE_VOLUME_PRINCIPLED 200 /* 201..700 occupied by other node types, continue from 701 */ #define SH_NODE_BSDF_HAIR_PRINCIPLED 701 +#define SH_NODE_MAP_RANGE 702 +#define SH_NODE_CLAMP 703 /* custom defines options for Material node */ #define SH_NODE_MAT_DIFF 1 @@ -1128,6 +1130,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree, #define CMP_NODE_CORNERPIN 321 #define CMP_NODE_SWITCH_VIEW 322 #define CMP_NODE_CRYPTOMATTE 323 +#define CMP_NODE_DENOISE 324 /* channel toggles */ #define CMP_CHAN_RGB 1 diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index cbe250d0ac8..37667599488 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -259,6 +259,8 @@ typedef struct SculptSession { struct StrokeCache *cache; + int active_vertex_index; + union { struct { struct SculptVertexPaintGeomMap gmap; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 3806868e060..62544efad2c 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -301,6 +301,7 @@ typedef struct PBVHVertexIter { int gx; int gy; int i; + int index; /* grid */ struct CCGElem **grids; @@ -369,6 +370,7 @@ void pbvh_vertex_iter_init(PBVH *bvh, PBVHNode *node, PBVHVertexIter *vi, int mo continue; \ vi.co = vi.mvert->co; \ vi.no = vi.mvert->no; \ + vi.index = vi.vert_indices[vi.i]; \ if (vi.vmask) \ vi.mask = &vi.vmask[vi.vert_indices[vi.gx]]; \ } \ @@ -385,6 +387,7 @@ void pbvh_vertex_iter_init(PBVH *bvh, PBVHNode *node, PBVHVertexIter *vi, int mo continue; \ vi.co = vi.bm_vert->co; \ vi.fno = vi.bm_vert->no; \ + vi.index = BM_elem_index_get(vi.bm_vert); \ vi.mask = BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \ } diff --git a/source/blender/blenkernel/BKE_writeavi.h b/source/blender/blenkernel/BKE_writeavi.h index 3212bad75cb..7fc740a4a9b 100644 --- a/source/blender/blenkernel/BKE_writeavi.h +++ b/source/blender/blenkernel/BKE_writeavi.h @@ -53,10 +53,10 @@ typedef struct bMovieHandle { const char *suffix, struct ReportList *reports); void (*end_movie)(void *context_v); - void (*get_movie_path)(char *string, - struct RenderData *rd, - bool preview, - const char *suffix); /* optional */ + + /* Optional function. */ + void (*get_movie_path)(char *string, struct RenderData *rd, bool preview, const char *suffix); + void *(*context_create)(void); void (*context_free)(void *context_v); } bMovieHandle; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index d9bd87d97b5..669abff6599 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -151,6 +151,7 @@ set(SRC intern/mesh_mapping.c intern/mesh_merge.c intern/mesh_remap.c + intern/mesh_remesh_voxel.c intern/mesh_runtime.c intern/mesh_tangent.c intern/mesh_validate.c @@ -300,6 +301,7 @@ set(SRC BKE_mesh_iterators.h BKE_mesh_mapping.h BKE_mesh_remap.h + BKE_mesh_remesh_voxel.h BKE_mesh_runtime.h BKE_mesh_tangent.h BKE_modifier.h diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 57a7379eeae..d072a0aa599 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1420,9 +1420,7 @@ short action_get_item_transforms(bAction *act, Object *ob, bPoseChannel *pchan, if ((curves) || (flags & ACT_TRANS_PROP) == 0) { /* custom properties only */ - pPtr = strstr( - bPtr, - "[\""); /* extra '"' comment here to keep my texteditor functionlist working :) */ + pPtr = strstr(bPtr, "[\""); if (pPtr) { flags |= ACT_TRANS_PROP; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 742e3634bf1..ea927ca9333 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2707,8 +2707,9 @@ void BKE_pose_where_is_bone(struct Depsgraph *depsgraph, cob = BKE_constraints_make_evalob(depsgraph, scene, ob, pchan, CONSTRAINT_OBTYPE_BONE); /* Solve PoseChannel's Constraints */ - BKE_constraints_solve( - depsgraph, &pchan->constraints, cob, ctime); /* ctime doesn't alter objects */ + + /* ctime doesn't alter objects. */ + BKE_constraints_solve(depsgraph, &pchan->constraints, cob, ctime); /* cleanup after Constraint Solving * - applies matrix back to pchan, and frees temporary struct used diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index bf7d81e5d63..cd1cdce91e0 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -889,7 +889,7 @@ void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph, Object *object, bPoseChannel *pchan = pose_pchan_get_indexed(object, pchan_index); DEG_debug_print_eval_subdata( depsgraph, __func__, object->id.name, object, "pchan", pchan->name, pchan); - /* TODO(sergey): Use indexec lookup, once it's guaranteed to be kept + /* TODO(sergey): Use indexed lookup, once it's guaranteed to be kept * around for the time while proxies are evaluating. */ #if 0 diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 25f2797915a..2031576190e 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -239,8 +239,9 @@ static Collection *collection_duplicate_recursive(Main *bmain, if (!do_hierarchy || collection_old->id.newid == NULL) { BKE_id_copy(bmain, &collection_old->id, (ID **)&collection_new); - id_us_min( - &collection_new->id); /* Copying add one user by default, need to get rid of that one. */ + + /* Copying add one user by default, need to get rid of that one. */ + id_us_min(&collection_new->id); if (do_hierarchy) { ID_NEW_SET(collection_old, collection_new); diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 2cb98d8d90b..f536f21c3e5 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -89,7 +89,8 @@ struct bContext { struct Scene *scene; int recursion; - int py_init; /* true if python is initialized */ + /** True if python is initialized. */ + bool py_init; void *py_context; } data; }; @@ -212,11 +213,11 @@ void CTX_store_free_list(ListBase *contexts) /* is python initialized? */ -int CTX_py_init_get(bContext *C) +bool CTX_py_init_get(bContext *C) { return C->data.py_init; } -void CTX_py_init_set(bContext *C, int value) +void CTX_py_init_set(bContext *C, bool value) { C->data.py_init = value; } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index efdfa2c19bb..980e8043a42 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -2737,8 +2737,10 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render) /* check if we will calculate tilt data */ do_tilt = CU_DO_TILT(cu, nu); - do_radius = CU_DO_RADIUS( - cu, nu); /* normal display uses the radius, better just to calculate them */ + + /* Normal display uses the radius, better just to calculate them. */ + do_radius = CU_DO_RADIUS(cu, nu); + do_weight = true; /* check we are a single point? also check we are not a surface and that the orderu is sane, diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 3c7ac5d8d2e..db0ed0dc0fb 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -294,7 +294,7 @@ static void layerInterp_mdeformvert(const void **sources, } } - /* delay writing to the destination incase dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ /* now we know how many unique deform weights there are, so realloc */ if (dvert->dw && (dvert->totweight == totweight)) { @@ -441,7 +441,7 @@ static void layerInterp_tface( } } - /* delay writing to the destination incase dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ *tf = *(MTFace *)(*sources); memcpy(tf->uv, uv, sizeof(tf->uv)); } @@ -548,7 +548,7 @@ static void layerInterp_origspace_face( } } - /* delay writing to the destination in case dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ memcpy(osf->uv, uv, sizeof(osf->uv)); } @@ -908,7 +908,7 @@ static void layerInterp_mloopcol( /* Subdivide smooth or fractal can cause problems without clamping * although weights should also not cause this situation */ - /* also delay writing to the destination incase dest is in sources */ + /* Also delay writing to the destination in case dest is in sources. */ mc->r = round_fl_to_uchar_clamp(col.r); mc->g = round_fl_to_uchar_clamp(col.g); mc->b = round_fl_to_uchar_clamp(col.b); @@ -1008,7 +1008,7 @@ static void layerInterp_mloopuv( } } - /* delay writing to the destination incase dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ copy_v2_v2(((MLoopUV *)dest)->uv, uv); ((MLoopUV *)dest)->flag = flag; } @@ -1104,7 +1104,7 @@ static void layerInterp_mloop_origspace( } } - /* delay writing to the destination incase dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ copy_v2_v2(((OrigSpaceLoop *)dest)->uv, uv); } /* --- end copy */ @@ -1152,7 +1152,7 @@ static void layerInterp_mcol( } } - /* delay writing to the destination incase dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ for (j = 0; j < 4; ++j) { /* Subdivide smooth or fractal can cause problems without clamping @@ -1220,7 +1220,7 @@ static void layerInterp_bweight(const void **sources, } } - /* delay writing to the destination incase dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ *((float *)dest) = f; } @@ -1251,7 +1251,7 @@ static void layerInterp_shapekey(const void **sources, } } - /* delay writing to the destination incase dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ copy_v3_v3((float *)dest, co); } @@ -1289,7 +1289,7 @@ static void layerInterp_mvert_skin(const void **sources, madd_v3_v3fl(radius, vs_src->radius, w); } - /* delay writing to the destination incase dest is in sources */ + /* Delay writing to the destination in case dest is in sources. */ vs_dst = dest; copy_v3_v3(vs_dst->radius, radius); vs_dst->flag &= ~MVERT_SKIN_ROOT; @@ -2828,9 +2828,7 @@ void CustomData_interp(const CustomData *source, const void *source_buf[SOURCE_BUF_SIZE]; const void **sources = source_buf; - /* slow fallback in case we're interpolating a ridiculous number of - * elements - */ + /* Slow fallback in case we're interpolating a ridiculous number of elements. */ if (count > SOURCE_BUF_SIZE) { sources = MEM_malloc_arrayN(count, sizeof(*sources), __func__); } @@ -3828,9 +3826,7 @@ void CustomData_bmesh_interp(CustomData *data, void *source_buf[SOURCE_BUF_SIZE]; const void **sources = (const void **)source_buf; - /* slow fallback in case we're interpolating a ridiculous number of - * elements - */ + /* Slow fallback in case we're interpolating a ridiculous number of elements. */ if (count > SOURCE_BUF_SIZE) { sources = MEM_malloc_arrayN(count, sizeof(*sources), __func__); } diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 1fb25375159..04cbdbb6bcd 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -4061,8 +4061,10 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( hit_found = HIT_VOLUME; /* Mark hit info */ - madd_v3_v3v3fl( - hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ + + /* Calculate final hit coordinates */ + madd_v3_v3v3fl(hitCoord, ray_start, ray_dir, hit.dist); + depth += dist * sample_factor; hitTri = f_index; } @@ -4113,8 +4115,10 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( treeData->tree, ray_start, proj_ray, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); if (hit.index != -1) { proxDist = hit.dist; - madd_v3_v3v3fl( - hitCo, ray_start, proj_ray, hit.dist); /* Calculate final hit coordinates */ + + /* Calculate final hit coordinates */ + madd_v3_v3v3fl(hitCo, ray_start, proj_ray, hit.dist); + tri = hit.index; } } diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index b8234ccc5bb..1c3f6a56629 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -61,11 +61,10 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em) /* The tessellation is NOT calculated on the copy here, * because currently all the callers of this function use - * it to make a backup copy of the BMEditMesh to restore - * it in the case of errors in an operation. For perf - * reasons, in that case it makes more sense to do the - * tessellation only when/if that copy ends up getting - * used.*/ + * it to make a backup copy of the #BMEditMesh to restore + * it in the case of errors in an operation. For performance reasons, + * in that case it makes more sense to do the + * tessellation only when/if that copy ends up getting used. */ em_copy->looptris = NULL; return em_copy; @@ -97,8 +96,8 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em) BMesh *bm = em->bm; - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ + /* This assumes all faces can be scan-filled, which isn't always true, + * worst case we over allocate a little which is acceptable. */ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); const int looptris_tot_prev_alloc = em->looptris ? (MEM_allocN_len(em->looptris) / sizeof(*em->looptris)) : @@ -109,7 +108,7 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em) /* this means no reallocs for quad dominant models, for */ if ((em->looptris != NULL) && /* (*em->tottri >= looptris_tot)) */ - /* check against alloc'd size incase we over alloc'd a little */ + /* Check against allocated size in case we over allocated a little. */ ((looptris_tot_prev_alloc >= looptris_tot) && (looptris_tot_prev_alloc <= looptris_tot * 2))) { looptris = em->looptris; diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 7cbd5b6b050..ffab82b75af 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -1338,8 +1338,9 @@ void BKE_sim_debug_data_clear_category(const char *category) BLI_ghashIterator_init(&iter, _sim_debug_data->gh); while (!BLI_ghashIterator_done(&iter)) { const SimDebugElement *elem = BLI_ghashIterator_getValue(&iter); - BLI_ghashIterator_step( - &iter); /* removing invalidates the current iterator, so step before removing */ + + /* Removing invalidates the current iterator, so step before removing. */ + BLI_ghashIterator_step(&iter); if (elem->category_hash == category_hash) { BLI_ghash_remove(_sim_debug_data->gh, elem, NULL, debug_element_free); diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 140d1f0d26c..9580ea763fb 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -506,8 +506,10 @@ static int binarysearch_bezt_index_ex( */ for (loopbreaker = 0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { /* compute and get midpoint */ - int mid = start + ((end - start) / - 2); /* we calculate the midpoint this way to avoid int overflows... */ + + /* We calculate the midpoint this way to avoid int overflows... */ + int mid = start + ((end - start) / 2); + float midfra = array[mid].vec[1][0]; /* check if exactly equal to midpoint */ diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c index 4295a44098c..48c0258bf47 100644 --- a/source/blender/blenkernel/intern/fmodifier.c +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -555,8 +555,10 @@ int BKE_fcm_envelope_find_index(FCM_EnvelopeData array[], */ for (loopbreaker = 0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { /* compute and get midpoint */ - int mid = start + ((end - start) / - 2); /* we calculate the midpoint this way to avoid int overflows... */ + + /* we calculate the midpoint this way to avoid int overflows... */ + int mid = start + ((end - start) / 2); + float midfra = array[mid].time; /* check if exactly equal to midpoint */ diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 78117a4f615..b55635560be 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -955,7 +955,7 @@ static bool vfont_to_curve(Object *ob, } } - current_line_length += xof; + current_line_length += xof - MARGIN_X_MIN; if (ct->dobreak) { current_line_length += twidth; } @@ -1026,7 +1026,7 @@ static bool vfont_to_curve(Object *ob, } ct++; } - current_line_length += xof + twidth; + current_line_length += xof + twidth - MARGIN_X_MIN; longest_line_length = MAX2(current_line_length, longest_line_length); cu->lines = 1; diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index ed4c6848751..0354aeaf0ca 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -642,8 +642,10 @@ void BKE_gpencil_copy_data(bGPdata *gpd_dst, const bGPdata *gpd_src, const int U BLI_listbase_clear(&gpd_dst->layers); for (const bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { /* make a copy of source layer and its data */ - bGPDlayer *gpl_dst = BKE_gpencil_layer_duplicate( - gpl_src); /* TODO here too could add unused flags... */ + + /* TODO here too could add unused flags... */ + bGPDlayer *gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); + BLI_addtail(&gpd_dst->layers, gpl_dst); } } @@ -1782,33 +1784,55 @@ bool BKE_gpencil_smooth_stroke_strength(bGPDstroke *gps, int point_index, float bGPDspoint *ptb = &gps->points[point_index]; /* Do nothing if not enough points */ - if (gps->totpoints <= 2) { + if ((gps->totpoints <= 2) || (point_index < 1)) { return false; } + /* Only affect endpoints by a fraction of the normal influence */ + float inf = influence; + if ((point_index == 0) || (point_index == gps->totpoints - 1)) { + inf *= 0.01f; + } + /* Limit max influence to reduce pop effect. */ + CLAMP_MAX(inf, 0.98f); - /* Compute theoretical optimal value using distances */ - bGPDspoint *pta, *ptc; - int before = point_index - 1; - int after = point_index + 1; + float total = 0.0f; + float max_strength = 0.0f; + const int steps = 4; + const float average_fac = 1.0f / (float)(steps * 2 + 1); + int step; - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); + /* add the point itself */ + total += ptb->strength * average_fac; + max_strength = ptb->strength; - pta = &gps->points[before]; - ptc = &gps->points[after]; + /* n-steps before/after current point */ + for (step = 1; step <= steps; step++) { + bGPDspoint *pt1, *pt2; + int before = point_index - step; + int after = point_index + step; - /* the optimal value is the corresponding to the interpolation of the strength - * at the distance of point b - */ - float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); - /* sometimes the factor can be wrong due stroke geometry, so use middle point */ - if ((fac < 0.0f) || (fac > 1.0f)) { - fac = 0.5f; + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pt1 = &gps->points[before]; + pt2 = &gps->points[after]; + + /* add both these points to the average-sum (s += p[i]/n) */ + total += pt1->strength * average_fac; + total += pt2->strength * average_fac; + /* Save max value. */ + if (max_strength < pt1->strength) { + max_strength = pt1->strength; + } + if (max_strength < pt2->strength) { + max_strength = pt2->strength; + } } - const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; - /* Based on influence factor, blend between original and optimal */ - ptb->strength = (1.0f - influence) * ptb->strength + influence * optimal; + /* Based on influence factor, blend between original and optimal smoothed value. */ + ptb->strength = interpf(ptb->strength, total, inf); + /* Clamp to maximum stroke strength to avoid weird results. */ + CLAMP_MAX(ptb->strength, max_strength); return true; } @@ -1823,31 +1847,52 @@ bool BKE_gpencil_smooth_stroke_thickness(bGPDstroke *gps, int point_index, float if ((gps->totpoints <= 2) || (point_index < 1)) { return false; } + /* Only affect endpoints by a fraction of the normal influence */ + float inf = influence; + if ((point_index == 0) || (point_index == gps->totpoints - 1)) { + inf *= 0.01f; + } + /* Limit max influence to reduce pop effect. */ + CLAMP_MAX(inf, 0.98f); - /* Compute theoretical optimal value using distances */ - bGPDspoint *pta, *ptc; - int before = point_index - 1; - int after = point_index + 1; + float total = 0.0f; + float max_pressure = 0.0f; + const int steps = 4; + const float average_fac = 1.0f / (float)(steps * 2 + 1); + int step; - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); + /* add the point itself */ + total += ptb->pressure * average_fac; + max_pressure = ptb->pressure; - pta = &gps->points[before]; - ptc = &gps->points[after]; + /* n-steps before/after current point */ + for (step = 1; step <= steps; step++) { + bGPDspoint *pt1, *pt2; + int before = point_index - step; + int after = point_index + step; - /* the optimal value is the corresponding to the interpolation of the pressure - * at the distance of point b - */ - float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); - /* sometimes the factor can be wrong due stroke geometry, so use middle point */ - if ((fac < 0.0f) || (fac > 1.0f)) { - fac = 0.5f; - } - float optimal = interpf(ptc->pressure, pta->pressure, fac); + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); - /* Based on influence factor, blend between original and optimal */ - ptb->pressure = interpf(optimal, ptb->pressure, influence); + pt1 = &gps->points[before]; + pt2 = &gps->points[after]; + + /* add both these points to the average-sum (s += p[i]/n) */ + total += pt1->pressure * average_fac; + total += pt2->pressure * average_fac; + /* Save max value. */ + if (max_pressure < pt1->pressure) { + max_pressure = pt1->pressure; + } + if (max_pressure < pt2->pressure) { + max_pressure = pt2->pressure; + } + } + /* Based on influence factor, blend between original and optimal smoothed value. */ + ptb->pressure = interpf(ptb->pressure, total, inf); + /* Clamp to maximum stroke thickness to avoid weird results. */ + CLAMP_MAX(ptb->pressure, max_pressure); return true; } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 8e04ef2d3ca..a99407b9bf9 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -2766,8 +2766,8 @@ static void do_makepicstring(char *string, int frame, const char imtype, const ImageFormatData *im_format, - const short use_ext, - const short use_frames, + const bool use_ext, + const bool use_frames, const char *suffix) { if (string == NULL) { diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 40608285785..de105b9b62a 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -494,6 +494,36 @@ static LayerCollection *collection_from_index(ListBase *lb, const int number, in } /** + * Determine if a collection is hidden, viewport visibility restricted, or excluded + */ +static bool layer_collection_hidden(ViewLayer *view_layer, LayerCollection *lc) +{ + if (lc->flag & LAYER_COLLECTION_EXCLUDE) { + return true; + } + + /* Check visiblilty restriction flags */ + if (lc->flag & LAYER_COLLECTION_HIDE || lc->collection->flag & COLLECTION_RESTRICT_VIEWPORT) { + return true; + } + else { + /* Restriction flags stay set, so we need to check parents */ + CollectionParent *parent = lc->collection->parents.first; + + if (parent) { + lc = BKE_layer_collection_first_from_scene_collection(view_layer, parent->collection); + + return lc && layer_collection_hidden(view_layer, lc); + } + else { + return false; + } + } + + return false; +} + +/** * Get the collection for a given index */ LayerCollection *BKE_layer_collection_from_index(ViewLayer *view_layer, const int index) @@ -537,8 +567,9 @@ LayerCollection *BKE_layer_collection_activate_parent(ViewLayer *view_layer, Lay lc = NULL; } - if (lc && (lc->flag & LAYER_COLLECTION_EXCLUDE)) { - /* Don't activate excluded collections. */ + /* Don't activate excluded or hidden collections to prevent creating objects in a hidden + * collection from the UI */ + if (lc && layer_collection_hidden(view_layer, lc)) { return BKE_layer_collection_activate_parent(view_layer, lc); } @@ -817,8 +848,7 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) /* Always set a valid active collection. */ LayerCollection *active = view_layer->active_collection; - - if (active && (active->flag & LAYER_COLLECTION_EXCLUDE)) { + if (active && layer_collection_hidden(view_layer, active)) { BKE_layer_collection_activate_parent(view_layer, active); } else if (active == NULL) { diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index de6f5142a19..2dbca3b4db1 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -1727,8 +1727,8 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) /* This was in 2.43 and previous releases * however all data in blender should be sorted, not just duplicate names - * sorting should not hurt, but noting just incase it alters the way other - * functions work, so sort every time */ + * sorting should not hurt, but noting just in case it alters the way other + * functions work, so sort every time. */ #if 0 if (result) { id_sort_by_name(lb, id); diff --git a/source/blender/blenkernel/intern/library_override.c b/source/blender/blenkernel/intern/library_override.c index fed90ad8982..e435f07e38d 100644 --- a/source/blender/blenkernel/intern/library_override.c +++ b/source/blender/blenkernel/intern/library_override.c @@ -703,9 +703,9 @@ void BKE_override_library_update(Main *bmain, ID *local) local->tag |= LIB_TAG_OVERRIDE_LIBRARY_REFOK; /* Full rebuild of Depsgraph! */ - DEG_on_visible_update( - bmain, - true); /* XXX Is this actual valid replacement for old DAG_relations_tag_update(bmain) ? */ + + /* XXX Is this actual valid replacement for old DAG_relations_tag_update(bmain) ? */ + DEG_on_visible_update(bmain, true); } /** Update all overrides from given \a bmain. */ diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index bf77c1417ea..ca3da0d89c7 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -1168,10 +1168,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) case ID_CA: return ELEM(id_type_used, ID_OB); case ID_KE: - return ELEM(id_type_used, - ID_ME, - ID_CU, - ID_LT); /* Warning! key->from, could be more types in future? */ + /* Warning! key->from, could be more types in future? */ + return ELEM(id_type_used, ID_ME, ID_CU, ID_LT); case ID_SCR: return ELEM(id_type_used, ID_SCE); case ID_WO: @@ -1190,7 +1188,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) case ID_MC: return ELEM(id_type_used, ID_GD, ID_IM); case ID_MSK: - return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */ + /* WARNING! mask->parent.id, not typed. */ + return ELEM(id_type_used, ID_MC); case ID_LS: return (ELEM(id_type_used, ID_TE, ID_OB)); case ID_LP: diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 170b8d0ab93..8e2c3a11ac0 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -485,16 +485,23 @@ int set_listbasepointers(Main *bmain, ListBase **lb) /* BACKWARDS! also watch order of free-ing! (mesh<->mat), first items freed last. * This is important because freeing data decreases user-counts of other data-blocks, * if this data is its self freed it can crash. */ - lb[INDEX_ID_LI] = &( - bmain->libraries); /* Libraries may be accessed from pretty much any other ID... */ + + /* Libraries may be accessed from pretty much any other ID. */ + lb[INDEX_ID_LI] = &(bmain->libraries); + lb[INDEX_ID_IP] = &(bmain->ipo); - lb[INDEX_ID_AC] = &( - bmain->actions); /* moved here to avoid problems when freeing with animato (aligorith) */ + + /* Moved here to avoid problems when freeing with animato (aligorith). */ + lb[INDEX_ID_AC] = &(bmain->actions); + lb[INDEX_ID_KE] = &(bmain->shapekeys); - lb[INDEX_ID_PAL] = &( - bmain->palettes); /* referenced by gpencil, so needs to be before that to avoid crashes */ - lb[INDEX_ID_GD] = &( - bmain->gpencils); /* referenced by nodes, objects, view, scene etc, before to free after. */ + + /* Referenced by gpencil, so needs to be before that to avoid crashes. */ + lb[INDEX_ID_PAL] = &(bmain->palettes); + + /* Referenced by nodes, objects, view, scene etc, before to free after. */ + lb[INDEX_ID_GD] = &(bmain->gpencils); + lb[INDEX_ID_NT] = &(bmain->nodetrees); lb[INDEX_ID_IM] = &(bmain->images); lb[INDEX_ID_TE] = &(bmain->textures); @@ -502,8 +509,7 @@ int set_listbasepointers(Main *bmain, ListBase **lb) lb[INDEX_ID_VF] = &(bmain->fonts); /* Important!: When adding a new object type, - * the specific data should be inserted here - */ + * the specific data should be inserted here. */ lb[INDEX_ID_AR] = &(bmain->armatures); diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index bb93d068bef..fc087ff91b2 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -892,8 +892,8 @@ void BKE_mask_copy_data(Main *UNUSED(bmain), { BLI_listbase_clear(&mask_dst->masklayers); - BKE_mask_layer_copy_list(&mask_dst->masklayers, - &mask_src->masklayers); /* TODO add unused flag to those as well. */ + /* TODO add unused flag to those as well. */ + BKE_mask_layer_copy_list(&mask_dst->masklayers, &mask_src->masklayers); /* enable fake user by default */ id_fake_user_set(&mask_dst->id); @@ -1469,15 +1469,6 @@ void BKE_mask_evaluate(Mask *mask, const float ctime, const bool do_newframe) } } -void BKE_mask_evaluate_all_masks(Main *bmain, float ctime, const bool do_newframe) -{ - Mask *mask; - - for (mask = bmain->masks.first; mask; mask = mask->id.next) { - BKE_mask_evaluate(mask, ctime, do_newframe); - } -} - void BKE_mask_parent_init(MaskParent *parent) { parent->id_type = ID_MC; @@ -1637,7 +1628,9 @@ MaskLayerShape *BKE_mask_layer_shape_find_frame(MaskLayer *masklay, const int fr return NULL; } -/* when returning 2 - the frame isnt found but before/after frames are */ +/** + * When returning 2 - the frame isn't found but before/after frames are. + */ int BKE_mask_layer_shape_find_frame_range(MaskLayer *masklay, const float frame, MaskLayerShape **r_masklay_shape_a, @@ -1775,10 +1768,10 @@ static void interp_weights_uv_v2_calc(float r_uv[2], { float pt_on_line[2]; r_uv[0] = closest_to_line_v2(pt_on_line, pt, pt_a, pt_b); + r_uv[1] = (len_v2v2(pt_on_line, pt) / len_v2v2(pt_a, pt_b)) * - ((line_point_side_v2(pt_a, pt_b, pt) < 0.0f) ? - -1.0f : - 1.0f); /* this line only sets the sign */ + /* This line only sets the sign. */ + ((line_point_side_v2(pt_a, pt_b, pt) < 0.0f) ? -1.0f : 1.0f); } static void interp_weights_uv_v2_apply(const float uv[2], diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index b01c1189fd1..1545ae4f48f 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -461,7 +461,7 @@ void BKE_material_append_id(Main *bmain, ID *id, Material *ma) } } -Material *BKE_material_pop_id(Main *bmain, ID *id, int index_i, bool update_data) +Material *BKE_material_pop_id(Main *bmain, ID *id, int index_i) { short index = (short)index_i; Material *ret = NULL; @@ -489,10 +489,7 @@ Material *BKE_material_pop_id(Main *bmain, ID *id, int index_i, bool update_data test_all_objects_materials(bmain, id); } - if (update_data) { - /* decrease mat_nr index */ - material_data_index_remove_id(id, index); - } + material_data_index_remove_id(id, index); DEG_id_tag_update(id, ID_RECALC_COPY_ON_WRITE); DEG_relations_tag_update(bmain); @@ -502,7 +499,7 @@ Material *BKE_material_pop_id(Main *bmain, ID *id, int index_i, bool update_data return ret; } -void BKE_material_clear_id(Main *bmain, ID *id, bool update_data) +void BKE_material_clear_id(Main *bmain, ID *id) { Material ***matar; if ((matar = give_matarar_id(id))) { @@ -516,12 +513,9 @@ void BKE_material_clear_id(Main *bmain, ID *id, bool update_data) MEM_freeN(*matar); *matar = NULL; } - test_all_objects_materials(bmain, id); - if (update_data) { - /* decrease mat_nr index */ - material_data_index_clear_id(id); - } + test_all_objects_materials(bmain, id); + material_data_index_clear_id(id); DEG_id_tag_update(id, ID_RECALC_COPY_ON_WRITE); DEG_relations_tag_update(bmain); diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index d6fa071009e..0820f0ebbb1 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -364,12 +364,13 @@ bool BKE_mball_is_any_unselected(const MetaBall *mb) return false; } -/* \brief copy some properties from object to other metaball object with same base name +/** + * \brief copy some properties from object to other metaball object with same base name * * When some properties (wiresize, threshold, update flags) of metaball are changed, then this * properties are copied to all metaballs in same "group" (metaballs with same base name: MBall, * MBall.001, MBall.002, etc). The most important is to copy properties to the base metaball, - * because this metaball influence polygonisation of metaballs. */ + * because this metaball influence polygonization of metaballs. */ void BKE_mball_properties_copy(Scene *scene, Object *active_object) { Scene *sce_iter = scene; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 7e755e54eaa..4f39d34574e 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -536,6 +536,7 @@ void BKE_mesh_init(Mesh *me) me->size[0] = me->size[1] = me->size[2] = 1.0; me->smoothresh = DEG2RADF(30); me->texflag = ME_AUTOSPACE; + me->remesh_voxel_size = 0.1f; CustomData_reset(&me->vdata); CustomData_reset(&me->edata); diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index ee060a117dc..3a2ba078dce 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -978,7 +978,7 @@ static void curve_to_mesh_eval_ensure(Object *object) * * So we create temporary copy of the object which will use same data as the original bevel, but * will have no modifiers. */ - Object bevel_object = {NULL}; + Object bevel_object = {{NULL}}; if (remapped_curve.bevobj != NULL) { bevel_object = *remapped_curve.bevobj; BLI_listbase_clear(&bevel_object.modifiers); @@ -986,7 +986,7 @@ static void curve_to_mesh_eval_ensure(Object *object) } /* Same thing for taper. */ - Object taper_object = {NULL}; + Object taper_object = {{NULL}}; if (remapped_curve.taperobj != NULL) { taper_object = *remapped_curve.taperobj; BLI_listbase_clear(&taper_object.modifiers); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index e28d50cbde4..ad06f4b7da1 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -1320,7 +1320,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli } // print_v2("new clnors", clnors_avg); } - /* Extra bonus: since smallstack is local to this func, + /* Extra bonus: since small-stack is local to this function, * no more need to empty it at all cost! */ BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); @@ -1336,7 +1336,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli copy_v3_v3(nor, lnor); } } - /* Extra bonus: since smallstack is local to this func, + /* Extra bonus: since small-stack is local to this funcion, * no more need to empty it at all cost! */ } } @@ -2378,6 +2378,24 @@ float BKE_mesh_calc_poly_area(const MPoly *mpoly, const MLoop *loopstart, const } } +float BKE_mesh_calc_poly_uv_area(const MPoly *mpoly, const MLoopUV *uv_array) +{ + + int i, l_iter = mpoly->loopstart; + float area; + float(*vertexcos)[2] = BLI_array_alloca(vertexcos, (size_t)mpoly->totloop); + + /* pack vertex cos into an array for area_poly_v2 */ + for (i = 0; i < mpoly->totloop; i++, l_iter++) { + copy_v2_v2(vertexcos[i], uv_array[l_iter].uv); + } + + /* finally calculate the area */ + area = area_poly_v2((const float(*)[2])vertexcos, (unsigned int)mpoly->totloop); + + return area; +} + /** * Calculate the volume and volume-weighted centroid of the volume * formed by the polygon and the origin. diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c new file mode 100644 index 00000000000..a5136311a22 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c @@ -0,0 +1,204 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 by Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <time.h> +#include <float.h> +#include <ctype.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_library.h" +#include "BKE_customdata.h" +#include "BKE_bvhutils.h" +#include "BKE_mesh_remesh_voxel.h" /* own include */ + +#ifdef WITH_OPENVDB +# include "openvdb_capi.h" +#endif + +#ifdef WITH_OPENVDB +struct OpenVDBLevelSet *BKE_mesh_remesh_voxel_ovdb_mesh_to_level_set_create( + Mesh *mesh, struct OpenVDBTransform *transform) +{ + BKE_mesh_runtime_looptri_recalc(mesh); + const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh); + MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(mesh), + "remesh_looptri"); + BKE_mesh_runtime_verttri_from_looptri( + verttri, mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(mesh)); + + unsigned int totfaces = BKE_mesh_runtime_looptri_len(mesh); + unsigned int totverts = mesh->totvert; + float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts"); + unsigned int *faces = (unsigned int *)MEM_malloc_arrayN( + totfaces * 3, sizeof(unsigned int), "remesh_intput_faces"); + + for (unsigned int i = 0; i < totverts; i++) { + MVert *mvert = &mesh->mvert[i]; + verts[i * 3] = mvert->co[0]; + verts[i * 3 + 1] = mvert->co[1]; + verts[i * 3 + 2] = mvert->co[2]; + } + + for (unsigned int i = 0; i < totfaces; i++) { + MVertTri *vt = &verttri[i]; + faces[i * 3] = vt->tri[0]; + faces[i * 3 + 1] = vt->tri[1]; + faces[i * 3 + 2] = vt->tri[2]; + } + + struct OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, NULL); + OpenVDBLevelSet_mesh_to_level_set(level_set, verts, faces, totverts, totfaces, transform); + + MEM_freeN(verts); + MEM_freeN(faces); + MEM_freeN(verttri); + + return level_set; +} + +Mesh *BKE_mesh_remesh_voxel_ovdb_volume_to_mesh_nomain(struct OpenVDBLevelSet *level_set, + double isovalue, + double adaptivity, + bool relax_disoriented_triangles) +{ +# ifdef WITH_OPENVDB + struct OpenVDBVolumeToMeshData output_mesh; + OpenVDBLevelSet_volume_to_mesh( + level_set, &output_mesh, isovalue, adaptivity, relax_disoriented_triangles); +# endif + + Mesh *mesh = BKE_mesh_new_nomain(output_mesh.totvertices, + 0, + 0, + (output_mesh.totquads * 4) + (output_mesh.tottriangles * 3), + output_mesh.totquads + output_mesh.tottriangles); + + for (int i = 0; i < output_mesh.totvertices; i++) { + copy_v3_v3(mesh->mvert[i].co, &output_mesh.vertices[i * 3]); + } + + MPoly *mp = mesh->mpoly; + MLoop *ml = mesh->mloop; + for (int i = 0; i < output_mesh.totquads; i++, mp++, ml += 4) { + mp->loopstart = (int)(ml - mesh->mloop); + mp->totloop = 4; + + ml[0].v = output_mesh.quads[i * 4 + 3]; + ml[1].v = output_mesh.quads[i * 4 + 2]; + ml[2].v = output_mesh.quads[i * 4 + 1]; + ml[3].v = output_mesh.quads[i * 4]; + } + + for (int i = 0; i < output_mesh.tottriangles; i++, mp++, ml += 3) { + mp->loopstart = (int)(ml - mesh->mloop); + mp->totloop = 3; + + ml[0].v = output_mesh.triangles[i * 3 + 2]; + ml[1].v = output_mesh.triangles[i * 3 + 1]; + ml[2].v = output_mesh.triangles[i * 3]; + } + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_calc_normals(mesh); + + MEM_freeN(output_mesh.quads); + MEM_freeN(output_mesh.vertices); + + if (output_mesh.tottriangles > 0) { + MEM_freeN(output_mesh.triangles); + } + + return mesh; +} +#endif + +Mesh *BKE_mesh_remesh_voxel_to_mesh_nomain(Mesh *mesh, float voxel_size) +{ + Mesh *new_mesh = NULL; +#ifdef WITH_OPENVDB + struct OpenVDBLevelSet *level_set; + struct OpenVDBTransform *xform = OpenVDBTransform_create(); + OpenVDBTransform_create_linear_transform(xform, (double)voxel_size); + level_set = BKE_mesh_remesh_voxel_ovdb_mesh_to_level_set_create(mesh, xform); + new_mesh = BKE_mesh_remesh_voxel_ovdb_volume_to_mesh_nomain(level_set, 0.0, 0.0, false); + OpenVDBLevelSet_free(level_set); + OpenVDBTransform_free(xform); +#else + UNUSED_VARS(mesh, voxel_size); +#endif + return new_mesh; +} + +void BKE_remesh_reproject_paint_mask(Mesh *target, Mesh *source) +{ + BVHTreeFromMesh bvhtree = { + .nearest_callback = NULL, + }; + BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); + MVert *target_verts = CustomData_get_layer(&target->vdata, CD_MVERT); + + float *target_mask; + if (CustomData_has_layer(&target->vdata, CD_PAINT_MASK)) { + target_mask = CustomData_get_layer(&target->vdata, CD_PAINT_MASK); + } + else { + target_mask = CustomData_add_layer( + &target->vdata, CD_PAINT_MASK, CD_CALLOC, NULL, target->totvert); + } + + float *source_mask; + if (CustomData_has_layer(&source->vdata, CD_PAINT_MASK)) { + source_mask = CustomData_get_layer(&source->vdata, CD_PAINT_MASK); + } + else { + source_mask = CustomData_add_layer( + &source->vdata, CD_PAINT_MASK, CD_CALLOC, NULL, source->totvert); + } + + for (int i = 0; i < target->totvert; i++) { + float from_co[3]; + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + copy_v3_v3(from_co, target_verts[i].co); + BLI_bvhtree_find_nearest(bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree); + if (nearest.index != -1) { + target_mask[i] = source_mask[nearest.index]; + } + } + free_bvhtree_from_mesh(&bvhtree); +} diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 0b06bfab2ab..2cc1083aba3 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -448,8 +448,9 @@ static float nlastrip_get_frame_actionclip(NlaStrip *strip, float cframe, short if (IS_EQF(strip->scale, 0.0f)) { strip->scale = 1.0f; } - scale = fabsf( - strip->scale); /* scale must be positive - we've got a special flag for reversing */ + + /* Scale must be positive - we've got a special flag for reversing. */ + scale = fabsf(strip->scale); /* length of referenced action */ actlength = strip->actend - strip->actstart; @@ -1280,9 +1281,9 @@ static void nlastrip_fix_resize_overlaps(NlaStrip *strip) * then offset everything else by the remaining defict to give the strip room */ nls->start = nls->end - 1.0f; - offset = ceilf( - strip->end - - nls->start); /* XXX: review whether preventing fractionals is good here... */ + + /* XXX: review whether preventing fractionals is good here... */ + offset = ceilf(strip->end - nls->start); /* apply necessary offset to ensure that the strip has enough space */ for (; nls; nls = nls->next) { @@ -1329,9 +1330,9 @@ static void nlastrip_fix_resize_overlaps(NlaStrip *strip) * then offset everything else by the remaining defict to give the strip room */ nls->end = nls->start + 1.0f; - offset = ceilf( - nls->end - - strip->start); /* XXX: review whether preventing fractionals is good here... */ + + /* XXX: review whether preventing fractionals is good here... */ + offset = ceilf(nls->end - strip->start); /* apply necessary offset to ensure that the strip has enough space */ for (; nls; nls = nls->next) { diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 775f6eb37a7..d64a5a33ef1 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -2799,6 +2799,16 @@ int nodeSocketIsHidden(bNodeSocket *sock) return ((sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0); } +void nodeSetSocketAvailability(bNodeSocket *sock, bool is_available) +{ + if (is_available) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } +} + /* ************** Node Clipboard *********** */ #define USE_NODE_CB_VALIDATE @@ -3779,6 +3789,7 @@ static void registerCompositNodes(void) register_node_type_cmp_despeckle(); register_node_type_cmp_defocus(); register_node_type_cmp_sunbeams(); + register_node_type_cmp_denoise(); register_node_type_cmp_valtorgb(); register_node_type_cmp_rgbtobw(); @@ -3856,6 +3867,8 @@ static void registerShaderNodes(void) register_node_type_sh_mapping(); register_node_type_sh_curve_vec(); register_node_type_sh_curve_rgb(); + register_node_type_sh_map_range(); + register_node_type_sh_clamp(); register_node_type_sh_math(); register_node_type_sh_vect_math(); register_node_type_sh_vect_transform(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index d95c02cdf7f..ae091f32fbf 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -3587,7 +3587,7 @@ bool BKE_object_shapekey_free(Main *bmain, Object *ob) BKE_id_free_us(bmain, key); - return false; + return true; } bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb) diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index 0dedbb7e934..7983fe54be5 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -762,8 +762,8 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem no_draw_flag |= PARS_NO_DISP; } - ctime = DEG_get_ctime( - ctx->depsgraph); /* NOTE: in old animsys, used parent object's timeoffset... */ + /* NOTE: in old animsys, used parent object's timeoffset... */ + ctime = DEG_get_ctime(ctx->depsgraph); totpart = psys->totpart; totchild = psys->totchild; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 202eadaa35a..49f5aa35f93 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -432,7 +432,7 @@ void BKE_object_eval_eval_base_flags(Depsgraph *depsgraph, BKE_base_eval_flags(base); /* For render, compute base visibility again since BKE_base_eval_flags - * assumed viewport visibility. Selectability does not matter here. */ + * assumed viewport visibility. Select-ability does not matter here. */ if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) { if (base->flag & BASE_ENABLED_RENDER) { base->flag |= BASE_VISIBLE; diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 39fb668c873..fcceebc3913 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -519,7 +519,7 @@ static void ocean_compute_htilda(void *__restrict userdata, int j; - /* note the <= _N/2 here, see the fftw doco + /* Note the <= _N/2 here, see the FFTW documentation * about the mechanics of the complex->real fft storage. */ for (j = 0; j <= o->_N / 2; ++j) { fftw_complex exp_param1; diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index da00a044009..ffeba0148a2 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -1764,8 +1764,9 @@ void psys_particle_on_dm(Mesh *mesh_final, copy_v3_v3(nor, tmpnor); } - normalize_v3( - tmpnor); /* XXX Why not normalize tmpnor before copying it into nor??? -- mont29 */ + /* XXX Why not normalize tmpnor before copying it into nor??? -- mont29 */ + normalize_v3(tmpnor); + mul_v3_fl(tmpnor, -foffset); add_v3_v3(vec, tmpnor); } diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 90ae512074d..13d0f1adb84 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2073,10 +2073,9 @@ static int ptcache_path(PTCacheID *pid, char *filename) file[i - 6] = '\0'; } - BLI_snprintf(filename, - MAX_PTCACHE_PATH, - "//" PTCACHE_PATH "%s", - file); /* add blend file name to pointcache dir */ + /* Add blend file name to pointcache dir. */ + BLI_snprintf(filename, MAX_PTCACHE_PATH, "//" PTCACHE_PATH "%s", file); + BLI_path_abs(filename, blendfilename); return BLI_add_slash(filename); /* new strlen() */ } @@ -2130,24 +2129,17 @@ static int ptcache_filename(PTCacheID *pid, char *filename, int cfra, short do_p if (pid->cache->flag & PTCACHE_EXTERNAL) { if (pid->cache->index >= 0) { - BLI_snprintf(newname, - MAX_PTCACHE_FILE, - "_%06d_%02u%s", - cfra, - pid->stack_index, - ext); /* always 6 chars */ + /* Always 6 chars. */ + BLI_snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02u%s", cfra, pid->stack_index, ext); } else { - BLI_snprintf(newname, MAX_PTCACHE_FILE, "_%06d%s", cfra, ext); /* always 6 chars */ + /* Always 6 chars. */ + BLI_snprintf(newname, MAX_PTCACHE_FILE, "_%06d%s", cfra, ext); } } else { - BLI_snprintf(newname, - MAX_PTCACHE_FILE, - "_%06d_%02u%s", - cfra, - pid->stack_index, - ext); /* always 6 chars */ + /* Always 6 chars. */ + BLI_snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02u%s", cfra, pid->stack_index, ext); } len += 16; } @@ -2178,8 +2170,9 @@ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra) fp = BLI_fopen(filename, "rb"); } else if (mode == PTCACHE_FILE_WRITE) { - BLI_make_existing_file( - filename); /* will create the dir if needs be, same as //textures is created */ + /* Will create the dir if needs be, same as "//textures" is created. */ + BLI_make_existing_file(filename); + fp = BLI_fopen(filename, "wb"); } else if (mode == PTCACHE_FILE_UPDATE) { @@ -3337,7 +3330,7 @@ int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra) cache->cached_frames[cfra - cache->startframe] = 1; } - BKE_ptcache_update_info(pid); + cache->flag |= PTCACHE_FLAG_INFO_DIRTY; return !error; } @@ -3500,8 +3493,9 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) break; } - BKE_ptcache_update_info(pid); + pid->cache->flag |= PTCACHE_FLAG_INFO_DIRTY; } + int BKE_ptcache_id_exist(PTCacheID *pid, int cfra) { if (!pid->cache) { @@ -4288,7 +4282,7 @@ void BKE_ptcache_toggle_disk_cache(PTCacheID *pid) BKE_ptcache_id_time(pid, NULL, 0.0f, NULL, NULL, NULL); - BKE_ptcache_update_info(pid); + cache->flag |= PTCACHE_FLAG_INFO_DIRTY; if ((cache->flag & PTCACHE_DISK_CACHE) == 0) { if (cache->index) { @@ -4461,7 +4455,8 @@ void BKE_ptcache_load_external(PTCacheID *pid) cache->cached_frames = NULL; cache->cached_frames_len = 0; } - BKE_ptcache_update_info(pid); + + cache->flag |= PTCACHE_FLAG_INFO_DIRTY; } void BKE_ptcache_update_info(PTCacheID *pid) @@ -4469,7 +4464,9 @@ void BKE_ptcache_update_info(PTCacheID *pid) PointCache *cache = pid->cache; PTCacheExtra *extra = NULL; int totframes = 0; - char mem_info[64]; + char mem_info[sizeof(((PointCache *)0)->info) / sizeof(*(((PointCache *)0)->info))]; + + cache->flag &= ~PTCACHE_FLAG_INFO_DIRTY; if (cache->flag & PTCACHE_EXTERNAL) { int cfra = cache->startframe; diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 2024a507ebb..d3b72fb295d 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -1731,8 +1731,8 @@ static int sb_detect_vertex_collisionCached(float opco[3], /* switch origin to be nv2*/ sub_v3_v3v3(edge1, nv1, nv2); sub_v3_v3v3(edge2, nv3, nv2); - sub_v3_v3v3( - dv1, opco, nv2); /* abuse dv1 to have vertex in question at *origin* of triangle */ + /* Abuse dv1 to have vertex in question at *origin* of triangle. */ + sub_v3_v3v3(dv1, opco, nv2); cross_v3_v3v3(d_nvect, edge2, edge1); /* n_mag = */ /* UNUSED */ normalize_v3(d_nvect); @@ -2087,9 +2087,12 @@ static int _softbody_calc_forces_slice_in_a_thread(Scene *scene, if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { float gravity[3]; copy_v3_v3(gravity, scene->physics_settings.gravity); + + /* Individual mass of node here. */ mul_v3_fl(gravity, sb_grav_force_scale(ob) * _final_mass(ob, bp) * - sb->effector_weights->global_gravity); /* individual mass of node here */ + sb->effector_weights->global_gravity); + add_v3_v3(bp->force, gravity); } @@ -2099,8 +2102,10 @@ static int _softbody_calc_forces_slice_in_a_thread(Scene *scene, float kd; float force[3] = {0.0f, 0.0f, 0.0f}; float speed[3] = {0.0f, 0.0f, 0.0f}; - float eval_sb_fric_force_scale = sb_fric_force_scale( - ob); /* just for calling function once */ + + /* just for calling function once */ + float eval_sb_fric_force_scale = sb_fric_force_scale(ob); + pd_point_from_soft(scene, bp->pos, bp->vec, sb->bpoint - bp, &epoint); BKE_effectors_apply(effectors, NULL, sb->effector_weights, &epoint, force, speed); @@ -2700,7 +2705,7 @@ static void mesh_to_softbody(Scene *scene, Object *ob) for (a = 0; a < me->totvert; a++, bp++) { /* get scalar values needed *per vertex* from vertex group functions, - * so we can *paint* them nicly .. + * so we can *paint* them nicely .. * they are normalized [0.0..1.0] so may be we need amplitude for scale * which can be done by caller but still .. i'd like it to go this way */ @@ -2743,9 +2748,10 @@ static void mesh_to_softbody(Scene *scene, Object *ob) build_bps_springlist(ob); /* scan for springs attached to bodypoints ONCE */ /* insert *other second order* springs if desired */ if (sb->secondspring > 0.0000001f) { - add_2nd_order_springs( - ob, sb->secondspring); /* exploits the first run of build_bps_springlist(ob);*/ - build_bps_springlist(ob); /* yes we need to do it again*/ + /* exploits the first run of build_bps_springlist(ob); */ + add_2nd_order_springs(ob, sb->secondspring); + /* yes we need to do it again. */ + build_bps_springlist(ob); } springs_from_mesh(ob); /* write the 'rest'-length of the springs */ if (ob->softflag & OB_SB_SELF) { diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 1a3e42a7da2..83be64e84c9 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -1931,7 +1931,7 @@ bool txt_replace_char(Text *text, unsigned int add) * * \note caller must handle undo. */ -static void txt_select_prefix(Text *text, const char *add) +static void txt_select_prefix(Text *text, const char *add, bool skip_blank_lines) { int len, num, curc_old, selc_old; char *tmp; @@ -1947,7 +1947,7 @@ static void txt_select_prefix(Text *text, const char *add) while (true) { /* don't indent blank lines */ - if (text->curl->len != 0) { + if ((text->curl->len != 0) || (skip_blank_lines == 0)) { tmp = MEM_mallocN(text->curl->len + indentlen + 1, "textline_string"); text->curc = 0; @@ -1971,7 +1971,9 @@ static void txt_select_prefix(Text *text, const char *add) } if (text->curl == text->sell) { - text->selc += indentlen; + if (text->curl->len != 0) { + text->selc += indentlen; + } break; } else { @@ -1995,7 +1997,9 @@ static void txt_select_prefix(Text *text, const char *add) text->curc = 0; } else { - text->curc = curc_old + indentlen; + if (text->curl->len != 0) { + text->curc = curc_old + indentlen; + } } } @@ -2089,7 +2093,8 @@ void txt_comment(Text *text) return; } - txt_select_prefix(text, prefix); + const bool skip_blank_lines = txt_has_sel(text); + txt_select_prefix(text, prefix, skip_blank_lines); } bool txt_uncomment(Text *text) @@ -2111,7 +2116,7 @@ void txt_indent(Text *text) return; } - txt_select_prefix(text, prefix); + txt_select_prefix(text, prefix, true); } bool txt_unindent(Text *text) diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 375721057c3..e30ea687b13 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -734,8 +734,9 @@ static int unit_scale_str(char *str, len_name = strlen(replace_str); len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator */ - len_num = BLI_snprintf( - str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); /* # removed later */ + + /* # removed later */ + len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); if (len_num > len_max) { len_num = len_max; @@ -748,8 +749,9 @@ static int unit_scale_str(char *str, if (len_move > 0) { /* resize the last part of the string */ - memmove( - str_found + len_num, str_found + len_name, len_move); /* may grow or shrink the string */ + + /* May grow or shrink the string. */ + memmove(str_found + len_num, str_found + len_name, len_move); } if (found_ofs + len_num > len_max) { diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h new file mode 100644 index 00000000000..fe81de5fc5e --- /dev/null +++ b/source/blender/blenlib/BLI_delaunay_2d.h @@ -0,0 +1,199 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BLI_DELAUNAY_2D_H__ +#define __BLI_DELAUNAY_2D_H__ + +/** \file + * \ingroup bli + */ + +/** + * Interface for Constrained Delaunay Triangulation (CDT) in 2D. + * + * The input is a set of vertices, edges between those vertices, + * and faces using those vertices. + * Those inputs are called "constraints". The output must contain + * those constraints, or at least edges, points, and vertices that + * may be pieced together to form the constraints. Part of the + * work of doing the CDT is to detect intersections and mergers + * among the input elements, so these routines are also useful + * for doing 2D intersection. + * + * The output is a triangulation of the plane that includes the + * constraints in the above sense, and also satisfies the + * "Delaunay condition" as modified to take into account that + * the constraints must be there: for every non-constrained edge + * in the output, there is a circle through the endpoints that + * does not contain any of the vertices directly connected to + * those endpoints. What this means in practice is that as + * much as possible the triangles look "nice" -- not too long + * and skinny. + * + * Optionally, the output can be a subset of the triangulation + * (but still containing all of the constraints), to get the + * effect of 2D intersection. + * + * The underlying method is incremental, but we need to know + * beforehand a bounding box for all of the constraints. + * This code can be extended in the future to allow for + * deletion of constraints, if there is a use in Blender + * for dynamically maintaining a triangulation. + */ + +/** + * Input to Constrained Delaunay Triangulation. + * There are verts_len vertices, whose coordinates + * are given by vert_coords. For the rest of the input, + * vertices are referred to by indices into that array. + * Edges and Faces are optional. If provided, they will + * appear in the output triangulation ("constraints"). + * One can provide faces and not edges -- the edges + * implied by the faces will be inferred. + * + * The edges are given by pairs of vertex indices. + * The faces are given in a triple `(faces, faces_start_table, faces_len_table)` + * to represent a list-of-lists as follows: + * the vertex indices for a counterclockwise traversal of + * face number `i` starts at `faces_start_table[i]` and has `faces_len_table[i]` + * elements. + * + * The edges implied by the faces are automatically added + * and need not be put in the edges array, which is intended + * as a way to specify edges that are not part of any face. + * + * Some notes about some special cases and how they are handled: + * - Input faces can have any number of vertices greater than 2. Depending + * on the output option, ngons may be triangulated or they may remain + * as ngons. + * - Input faces may have repeated vertices. Output faces will not, + * except when the CDT_CONSTRAINTS output option is used. + * - Input faces may have edges that self-intersect, but currently the labeling + * of which output faces have which input faces may not be done correctly, + * since the labeling relies on the inside being on the left of edges + * as one traverses the face. Output faces will not self-intersect. + * - Input edges, including those implied by the input faces, may have + * zero-length or near-zero-length edges (nearness as determined by epsilon), + * but those edges will not be in the output. + * - Input edges (including face edges) can overlap or nearly overlap each other. + * The output edges will not overlap, but instead be divided into as many + * edges as necessary to represent each overlap regime. + * - Input vertices may be coincide with, or nearly coincide with (as determined + * by epsilon) other input vertices. Only one representative will survive + * in the output. If an input vertex is within epsilon of an edge (including + * an added triangulation edge), it will be snapped to that edge, so the + * output coordinates may not exactly match the input coordinates in all cases. + * - Wire edges (those not part of faces) and isolated vertices are allowed in + * the input. If they are inside faces, they will be incorporated into the + * triangulation of those faces. + * + * Epsilon is used for "is it near enough" distance calculations. + * If zero is supplied for epsilon, an internal value of 1e-8 used + * instead, since this code will not work correctly if it is not allowed + * to merge "too near" vertices. + */ +typedef struct CDT_input { + int verts_len; + int edges_len; + int faces_len; + float (*vert_coords)[2]; + int (*edges)[2]; + int *faces; + int *faces_start_table; + int *faces_len_table; + float epsilon; +} CDT_input; + +/** + * A representation of the triangulation for output. + * See #CDT_input for the representation of the output + * vertices, edges, and faces, all represented in + * a similar way to the input. + * + * The output may have merged some input vertices together, + * if they were closer than some epsilon distance. + * The output edges may be overlapping sub-segments of some + * input edges; or they may be new edges for the triangulation. + * The output faces may be pieces of some input faces, or they + * may be new. + * + * In the same way that faces lists-of-lists were represented by + * a run-together array and a "start" and "len" extra array, + * similar triples are used to represent the output to input + * mapping of vertices, edges, and faces. + * + * Those triples are: + * - verts_orig, verts_orig_start_table, verts_orig_len_table + * - edges_orig, edges_orig_start_table, edges_orig_len_table + * - faces_orig, faces_orig_start_table, faces_orig_len_table + * + * For edges, the edges_orig triple can also say which original face + * edge is part of a given output edge. If an index in edges_orig + * is greater than the input's edges_len, then subtract input's edges_len + * from it to some number i: then the face edge that starts from the + * input vertex at input's faces[i] is the corresponding face edge. + * for convenience, face_edge_offset in the result will be the input's + * edges_len, so that this conversion can be easily done by the caller. + */ +typedef struct CDT_result { + int verts_len; + int edges_len; + int faces_len; + int face_edge_offset; + float (*vert_coords)[2]; + int (*edges)[2]; + int *faces; + int *faces_start_table; + int *faces_len_table; + int *verts_orig; + int *verts_orig_start_table; + int *verts_orig_len_table; + int *edges_orig; + int *edges_orig_start_table; + int *edges_orig_len_table; + int *faces_orig; + int *faces_orig_start_table; + int *faces_orig_len_table; +} CDT_result; + +/** What triangles and edges of CDT are desired when getting output? */ +typedef enum CDT_output_type { + /** All triangles, outer boundary is convex hull. */ + CDT_FULL, + /** All triangles fully enclosed by constraint edges or faces. */ + CDT_INSIDE, + /** Only point, edge, and face constraints, and their intersections. */ + CDT_CONSTRAINTS, + /** + * Like CDT_CONSTRAINTS, but keep enough + * edges so that any output faces that came from input faces can be made as valid + * #BMesh faces in Blender: that is, + * no vertex appears more than once and no isolated holes in faces. + */ + CDT_CONSTRAINTS_VALID_BMESH +} CDT_output_type; + +/** + * API interface to CDT. + * This returns a pointer to an allocated CDT_result. + * When the caller is finished with it, the caller + * should use #BLI_delaunay_2d_cdt_free() to free it. + */ +CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_type output_type); + +void BLI_delaunay_2d_cdt_free(CDT_result *result); + +#endif /* __BLI_DELAUNAY_2D_H__ */ diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 5ac4ce8be0b..39b1b96d009 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -92,6 +92,7 @@ float volume_tetrahedron_signed_v3(const float v1[3], const float v3[3], const float v4[3]); +bool is_edge_convex_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3]); bool is_quad_convex_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3]); bool is_quad_convex_v2(const float v1[2], const float v2[2], const float v3[2], const float v4[2]); bool is_poly_convex_v2(const float verts[][2], unsigned int nr); @@ -390,6 +391,13 @@ bool isect_tri_tri_epsilon_v3(const float t_a0[3], float r_i2[3], const float epsilon); +bool isect_tri_tri_v2(const float p1[2], + const float q1[2], + const float r1[2], + const float p2[2], + const float q2[2], + const float r2[2]); + /* water-tight raycast (requires pre-calculation) */ struct IsectRayPrecalc { /* Maximal dimension kz, and orthogonal dimensions. */ diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index 31b68204c51..99e86615e50 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -42,6 +42,8 @@ void BLI_split_dirfile( const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen); void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen); void BLI_split_file_part(const char *string, char *file, const size_t filelen); +const char *BLI_path_extension(const char *filepath) ATTR_NONNULL(); + void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file) ATTR_NONNULL(); void BLI_join_dirfile(char *__restrict string, diff --git a/source/blender/blenlib/BLI_string_utils.h b/source/blender/blenlib/BLI_string_utils.h index 9740629276d..13dbb2de659 100644 --- a/source/blender/blenlib/BLI_string_utils.h +++ b/source/blender/blenlib/BLI_string_utils.h @@ -38,6 +38,7 @@ struct ListBase; typedef bool (*UniquenameCheckCallback)(void *arg, const char *name); size_t BLI_split_name_num(char *left, int *nr, const char *name, const char delim); +bool BLI_string_is_decimal(const char *string) ATTR_NONNULL(); void BLI_string_split_suffix(const char *string, char *r_body, char *r_suf, const size_t str_len); void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, const size_t str_len); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 0ec6e7ee4fc..7f6e9d49b17 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -63,6 +63,7 @@ set(SRC intern/buffer.c intern/callbacks.c intern/convexhull_2d.c + intern/delaunay_2d.c intern/dynlib.c intern/easing.c intern/edgehash.c @@ -150,6 +151,7 @@ set(SRC BLI_compiler_typecheck.h BLI_console.h BLI_convexhull_2d.h + BLI_delaunay_2d.h BLI_dial_2d.h BLI_dlrbTree.h BLI_dynlib.h diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 769bb02e2b9..05ffb02597d 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -886,7 +886,7 @@ bool BLI_ghash_ensure_p_ex(GHash *gh, const void *key, void ***r_key, void ***r_ const bool haskey = (e != NULL); if (!haskey) { - /* pass 'key' incase we resize */ + /* Pass 'key' in case we resize. */ e = BLI_mempool_alloc(gh->entrypool); ghash_insert_ex_keyonly_entry(gh, (void *)key, bucket_index, (Entry *)e); e->e.key = NULL; /* caller must re-assign */ @@ -1189,7 +1189,7 @@ bool BLI_gset_ensure_p_ex(GSet *gs, const void *key, void ***r_key) const bool haskey = (e != NULL); if (!haskey) { - /* pass 'key' incase we resize */ + /* Pass 'key' in case we resize */ e = BLI_mempool_alloc(((GHash *)gs)->entrypool); ghash_insert_ex_keyonly_entry((GHash *)gs, (void *)key, bucket_index, (Entry *)e); e->key = NULL; /* caller must re-assign */ diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c index 196d45967be..ddc7f9ee4c7 100644 --- a/source/blender/blenlib/intern/boxpack_2d.c +++ b/source/blender/blenlib/intern/boxpack_2d.c @@ -273,12 +273,12 @@ static int vertex_sort(const void *p1, const void *p2, void *vs_ctx_p) /** \} */ /** - * Main boxpacking function accessed from other functions + * Main box-packing function accessed from other functions * This sets boxes x,y to positive values, sorting from 0,0 outwards. * There is no limit to the space boxes may take, only that they will be packed * tightly into the lower left hand corner (0,0) * - * \param boxarray: a pre allocated array of boxes. + * \param boxarray: a pre-allocated array of boxes. * only the 'box->x' and 'box->y' are set, 'box->w' and 'box->h' are used, * 'box->index' is not used at all, the only reason its there * is that the box array is sorted by area and programs need to be able diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c new file mode 100644 index 00000000000..23f560c5463 --- /dev/null +++ b/source/blender/blenlib/intern/delaunay_2d.c @@ -0,0 +1,2913 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + * + * Dynamic Constrained Delaunay Triangulation. + * See paper by Marcelo Kallmann, Hanspeter Bieri, and Daniel Thalmann + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_array.h" +#include "BLI_bitmap.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_rand.h" + +#include "BLI_delaunay_2d.h" + +/* Uncomment this define to get helpful debugging functions etc. defined. */ +// #define DEBUG_CDT + +struct CDTEdge; +struct CDTFace; +struct CDTVert; + +typedef struct SymEdge { + struct SymEdge *next; /* In face, doing CCW traversal of face. */ + struct SymEdge *rot; /* CCW around vert. */ + struct CDTVert *vert; /* Vert at origin. */ + struct CDTEdge *edge; /* Undirected edge this is for. */ + struct CDTFace *face; /* Face on left side. */ +} SymEdge; + +typedef struct CDTVert { + double co[2]; /* Coordinate. */ + SymEdge *symedge; /* Some edge attached to it. */ + LinkNode *input_ids; /* List of corresponding vertex input ids. */ + int index; /* Index into array that cdt keeps. */ +} CDTVert; + +typedef struct CDTEdge { + LinkNode *input_ids; /* List of input edge ids that this is part of. */ + SymEdge symedges[2]; /* The directed edges for this edge. */ +} CDTEdge; + +typedef struct CDTFace { + double centroid[2]; /* Average of vertex coords. */ + SymEdge *symedge; /* A symedge in face; only used during output. */ + LinkNode *input_ids; /* List of input face ids that this is part of. */ + int visit_index; /* Which visit epoch has this been seen. */ + bool deleted; /* Marks this face no longer used. */ +} CDTFace; + +typedef struct CDT_state { + LinkNode *edges; + LinkNode *faces; + CDTFace *outer_face; + CDTVert **vert_array; + int vert_array_len; + int vert_array_len_alloc; + double minx; + double miny; + double maxx; + double maxy; + double margin; + int visit_count; + int face_edge_offset; + RNG *rng; + MemArena *arena; + BLI_mempool *listpool; + double epsilon; + bool output_prepared; +} CDT_state; + +typedef struct LocateResult { + enum { OnVert, OnEdge, InFace } loc_kind; + SymEdge *se; + double edge_lambda; +} LocateResult; + +#define DLNY_ARENASIZE 1 << 14 + +/** + * This margin means that will only be a 1 degree possible + * concavity on outside if remove all border touching triangles. + */ +#define DLNY_MARGIN_PCT 2000.0 + +#ifdef DEBUG_CDT +# define F2(p) p[0], p[1] +static void dump_se(const SymEdge *se, const char *lab); +static void dump_v(const CDTVert *v, const char *lab); +static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit); +static void dump_id_list(const LinkNode *id_list, const char *lab); +static void dump_cdt(const CDT_state *cdt, const char *lab); +static void cdt_draw(CDT_state *cdt, const char *lab); +static void validate_face_centroid(SymEdge *se); +static void validate_cdt(CDT_state *cdt, bool check_all_tris); +#endif + +/* TODO: move these to BLI_vector... and BLI_math... */ +static double max_dd(const double a, const double b) +{ + return (a > b) ? a : b; +} + +static double len_v2v2_db(const double a[2], const double b[2]) +{ + return sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); +} + +static double len_squared_v2v2_db(const double a[2], const double b[2]) +{ + return (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]); +} + +static void add_v2_v2_db(double a[2], const double b[2]) +{ + a[0] += b[0]; + a[1] += b[1]; +} + +static void sub_v2_v2v2_db(double *a, const double *b, const double *c) +{ + a[0] = b[0] - c[0]; + a[1] = b[1] - c[1]; +} + +static double dot_v2v2_db(const double *a, const double *b) +{ + return a[0] * b[0] + a[1] * b[1]; +} + +static double closest_to_line_v2_db(double r_close[2], + const double p[2], + const double l1[2], + const double l2[2]) +{ + double h[2], u[2], lambda, denom; + sub_v2_v2v2_db(u, l2, l1); + sub_v2_v2v2_db(h, p, l1); + denom = dot_v2v2_db(u, u); + if (denom < DBL_EPSILON) { + r_close[0] = l1[0]; + r_close[1] = l1[1]; + return 0.0; + } + lambda = dot_v2v2_db(u, h) / dot_v2v2_db(u, u); + r_close[0] = l1[0] + u[0] * lambda; + r_close[1] = l1[1] + u[1] * lambda; + return lambda; +} + +/** + * If intersection == ISECT_LINE_LINE_CROSS or ISECT_LINE_LINE_NONE: + * <pre> + * pt = v1 + lamba * (v2 - v1) = v3 + mu * (v4 - v3) + * </pre> + */ +static int isect_seg_seg_v2_lambda_mu_db(const double v1[2], + const double v2[2], + const double v3[2], + const double v4[2], + double *r_lambda, + double *r_mu) +{ + double div, lambda, mu; + + div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]); + if (fabs(div) < DBL_EPSILON) { + return ISECT_LINE_LINE_COLINEAR; + } + + lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div; + + mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div; + + if (r_lambda) { + *r_lambda = lambda; + } + if (r_mu) { + *r_mu = mu; + } + + if (lambda >= 0.0 && lambda <= 1.0 && mu >= 0.0 && mu <= 1.0) { + if (lambda == 0.0 || lambda == 1.0 || mu == 0.0 || mu == 1.0) { + return ISECT_LINE_LINE_EXACT; + } + return ISECT_LINE_LINE_CROSS; + } + return ISECT_LINE_LINE_NONE; +} + +/** return 1 if a,b,c forms CCW angle, -1 if a CW angle, 0 if straight */ +static int CCW_test(const double a[2], const double b[2], const double c[2]) +{ + double det; + double ab; + + /* This is twice the signed area of triangle abc. */ + det = (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]); + ab = len_v2v2_db(a, b); + if (ab < DBL_EPSILON) { + return 0; + } + det /= ab; + if (det > DBL_EPSILON) { + return 1; + } + else if (det < -DBL_EPSILON) { + return -1; + } + return 0; +} + +/** return true if a -- b -- c are in that order, assuming they are on a straight line. */ +static bool in_line(const double a[2], const double b[2], const double c[2]) +{ + double dir_ab[2], dir_ac[2]; + + sub_v2_v2v2_db(dir_ab, a, b); + sub_v2_v2v2_db(dir_ac, a, c); + return dot_v2v2_db(dir_ab, dir_ac) >= 0.0; +} + +#ifndef NDEBUG +/** Is s2 reachable from s1 by next pointers with < limit hops? */ +static bool reachable(SymEdge *s1, SymEdge *s2, int limit) +{ + int count = 0; + for (SymEdge *s = s1; s && count < limit; s = s->next) { + if (s == s2) { + return true; + } + count++; + } + return false; +} +#endif + +static void calc_face_centroid(SymEdge *se) +{ + SymEdge *senext; + double *centroidp = se->face->centroid; + int count; + copy_v2_v2_db(centroidp, se->vert->co); + count = 1; + for (senext = se->next; senext != se; senext = senext->next) { + add_v2_v2_db(centroidp, senext->vert->co); + count++; + } + centroidp[0] /= count; + centroidp[1] /= count; +} + +/** Using array to store these instead of linked list so can make a random selection from them. */ +static CDTVert *add_cdtvert(CDT_state *cdt, double x, double y) +{ + CDTVert *v = BLI_memarena_alloc(cdt->arena, sizeof(*v)); + v->co[0] = x; + v->co[1] = y; + v->input_ids = NULL; + v->symedge = NULL; + if (cdt->vert_array_len == cdt->vert_array_len_alloc) { + CDTVert **old_array = cdt->vert_array; + cdt->vert_array_len_alloc *= 4; + cdt->vert_array = BLI_memarena_alloc(cdt->arena, + cdt->vert_array_len_alloc * sizeof(cdt->vert_array[0])); + memmove(cdt->vert_array, old_array, cdt->vert_array_len * sizeof(cdt->vert_array[0])); + } + BLI_assert(cdt->vert_array_len < cdt->vert_array_len_alloc); + v->index = cdt->vert_array_len; + cdt->vert_array[cdt->vert_array_len++] = v; + return v; +} + +static CDTEdge *add_cdtedge( + CDT_state *cdt, CDTVert *v1, CDTVert *v2, CDTFace *fleft, CDTFace *fright) +{ + CDTEdge *e = BLI_memarena_alloc(cdt->arena, sizeof(*e)); + SymEdge *se = &e->symedges[0]; + SymEdge *sesym = &e->symedges[1]; + e->input_ids = NULL; + BLI_linklist_prepend_arena(&cdt->edges, (void *)e, cdt->arena); + se->edge = sesym->edge = e; + se->face = fleft; + sesym->face = fright; + se->vert = v1; + if (v1->symedge == NULL) { + v1->symedge = se; + } + sesym->vert = v2; + if (v2->symedge == NULL) { + v2->symedge = sesym; + } + se->next = sesym->next = se->rot = sesym->rot = NULL; + return e; +} + +static CDTFace *add_cdtface(CDT_state *cdt) +{ + CDTFace *f = BLI_memarena_alloc(cdt->arena, sizeof(*f)); + f->visit_index = 0; + f->deleted = false; + f->symedge = NULL; + f->input_ids = NULL; + BLI_linklist_prepend_arena(&cdt->faces, (void *)f, cdt->arena); + return f; +} + +static bool id_in_list(const LinkNode *id_list, int id) +{ + const LinkNode *ln; + + for (ln = id_list; ln; ln = ln->next) { + if (POINTER_AS_INT(ln->link) == id) { + return true; + } + } + return false; +} + +/** is any id in (range_start, range_start+1, ... , range_end) in id_list? */ +static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end) +{ + const LinkNode *ln; + int id; + + for (ln = id_list; ln; ln = ln->next) { + id = POINTER_AS_INT(ln->link); + if (id >= range_start && id <= range_end) { + return true; + } + } + return false; +} + +static void add_to_input_ids(LinkNode **dst, int input_id, CDT_state *cdt) +{ + if (!id_in_list(*dst, input_id)) { + BLI_linklist_prepend_arena(dst, POINTER_FROM_INT(input_id), cdt->arena); + } +} + +static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src, CDT_state *cdt) +{ + const LinkNode *ln; + + for (ln = src; ln; ln = ln->next) { + add_to_input_ids(dst, POINTER_AS_INT(ln->link), cdt); + } +} + +/** Return other #SymEdge for same #CDTEdge as se. */ +static inline SymEdge *sym(const SymEdge *se) +{ + return se->next->rot; +} + +/** Return SymEdge whose next is se. */ +static inline SymEdge *prev(const SymEdge *se) +{ + return se->rot->next->rot; +} + +static inline bool is_border_edge(const CDTEdge *e, const CDT_state *cdt) +{ + return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face; +} + +/** Does one edge of this edge touch the frame? */ +static bool edge_touches_frame(const CDTEdge *e) +{ + return e->symedges[0].vert->index < 4 || e->symedges[1].vert->index < 4; +} + +static inline bool is_constrained_edge(const CDTEdge *e) +{ + return e->input_ids != NULL; +} + +static inline bool is_deleted_edge(const CDTEdge *e) +{ + return e->symedges[0].next == NULL; +} + +/** Is there already an edge between a and b? */ +static bool exists_edge(const CDTVert *a, const CDTVert *b) +{ + SymEdge *se, *ss; + se = a->symedge; + if (se->next->vert == b) { + return true; + } + for (ss = se->rot; ss != se; ss = ss->rot) { + if (ss->next->vert == b) { + return true; + } + } + return false; +} + +/** + * Assume s1 and s2 are both SymEdges in a face with > 3 sides, + * and one is not the next of the other. + * Add an edge from s1->v to s2->v, splitting the face in two. + * The original face will continue to be associated with the subface + * that has s1, and a new face will be made for s2's new face. + * The centroids of both faces are recalculated. + * Return the new diagonal's CDTEdge *. + */ +static CDTEdge *add_diagonal(CDT_state *cdt, SymEdge *s1, SymEdge *s2) +{ + CDTEdge *ediag; + CDTFace *fold, *fnew; + SymEdge *sdiag, *sdiagsym; + SymEdge *s1prev, *s1prevsym, *s2prev, *s2prevsym, *se; + BLI_assert(reachable(s1, s2, 20)); + BLI_assert(reachable(s2, s1, 20)); + fold = s1->face; + fnew = add_cdtface(cdt); + s1prev = prev(s1); + s1prevsym = sym(s1prev); + s2prev = prev(s2); + s2prevsym = sym(s2prev); + ediag = add_cdtedge(cdt, s1->vert, s2->vert, fnew, fold); + sdiag = &ediag->symedges[0]; + sdiagsym = &ediag->symedges[1]; + sdiag->next = s2; + sdiagsym->next = s1; + s2prev->next = sdiagsym; + s1prev->next = sdiag; + s1->rot = sdiag; + sdiag->rot = s1prevsym; + s2->rot = sdiagsym; + sdiagsym->rot = s2prevsym; +#ifdef DEBUG_CDT + BLI_assert(reachable(s2, sdiag, 20)); +#endif + for (se = s2; se != sdiag; se = se->next) { + se->face = fnew; + } + add_list_to_input_ids(&fnew->input_ids, fold->input_ids, cdt); + calc_face_centroid(sdiag); + calc_face_centroid(sdiagsym); + return ediag; +} + +/** + * Split \a se at fraction \a lambda, + * and return the new #CDTEdge that is the new second half. + * Copy the edge input_ids into the new one. + */ +static CDTEdge *split_edge(CDT_state *cdt, SymEdge *se, double lambda) +{ + const double *a, *b; + double p[2]; + CDTVert *v; + CDTEdge *e; + SymEdge *sesym, *newse, *newsesym, *senext, *sesymprev, *sesymprevsym; + /* Split e at lambda. */ + a = se->vert->co; + b = se->next->vert->co; + sesym = sym(se); + sesymprev = prev(sesym); + sesymprevsym = sym(sesymprev); + senext = se->next; + p[0] = (1.0 - lambda) * a[0] + lambda * b[0]; + p[1] = (1.0 - lambda) * a[1] + lambda * b[1]; + v = add_cdtvert(cdt, p[0], p[1]); + e = add_cdtedge(cdt, v, se->next->vert, se->face, sesym->face); + sesym->vert = v; + newse = &e->symedges[0]; + newsesym = &e->symedges[1]; + se->next = newse; + newsesym->next = sesym; + newse->next = senext; + newse->rot = sesym; + sesym->rot = newse; + senext->rot = newsesym; + newsesym->rot = sesymprevsym; + sesymprev->next = newsesym; + if (newsesym->vert->symedge == sesym) { + newsesym->vert->symedge = newsesym; + } + add_list_to_input_ids(&e->input_ids, se->edge->input_ids, cdt); + calc_face_centroid(se); + calc_face_centroid(sesym); + return e; +} + +/** + * Delete an edge from the structure. The new combined face on either side of + * the deleted edge will be the one that was e's face; the centroid is updated. + * There will be now an unused face, marked by setting its deleted flag, + * and an unused #CDTEdge, marked by setting the next and rot pointers of + * its SymEdges to NULL. + * <pre> + * . v2 . + * / \ / \ + * /f|j\ / \ + * / | \ / \ + * | + * A | B A + * \ e| / \ / + * \ | / \ / + * \h|i/ \ / + * . v1 . + * </pre> + * Also handle variant cases where one or both ends + * are attached only to e. + */ +static void delete_edge(CDT_state *cdt, SymEdge *e) +{ + SymEdge *esym, *f, *h, *i, *j, *k, *jsym, *hsym; + CDTFace *aface, *bface; + CDTVert *v1, *v2; + bool v1_isolated, v2_isolated; + + esym = sym(e); + v1 = e->vert; + v2 = esym->vert; + aface = e->face; + bface = esym->face; + f = e->next; + h = prev(e); + i = esym->next; + j = prev(esym); + jsym = sym(j); + hsym = sym(h); + v1_isolated = (i == e); + v2_isolated = (f == esym); + + if (!v1_isolated) { + h->next = i; + i->rot = hsym; + } + if (!v2_isolated) { + j->next = f; + f->rot = jsym; + } + if (!v1_isolated && !v2_isolated && aface != bface) { + for (k = i; k != f; k = k->next) { + k->face = aface; + } + } + + /* If e was representative symedge for v1 or v2, fix that. */ + if (v1_isolated) { + v1->symedge = NULL; + } + else if (v1->symedge == e) { + v1->symedge = i; + } + if (v2_isolated) { + v2->symedge = NULL; + } + else if (v2->symedge == esym) { + v2->symedge = f; + } + + /* Mark SymEdge as deleted by setting all its pointers to NULL. */ + e->next = e->rot = NULL; + esym->next = esym->rot = NULL; + if (!v1_isolated && !v2_isolated && aface != bface) { + bface->deleted = true; + if (cdt->outer_face == bface) { + cdt->outer_face = aface; + } + } + if (aface != cdt->outer_face) { + calc_face_centroid(f); + } +} + +/** + * The initial structure will be the rectangle with opposite corners (minx,miny) + * and (maxx,maxy), and a diagonal going between those two corners. + * We keep track of the outer face (surrounding the entire structure; its boundary + * is the clockwise traversal of the bounding box rectangle initially) in cdt->outer_face. + * + * The vertices are kept as pointers in an array (which may need to be reallocated from + * time to time); the edges and faces are kept in lists. Sometimes edges and faces are deleted, + * marked by setting all pointers to NULL (for edges), or setting the deleted flag to true (for + * faces). + * + * A #MemArena is allocated to do all allocations from except for link list nodes; a listpool + * is created for link list node allocations. + * + * The epsilon argument is stored and used in "near enough" distance calculations. + * + * When done, caller must call BLI_constrained_delaunay_free to free + * the memory used by the returned #CDT_state. + */ +static CDT_state *cdt_init(double minx, double maxx, double miny, double maxy, double epsilon) +{ + double x0, x1, y0, y1; + double margin; + CDTVert *v[4]; + CDTEdge *e[4]; + CDTFace *f0, *fouter; + int i, inext, iprev; + MemArena *arena = BLI_memarena_new(DLNY_ARENASIZE, __func__); + CDT_state *cdt = BLI_memarena_alloc(arena, sizeof(CDT_state)); + cdt->edges = NULL; + cdt->faces = NULL; + cdt->vert_array_len = 0; + cdt->vert_array_len_alloc = 32; + cdt->vert_array = BLI_memarena_alloc(arena, + cdt->vert_array_len_alloc * sizeof(*cdt->vert_array)); + cdt->minx = minx; + cdt->miny = miny; + cdt->maxx = maxx; + cdt->maxy = maxy; + cdt->arena = arena; + cdt->listpool = BLI_mempool_create(sizeof(LinkNode), 128, 128, 0); + cdt->rng = BLI_rng_new(0); + cdt->epsilon = epsilon; + + /* Expand bounding box a bit and make initial CDT from it. */ + margin = DLNY_MARGIN_PCT * max_dd(maxx - minx, maxy - miny) / 100.0; + if (margin <= 0.0) { + margin = 1.0; + } + if (margin < epsilon) { + margin = 4 * epsilon; /* Make sure constraint verts don't merge with border verts. */ + } + cdt->margin = margin; + x0 = minx - margin; + y0 = miny - margin; + x1 = maxx + margin; + y1 = maxy + margin; + + /* Make a quad, then split it with a diagonal. */ + v[0] = add_cdtvert(cdt, x0, y0); + v[1] = add_cdtvert(cdt, x1, y0); + v[2] = add_cdtvert(cdt, x1, y1); + v[3] = add_cdtvert(cdt, x0, y1); + cdt->outer_face = fouter = add_cdtface(cdt); + f0 = add_cdtface(cdt); + for (i = 0; i < 4; i++) { + e[i] = add_cdtedge(cdt, v[i], v[(i + 1) % 4], f0, fouter); + } + for (i = 0; i < 4; i++) { + inext = (i + 1) % 4; + iprev = (i + 3) % 4; + e[i]->symedges[0].next = &e[inext]->symedges[0]; + e[inext]->symedges[1].next = &e[i]->symedges[1]; + e[i]->symedges[0].rot = &e[iprev]->symedges[1]; + e[iprev]->symedges[1].rot = &e[i]->symedges[0]; + } + calc_face_centroid(&e[0]->symedges[0]); + add_diagonal(cdt, &e[0]->symedges[0], &e[2]->symedges[0]); + fouter->centroid[0] = fouter->centroid[1] = 0.0; + + cdt->visit_count = 0; + cdt->output_prepared = false; + cdt->face_edge_offset = 0; + return cdt; +} + +static void cdt_free(CDT_state *cdt) +{ + BLI_rng_free(cdt->rng); + BLI_mempool_destroy(cdt->listpool); + BLI_memarena_free(cdt->arena); +} + +static bool locate_point_final(const double p[2], + SymEdge *tri_se, + bool try_neighbors, + const double epsilon, + LocateResult *r_lr) +{ + /* 'p' should be in or on our just outside of 'cur_tri'. */ + double dist_inside[3]; + int i; + SymEdge *se; + const double *a, *b; + double lambda, close[2]; + bool done = false; +#ifdef DEBUG_CDT + int dbglevel = 0; + if (dbglevel > 0) { + fprintf(stderr, "locate_point_final %d\n", try_neighbors); + dump_se(tri_se, "tri_se"); + fprintf(stderr, "\n"); + } +#endif + se = tri_se; + i = 0; + do { +#ifdef DEBUG_CDT + if (dbglevel > 1) { + fprintf(stderr, "%d: ", i); + dump_se(se, "search se"); + } +#endif + a = se->vert->co; + b = se->next->vert->co; + lambda = closest_to_line_v2_db(close, p, a, b); + double len_close_p = len_v2v2_db(close, p); + if (len_close_p < epsilon) { + if (len_v2v2_db(p, a) < epsilon) { +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "OnVert case a (%.2f,%.2f)\n", F2(a)); + } +#endif + r_lr->loc_kind = OnVert; + r_lr->se = se; + r_lr->edge_lambda = 0.0; + done = true; + } + else if (len_v2v2_db(p, b) < epsilon) { +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "OnVert case b (%.2f,%.2f)\n", F2(b)); + } +#endif + r_lr->loc_kind = OnVert; + r_lr->se = se->next; + r_lr->edge_lambda = 0.0; + done = true; + } + else if (lambda > 0.0 && lambda < 1.0) { +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "OnEdge case, lambda=%f\n", lambda); + dump_se(se, "se"); + } +#endif + r_lr->loc_kind = OnEdge; + r_lr->se = se; + r_lr->edge_lambda = lambda; + done = true; + } + } + else { + dist_inside[i] = len_close_p; + dist_inside[i] = CCW_test(a, b, p) >= 0 ? len_close_p : -len_close_p; + } + i++; + se = se->next; + } while (se != tri_se && !done); + if (!done) { +#ifdef DEBUG_CDT + if (dbglevel > 1) { + fprintf(stderr, + "not done, dist_inside=%f %f %f\n", + dist_inside[0], + dist_inside[1], + dist_inside[2]); + } +#endif + if (dist_inside[0] >= 0.0 && dist_inside[1] >= 0.0 && dist_inside[2] >= 0.0) { +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "InFace case\n"); + dump_se_cycle(tri_se, "tri", 10); + } +#endif + r_lr->loc_kind = InFace; + r_lr->se = tri_se; + r_lr->edge_lambda = 0.0; + done = true; + } + else if (try_neighbors) { + for (se = tri_se->next; se != tri_se; se = se->next) { + if (locate_point_final(p, se, false, epsilon, r_lr)) { + done = true; + break; + } + } + if (!done) { + /* Shouldn't happen desperation mode: pick something. */ + se = NULL; + if (dist_inside[0] > 0) { + se = tri_se; + } + if (dist_inside[1] > 0 && (se == NULL || dist_inside[1] < dist_inside[i])) { + se = tri_se->next; + } + if (se == NULL) { + se = tri_se->next->next; + } + a = se->vert->co; + b = se->next->vert->co; + lambda = closest_to_line_v2_db(close, p, a, b); + if (lambda <= 0.0) { + r_lr->loc_kind = OnVert; + r_lr->se = se; + r_lr->edge_lambda = 0.0; + } + else if (lambda >= 1.0) { + r_lr->loc_kind = OnVert; + r_lr->se = se->next; + r_lr->edge_lambda = 0.0; + } + else { + r_lr->loc_kind = OnEdge; + r_lr->se = se->next; + r_lr->edge_lambda = lambda; + } +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf( + stderr, "desperation case kind=%u lambda=%f\n", r_lr->loc_kind, r_lr->edge_lambda); + dump_se(r_lr->se, "se"); + BLI_assert(0); /* While developing, catch these "should not happens" */ + } +#endif + fprintf(stderr, "desperation!\n"); // TODO: remove + return true; + } + } + } + return done; +} + +static LocateResult locate_point(CDT_state *cdt, const double p[2]) +{ + LocateResult lr; + SymEdge *cur_se, *next_se, *next_se_sym; + CDTFace *cur_tri; + bool done; + int sample_n, i, k; + CDTVert *v, *best_start_vert; + double dist_squared, best_dist_squared; + double *a, *b, *c; + const double epsilon = cdt->epsilon; + int visit = ++cdt->visit_count; + int loop_count = 0; +#ifdef DEBUG_CDT + int dbglevel = 0; + + if (dbglevel > 0) { + fprintf(stderr, "locate_point (%.2f,%.2f), visit_index=%d\n", F2(p), visit); + } +#endif + /* Starting point determined by closest to p in an n ** (1/3) sized sample of current points. */ + BLI_assert(cdt->vert_array_len > 0); + sample_n = (int)round(pow((double)cdt->vert_array_len, 0.33333)); + if (sample_n < 1) { + sample_n = 1; + } + best_start_vert = NULL; + best_dist_squared = DBL_MAX; + for (k = 0; k < sample_n; k++) { + /* Yes, this may try some i's more than once, + * but will still get about an n ** (1/3) size sample. */ + i = (int)(BLI_rng_get_uint(cdt->rng) % cdt->vert_array_len); + v = cdt->vert_array[i]; + dist_squared = len_squared_v2v2_db(p, v->co); +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "try start vert %d, dist_squared=%f\n", i, dist_squared); + dump_v(v, "v"); + } +#endif + if (dist_squared < best_dist_squared) { + best_dist_squared = dist_squared; + best_start_vert = v; + } + } + cur_se = &best_start_vert->symedge[0]; + if (cur_se->face == cdt->outer_face) { + cur_se = cur_se->rot; + BLI_assert(cur_se->face != cdt->outer_face); + } +#ifdef DEBUG_CDT + if (dbglevel > 0) { + dump_se(cur_se, "start vert edge"); + } +#endif + done = false; + while (!done) { + /* Find edge of cur_tri that separates p and t's centroid, + * and where other tri over the edge is unvisited. */ +#ifdef DEBUG_CDT + if (dbglevel > 0) { + dump_se_cycle(cur_se, "cur search face", 5); + } +#endif + cur_tri = cur_se->face; + BLI_assert(cur_tri != cdt->outer_face); + cur_tri->visit_index = visit; + /* Is p in or on current triangle? */ + a = cur_se->vert->co; + b = cur_se->next->vert->co; + c = cur_se->next->next->vert->co; + if (CCW_test(a, b, p) >= 0 && CCW_test(b, c, p) >= 0 && CCW_test(c, a, p) >= 0) { +#ifdef DEBUG_CDT + if (dbglevel > 1) { + fprintf(stderr, "p in current triangle\n"); + } +#endif + done = locate_point_final(p, cur_se, false, epsilon, &lr); + BLI_assert(done == true); + break; + } + bool found_next = false; + next_se = cur_se; + do { + a = next_se->vert->co; + b = next_se->next->vert->co; + c = next_se->next->next->vert->co; +#ifdef DEBUG_CDT + if (dbglevel > 1) { + dump_se(next_se, "search edge"); + fprintf(stderr, "tri centroid=(%.2f,%.2f)\n", F2(cur_tri->centroid)); + validate_face_centroid(next_se); + } +#endif + next_se_sym = sym(next_se); + if (CCW_test(a, b, p) <= 0 && next_se->face != cdt->outer_face) { +#ifdef DEBUG_CDT + if (dbglevel > 1) { + fprintf(stderr, "CCW_test(a, b, p) <= 0\n"); + } +#endif +#ifdef DEBUG_CDT + if (dbglevel > 0) { + dump_se(next_se_sym, "next_se_sym"); + fprintf(stderr, "next_se_sym face visit=%d\n", next_se_sym->face->visit_index); + } +#endif + if (next_se_sym->face->visit_index != visit) { +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "found edge to cross\n"); + } +#endif + found_next = true; + cur_se = next_se_sym; + break; + } + } + next_se = next_se->next; + } while (next_se != cur_se); + if (!found_next) { + done = locate_point_final(p, cur_se, true, epsilon, &lr); + BLI_assert(done = true); + done = true; + } + if (++loop_count > 1000000) { + fprintf(stderr, "infinite search loop?\n"); + done = locate_point_final(p, cur_se, true, epsilon, &lr); + } + } + + return lr; +} + +/** return true if circumcircle(v1, v2, v3) does not contain p. */ +static bool delaunay_check(CDTVert *v1, CDTVert *v2, CDTVert *v3, CDTVert *p, const double epsilon) +{ + double a, b, c, d, z1, z2, z3; + const double *p1, *p2, *p3; + double cen[2], r, len_pc; + /* To do epislon test, need center and radius of circumcircle. */ + p1 = v1->co; + p2 = v2->co; + p3 = v3->co; + z1 = dot_v2v2_db(p1, p1); + z2 = dot_v2v2_db(p2, p2); + z3 = dot_v2v2_db(p3, p3); + a = p1[0] * (p2[1] - p3[1]) - p1[1] * (p2[0] - p3[0]) + p2[0] * p3[1] - p3[0] * p2[1]; + b = z1 * (p3[1] - p2[1]) + z2 * (p1[1] - p3[1]) + z3 * (p2[1] - p1[1]); + c = z1 * (p2[0] - p3[0]) + z2 * (p3[0] - p1[0]) + z3 * (p1[0] - p2[0]); + d = z1 * (p3[0] * p2[1] - p2[0] * p3[1]) + z2 * (p1[0] * p3[1] - p3[0] * p1[1]) + + z3 * (p2[0] * p1[1] - p1[0] * p2[1]); + if (a == 0.0) { + return true; /* Not really, but this shouldn't happen. */ + } + cen[0] = -b / (2 * a); + cen[1] = -c / (2 * a); + r = sqrt((b * b + c * c - 4 * a * d) / (4 * a * a)); + len_pc = len_v2v2_db(p->co, cen); + return (len_pc >= (r - epsilon)); +} + +/** Use LinkNode linked list as stack of SymEdges, allocating from cdt->listpool. */ +typedef LinkNode *Stack; + +static inline void push(Stack *stack, SymEdge *se, CDT_state *cdt) +{ + BLI_linklist_prepend_pool(stack, se, cdt->listpool); +} + +static inline SymEdge *pop(Stack *stack, CDT_state *cdt) +{ + return (SymEdge *)BLI_linklist_pop_pool(stack, cdt->listpool); +} + +static inline bool is_empty(Stack *stack) +{ + return *stack == NULL; +} + +/** + * <pre> + * /\ /\ + * /a|\ / \ + * / | sesym / \ + * / | \ / \ + * . b | d . -> . se______ + * \ se| / \ / + * \ |c/ \ / + * \ |/ \ / + * </pre> + */ +static void flip(SymEdge *se, CDT_state *cdt) +{ + SymEdge *a, *b, *c, *d; + SymEdge *sesym, *asym, *bsym, *csym, *dsym; + CDTFace *t1, *t2; + CDTVert *v1, *v2; +#ifdef DEBUG_CDT + const int dbglevel = 0; +#endif + + sesym = sym(se); +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "flip\n"); + dump_se(se, "se"); + dump_se(sesym, "sesym"); + } +#endif + a = se->next; + b = a->next; + c = sesym->next; + d = c->next; + asym = sym(a); + bsym = sym(b); + csym = sym(c); + dsym = sym(d); +#ifdef DEBUG_CDT + if (dbglevel > 1) { + dump_se(a, "a"); + dump_se(b, "b"); + dump_se(c, "c"); + dump_se(d, "d"); + } +#endif + v1 = se->vert; + v2 = sesym->vert; + t1 = a->face; + t2 = c->face; + + se->vert = b->vert; + sesym->vert = d->vert; + + a->next = se; + se->next = d; + d->next = a; + + sesym->next = b; + b->next = c; + c->next = sesym; + + a->rot = dsym; + b->rot = se; + se->rot = asym; + + c->rot = bsym; + d->rot = sesym; + sesym->rot = csym; + + a->face = se->face = d->face = t1; + sesym->face = b->face = c->face = t2; + + if (v1->symedge == se) { + v1->symedge = c; + } + if (v2->symedge == sesym) { + v2->symedge = a; + } + + calc_face_centroid(a); + calc_face_centroid(sesym); + +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "after flip\n"); + dump_se_cycle(a, "a cycle", 5); + dump_se_cycle(sesym, "sesym cycle", 5); + } +#endif + if (cdt) { + /* Pass. */ + } +} + +static void flip_edges(CDTVert *v, Stack *stack, CDT_state *cdt) +{ + SymEdge *se, *sesym; + CDTVert *a, *b, *c, *d; + SymEdge *tri_without_p; + bool is_delaunay; + const double epsilon = cdt->epsilon; + int count = 0; +#ifdef DEBUG_CDT + const int dbglevel = 0; + if (dbglevel > 0) { + fprintf(stderr, "flip_edges, v=(%.2f,%.2f)\n", F2(v->co)); + } +#endif + while (!is_empty(stack)) { + if (++count > 10000) { + fprintf(stderr, "infinite flip loop?\n"); + return; + } + se = pop(stack, cdt); +#ifdef DEBUG_CDT + if (dbglevel > 0) { + dump_se(se, "flip_edges popped"); + } +#endif + if (!is_constrained_edge(se->edge)) { + /* Edge is not constrained; is it Delaunay? */ +#ifdef DEBUG_CDT + if (dbglevel > 1) { + dump_se_cycle(se, "unconstrained edge", 5); + } + else if (dbglevel > 0) { + fprintf(stderr, "unconstrained edge\n"); + } +#endif + a = se->vert; + b = se->next->vert; + c = se->next->next->vert; + sesym = sym(se); + d = sesym->next->next->vert; +#ifdef DEBUG_CDT + if (dbglevel > 1) { + fprintf(stderr, "a=(%.2f,%.2f) b=(%.2f,%.2f)\n", F2(a->co), F2(b->co)); + fprintf(stderr, "c=(%.2f,%.2f) d=(%.2f,%.2f)\n", F2(c->co), F2(d->co)); + } +#endif + if (v == c) { + tri_without_p = sesym; + is_delaunay = delaunay_check(a, b, c, d, epsilon); +#ifdef DEBUG_CDT + if (dbglevel > 1) { + fprintf(stderr, "v==c, delaunay(a,b,c,d)=%d\n", is_delaunay); + } +#endif + } + else { + tri_without_p = se; + BLI_assert(d == v); + is_delaunay = delaunay_check(b, a, d, c, epsilon); +#ifdef DEBUG_CDT + if (dbglevel > 1) { + fprintf(stderr, "v!=c, delaunay(b,a,d,c)=%d\n", is_delaunay); + } +#endif + } + if (!is_delaunay) { + /* Push two edges of tri without p that aren't se. */ +#ifdef DEBUG_CDT + if (dbglevel > 0) { + fprintf(stderr, "maybe pushing more edges\n"); + } +#endif + if (!is_border_edge(tri_without_p->next->edge, cdt)) { +#ifdef DEBUG_CDT + if (dbglevel > 0) { + dump_se(tri_without_p->next, "push1"); + } +#endif + push(stack, tri_without_p->next, cdt); + } + if (!is_border_edge(tri_without_p->next->next->edge, cdt)) { +#ifdef DEBUG_CDT + if (dbglevel > 0) { + dump_se(tri_without_p->next->next, "\npush2"); + } +#endif + push(stack, tri_without_p->next->next, cdt); + } + flip(se, cdt); + } + } + } +} + +/** + * Splits e at lambda and returns a #SymEdge with new vert as its vert. + * The two opposite triangle vertices to e are connect to new point. + * <pre> + * /\ /\ + * /f|\ / |\ + * / |j\ / | \ + * / | i\ / k| \ + * . | . -> . l_ p m_. + * \g | / \ | / + * \ |h/ \ | / + * \e|/ \ e|/ + * + * t1 = {e, f, g}; t2 = {h, i, j}; + * t1' = {e, l.sym, g}; t2' = {h, m.sym, e'.sym} + * t3 = {k, f, l}; t4 = {m, i, j} + * </pre> + */ +static CDTVert *insert_point_in_edge(CDT_state *cdt, SymEdge *e, double lambda) +{ + SymEdge *f, *g, *h, *i, *j, *k; + CDTEdge *ke; + CDTVert *p; + Stack stack; + /* Split e at lambda. */ + + f = e->next; + g = f->next; + BLI_assert(g->next == e); + j = sym(e); + h = j->next; + i = h->next; + BLI_assert(i->next == j); + + ke = split_edge(cdt, e, lambda); + k = &ke->symedges[0]; + p = k->vert; + + add_diagonal(cdt, g, k); + add_diagonal(cdt, sym(e), i); + + stack = NULL; + if (!is_border_edge(f->edge, cdt)) { + push(&stack, f, cdt); + } + if (!is_border_edge(g->edge, cdt)) { + push(&stack, g, cdt); + } + if (!is_border_edge(h->edge, cdt)) { + push(&stack, h, cdt); + } + if (!is_border_edge(i->edge, cdt)) { + push(&stack, i, cdt); + } + flip_edges(k->vert, &stack, cdt); + return p; +} + +/** + * Inserts p inside e's triangle and connects the three cornders + * of the triangle to the new point. Returns a SymEdge that has + * new point as its point. + * <pre> + * * * + * *g * * .j* + * * * * . * + * * p * -> * 1. p . 3* + * * * * . . * + * * e f* * . h 2 i . * + * * * * * * * * * * * * * * * * * * * * * * * * * * * + * </pre> + */ +static CDTVert *insert_point_in_face(CDT_state *cdt, SymEdge *e, const double p[2]) +{ + SymEdge *f, *g, *h, *i, *j; + SymEdge *esym, *fsym, *gsym, *hsym, *isym, *jsym; + CDTVert *v; + CDTEdge *he, *ie, *je; + CDTFace *t1, *t2, *t3; + Stack stack; + + f = e->next; + g = f->next; + esym = sym(e); + fsym = sym(f); + gsym = sym(g); + t1 = e->face; + t2 = add_cdtface(cdt); + t3 = add_cdtface(cdt); + + v = add_cdtvert(cdt, p[0], p[1]); + he = add_cdtedge(cdt, e->vert, v, t1, t2); + h = &he->symedges[0]; + hsym = &he->symedges[1]; + ie = add_cdtedge(cdt, f->vert, v, t2, t3); + i = &ie->symedges[0]; + isym = &ie->symedges[1]; + je = add_cdtedge(cdt, g->vert, v, t3, t1); + j = &je->symedges[0]; + jsym = &je->symedges[1]; + + e->next = i; + i->next = hsym; + hsym->next = e; + e->face = t2; + + f->next = j; + j->next = isym; + isym->next = f; + f->face = t3; + + g->next = h; + h->next = jsym; + jsym->next = g; + g->face = t1; + + e->rot = h; + i->rot = esym; + hsym->rot = isym; + + f->rot = i; + j->rot = fsym; + isym->rot = jsym; + + g->rot = j; + h->rot = gsym; + jsym->rot = hsym; + + calc_face_centroid(e); + calc_face_centroid(f); + calc_face_centroid(g); + + stack = NULL; + if (!is_border_edge(e->edge, cdt)) { + push(&stack, e, cdt); + } + if (!is_border_edge(f->edge, cdt)) { + push(&stack, f, cdt); + } + if (!is_border_edge(g->edge, cdt)) { + push(&stack, g, cdt); + } + flip_edges(v, &stack, cdt); + + return v; +} + +/** + * Re-triangulates, assuring constrained delaunay condition, + * the pseudo-polygon that cycles from se. + * "pseudo" because a vertex may be repeated. + * See Anglada paper, "An Improved incremental algorithm + * for constructing restricted Delaunay triangulations". + */ +static void re_delaunay_triangulate(CDT_state *cdt, SymEdge *se) +{ + SymEdge *ss, *first, *cse; + CDTVert *a, *b, *c, *v; + CDTEdge *ebc, *eca; + const double epsilon = cdt->epsilon; + int count; +#ifdef DEBUG_CDT + SymEdge *last; + const int dbg_level = 0; + + if (dbg_level > 0) { + fprintf(stderr, "retriangulate"); + dump_se_cycle(se, "poly ", 1000); + } +#endif + /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */ + count = 1; + for (ss = se->next; ss != se; ss = ss->next) { + count++; + } + if (count <= 3) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "nothing to do\n"); + } +#endif + return; + } + /* First and last are the SymEdges whose verts are first and last off of base, + * continuing from 'se'. */ + first = se->next->next; + /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */ + a = se->vert; + b = se->next->vert; + c = first->vert; + cse = first; +#ifdef DEBUG_CDT + last = prev(se); + if (dbg_level > 1) { + dump_se(first, "first"); + dump_se(last, "last"); + dump_v(a, "a"); + dump_v(b, "b"); + dump_v(c, "c"); + } +#endif + for (ss = first->next; ss != se; ss = ss->next) { + v = ss->vert; + if (!delaunay_check(a, b, c, v, epsilon)) { + c = v; + cse = ss; +#ifdef DEBUG_CDT + if (dbg_level > 1) { + dump_v(c, "new c "); + } +#endif + } + } + /* Add diagonals necessary to make abc a triangle. */ +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "make triangle abc exist where\n"); + dump_v(a, " a"); + dump_v(b, " b"); + dump_v(c, " c"); + } +#endif + ebc = NULL; + eca = NULL; + if (!exists_edge(b, c)) { + ebc = add_diagonal(cdt, se->next, cse); +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "added edge ebc\n"); + dump_se(&ebc->symedges[0], " ebc"); + } +#endif + } + if (!exists_edge(c, a)) { + eca = add_diagonal(cdt, cse, se); +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "added edge eca\n"); + dump_se(&eca->symedges[0], " eca"); + } +#endif + } + /* Now recurse. */ + if (ebc) { + re_delaunay_triangulate(cdt, &ebc->symedges[1]); + } + if (eca) { + re_delaunay_triangulate(cdt, &eca->symedges[1]); + } +} + +/** + * Add a constrained point to cdt structure, and return the corresponding CDTVert*. + * May not be at exact coords given, because it can be merged with an existing vertex + * or moved to an existing edge (which could be a triangulation edge, not just a constraint one) + * if the point is within cdt->epsilon of those other elements. + * + * input_id will be added to the list of input_ids for the returned CDTVert (don't use -1 for id). + * + * Assumes cdt has been initialized, with min/max bounds that contain coords. + * Assumes that #BLI_constrained_delaunay_get_output has not been called yet. + */ +static CDTVert *add_point_constraint(CDT_state *cdt, const double coords[2], int input_id) +{ + LocateResult lr; + CDTVert *v; +#ifdef DEBUG_CDT + const int dbg_level = 0; +#endif + + BLI_assert(!cdt->output_prepared); +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "add point constraint (%.3f,%.3f), id=%d\n", F2(coords), input_id); + } +#endif + lr = locate_point(cdt, coords); +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, " locate result has loc_kind %u\n", lr.loc_kind); + } +#endif + if (lr.loc_kind == OnVert) { + v = lr.se->vert; + } + else if (lr.loc_kind == OnEdge) { + v = insert_point_in_edge(cdt, lr.se, lr.edge_lambda); + } + else { + v = insert_point_in_face(cdt, lr.se, coords); + } + add_to_input_ids(&v->input_ids, input_id, cdt); + return v; +} + +/** + * Add a constrained edge between v1 and v2 to cdt structure. + * This may result in a number of #CDTEdges created, due to intersections + * and partial overlaps with existing cdt vertices and edges. + * Each created #CDTEdge will have input_id added to its input_ids list. + * + * If \a r_edges is not NULL, the #CDTEdges generated or found that go from + * v1 to v2 are put into that linked list, in order. + * + * Assumes that #BLI_constrained_delaunay_get_output has not been called yet. + */ +static void add_edge_constraint( + CDT_state *cdt, CDTVert *v1, CDTVert *v2, int input_id, LinkNode **r_edges) +{ + CDTVert *va, *vb, *vc; + SymEdge *vse1; +#ifdef DEBUG_CDT + SymEdge *vse2; +#endif + SymEdge *t, *tstart, *tout, *tnext; + SymEdge *se; + CDTEdge *edge; + int ccw1, ccw2, isect; + int i, search_count; + double lambda; + bool done, state_through_vert; + LinkNodePair edge_list = {NULL, NULL}; + typedef struct CrossData { + double lambda; + CDTVert *vert; + SymEdge *in; + SymEdge *out; + } CrossData; + CrossData cdata; + CrossData *crossings = NULL; + CrossData *cd; + BLI_array_staticdeclare(crossings, 128); +#ifdef DEBUG_CDT + const int dbg_level = 0; +#endif + + /* Find path through structure from v1 to v2 and record how we got there in crossings. + * In crossings array, each CrossData is populated as follows: + * + * If ray from previous node goes through a face, not along an edge: + * + * _ B + * / |\ + * - - | \ + * prev........X \ + * \ d | \C + * -- | / + * \ a| b/ + * - - | / + * \ A + * + * lambda = fraction of way along AB where X is. + * vert = NULL initially, will later get new node that splits AB + * in = a (SymEdge from A->B, whose face the ray goes through) + * out = b (SymEdge from A->C, whose face the ray goes through next + * + * If the ray from the previous node goes directly to an existing vertex, say A + * in the previous diagram, maybe along an existing edge like d in that diagram + * but if prev had lambda !=0 then there may be no such edge d, then: + * + * lambda = 0 + * vert = A + * in = a + * out = b + * + * crossings[0] will have in = NULL, and crossings[last] will have out = NULL + */ + if (r_edges) { + *r_edges = NULL; + } + vse1 = v1->symedge; +#ifdef DEBUG_CDT + if (dbg_level > 0) { + vse2 = v2->symedge; + fprintf(stderr, "\ninsert_segment %d\n", input_id); + dump_v(v1, " 1"); + dump_v(v2, " 2"); + if (dbg_level > 1) { + dump_se(vse1, " se1"); + dump_se(vse2, " se2"); + } + } +#endif + if (v1 == v2) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "segment between same vertices, ignored\n"); + } +#endif + return; + } + state_through_vert = true; + done = false; + t = vse1; + search_count = 0; + while (!done) { + /* Invariant: crossings[0 .. BLI_array_len(crossings)] has crossing info for path up to + * but not including the crossing of edge t, which will either be through a vert + * (if state_through_vert is true) or through edge t not at either end. + * In the latter case, t->face is the face that ray v1--v2 goes through after path-so-far. + */ +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf( + stderr, "top of insert_segment main loop, state_through_vert=%d\n", state_through_vert); + dump_se_cycle(t, "current t ", 4); + } +#endif + if (state_through_vert) { + /* Invariant: ray v1--v2 contains t->vert. */ + cdata.in = (BLI_array_len(crossings) == 0) ? NULL : t; + cdata.out = NULL; /* To be filled in if this isn't final. */ + cdata.lambda = 0.0; + cdata.vert = t->vert; + BLI_array_append(crossings, cdata); + if (t->vert == v2) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "found v2, so done\n"); + } +#endif + done = true; + } + else { + /* Do ccw scan of triangles around t->vert to find exit triangle for ray v1--v2. */ + tstart = t; + tout = NULL; + do { + va = t->next->vert; + vb = t->next->next->vert; + ccw1 = CCW_test(t->vert->co, va->co, v2->co); + ccw2 = CCW_test(t->vert->co, vb->co, v2->co); +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "non-final through vert case\n"); + dump_v(va, " va"); + dump_v(vb, " vb"); + fprintf(stderr, "ccw1=%d, ccw2=%d\n", ccw1, ccw2); + } +#endif + if (ccw1 == 0 && in_line(t->vert->co, va->co, v2->co)) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "ray goes through va\n"); + } +#endif + state_through_vert = true; + tout = t; + t = t->next; + break; + } + else if (ccw2 == 0 && in_line(t->vert->co, vb->co, v2->co)) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "ray goes through vb\n"); + } +#endif + state_through_vert = true; + t = t->next->next; + tout = sym(t); + break; + } + else if (ccw1 > 0 && ccw2 < 0) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "segment intersects\n"); + } +#endif + state_through_vert = false; + tout = t; + t = t->next; + break; + } + t = t->rot; +#ifdef DEBUG_CDT + if (dbg_level > 1) { + dump_se_cycle(t, "next rot tri", 4); + } +#endif + } while (t != tstart); + BLI_assert(tout != NULL); /* TODO: something sensivle for "this can't happen" */ + crossings[BLI_array_len(crossings) - 1].out = tout; + } + } + else { /* State is "through edge", not "through vert" */ + /* Invariant: ray v1--v2 intersects segment t->edge, not at either end. + * and t->face is the face we have just passed through. */ + va = t->vert; + vb = t->next->vert; +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "through edge case\n"); + dump_v(va, " va"); + dump_v(vb, " vb"); + } +#endif + isect = isect_seg_seg_v2_lambda_mu_db(va->co, vb->co, v1->co, v2->co, &lambda, NULL); + /* TODO: something sensible for "this can't happen" */ + BLI_assert(isect == ISECT_LINE_LINE_CROSS); + UNUSED_VARS_NDEBUG(isect); +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "intersect point at %f along va--vb\n", lambda); + if (dbg_level == 1) { + dump_v(va, " va"); + dump_v(vb, " vb"); + } + } +#endif + tout = sym(t)->next; + cdata.in = t; + cdata.out = tout; + cdata.lambda = lambda; + cdata.vert = NULL; /* To be filled in with edge split vertex later. */ + BLI_array_append(crossings, cdata); +#ifdef DEBUG_CDT + if (dbg_level > 0) { + dump_se_cycle(tout, "next search tri", 4); + } +#endif + /* 'tout' is 'symedge' from 'vb' to third vertex, 'vc'. */ + BLI_assert(tout->vert == va); + vc = tout->next->vert; + ccw1 = CCW_test(v1->co, v2->co, vc->co); +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "now searching with third vertex "); + dump_v(vc, "vc"); + fprintf(stderr, "ccw(v1, v2, vc) = %d\n", ccw1); + } +#endif + if (ccw1 == -1) { + /* v1--v2 should intersect vb--vc. */ +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "v1--v2 intersects vb--vc\n"); + } +#endif + t = tout->next; + state_through_vert = false; + } + else if (ccw1 == 1) { + /* v1--v2 should intersect va--vc. */ +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "v1--v2 intersects va--vc\n"); + } +#endif + t = tout; + state_through_vert = false; + } + else { + /* ccw1 == 0. */ +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "ccw==0 case, so going through or to vc\n"); + } +#endif + t = tout->next; + state_through_vert = true; + } + } + if (++search_count > 10000) { + fprintf(stderr, "infinite loop? bailing out\n"); + BLI_assert(0); /* Catch these while developing. */ + break; + } + } +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "Crossing info gathered:\n"); + for (i = 0; i < BLI_array_len(crossings); i++) { + cd = &crossings[i]; + fprintf(stderr, "%d:\n", i); + if (cd->vert != NULL) { + dump_v(cd->vert, " vert: "); + } + else { + fprintf(stderr, " lambda=%f along in\n", cd->lambda); + } + if (cd->in) { + dump_se(cd->in, " in: "); + } + if (cd->out) { + dump_se(cd->out, " out: "); + } + } + } +#endif + + if (BLI_array_len(crossings) == 2) { + /* For speed, handle special case of segment must have already been there. */ + se = crossings[1].in; + if (se->next->vert != v1) { + se = prev(se); + } + BLI_assert(se->vert == v1 || se->next->vert == v1); +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "segment already there: "); + dump_se(se, ""); + } +#endif + add_to_input_ids(&se->edge->input_ids, input_id, cdt); + if (r_edges != NULL) { + BLI_linklist_append_pool(&edge_list, se->edge, cdt->listpool); + } + } + else { + /* Insert all intersection points. */ + for (i = 0; i < BLI_array_len(crossings); i++) { + cd = &crossings[i]; + if (cd->lambda != 0.0 && is_constrained_edge(cd->in->edge)) { + edge = split_edge(cdt, cd->in, cd->lambda); + cd->vert = edge->symedges[0].vert; +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "insert vert for crossing %d: ", i); + dump_v(cd->vert, "inserted"); + } +#endif + } + } + + /* Remove any crossed, non-intersected edges. */ + for (i = 0; i < BLI_array_len(crossings); i++) { + cd = &crossings[i]; + if (cd->lambda != 0.0 && !is_constrained_edge(cd->in->edge)) { + delete_edge(cdt, cd->in); +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "delete edge for crossing %d\n", i); + } +#endif + } + } + + /* Insert segments for v1->v2. */ + tstart = crossings[0].out; + for (i = 1; i < BLI_array_len(crossings); i++) { + cd = &crossings[i]; + t = tnext = NULL; + if (cd->lambda != 0.0) { + if (is_constrained_edge(cd->in->edge)) { + t = cd->vert->symedge; + tnext = sym(t)->next; + } + } + else if (cd->lambda == 0.0) { + t = cd->in; + tnext = cd->out; + } + if (t) { +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "insert diagonal between\n"); + dump_se(tstart, " "); + dump_se(t, " "); + dump_se_cycle(tstart, "tstart", 100); + dump_se_cycle(t, "t", 100); + } +#endif + if (tstart->next->vert == t->vert) { + edge = tstart->edge; +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "already there\n"); + } +#endif + } + else { + edge = add_diagonal(cdt, tstart, t); + } + add_to_input_ids(&edge->input_ids, input_id, cdt); +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "added\n"); + } +#endif + if (r_edges != NULL) { + BLI_linklist_append_pool(&edge_list, edge, cdt->listpool); + } + /* Now retriangulate upper and lower gaps. */ + re_delaunay_triangulate(cdt, &edge->symedges[0]); + re_delaunay_triangulate(cdt, &edge->symedges[1]); + } + if (i < BLI_array_len(crossings) - 1) { + if (tnext != NULL) { + tstart = tnext; +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "now tstart = "); + dump_se(tstart, ""); + } +#endif + } + } + } + } + if (r_edges) { + *r_edges = edge_list.list; + } + BLI_array_free(crossings); +} + +/** + * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that + * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is + * on the left of that SymEdge. + * + * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then + * process all adjacent faces where the adjacency isn't across an edge that was a constraint added + * for the boundary of the input face. + * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face. + * + * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside. + * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the + * contiguous set of faces enclosed by parts of the boundary, leaving the other such untagged. This + * may be a feature instead of a bug if the first contiguous section is most of the face and the + * others are tiny self-crossing triangles at some parts of the boundary. On the other hand, if + * decide we want to handle these in full generality, then will need a more complicated algorithm + * (using "inside" tests and a parity rule) to decide on the interior. + */ +static void add_face_ids( + CDT_state *cdt, SymEdge *face_symedge, int face_id, int fedge_start, int fedge_end) +{ + Stack stack; + SymEdge *se, *se_start, *se_sym; + CDTFace *face, *face_other; + int visit; + + /* Can't loop forever since eventually would visit every face. */ + cdt->visit_count++; + visit = cdt->visit_count; + stack = NULL; + push(&stack, face_symedge, cdt); + while (!is_empty(&stack)) { + se = pop(&stack, cdt); + face = se->face; + if (face->visit_index == visit) { + continue; + } + face->visit_index = visit; + add_to_input_ids(&face->input_ids, face_id, cdt); + se_start = se; + for (se = se->next; se != se_start; se = se->next) { + if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) { + se_sym = sym(se); + face_other = se_sym->face; + if (face_other->visit_index != visit) { + push(&stack, se_sym, cdt); + } + } + } + } +} + +/* Delete_edge but try not to mess up outer face. + * Also faces have symedges now, so make sure not + * to mess those up either. */ +static void dissolve_symedge(CDT_state *cdt, SymEdge *se) +{ + SymEdge *symse = sym(se); + if (symse->face == cdt->outer_face) { + se = sym(se); + symse = sym(se); + } + if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) { + /* Advancing by 2 to get past possible 'sym(se)'. */ + if (se->next->next == se) { + cdt->outer_face->symedge = NULL; + } + else { + cdt->outer_face->symedge = se->next->next; + } + } + else { + if (se->face->symedge == se) { + se->face->symedge = se->next; + } + if (symse->face->symedge == se) { + symse->face->symedge = symse->next; + } + } + delete_edge(cdt, se); +} + +static void remove_non_constraint_edges(CDT_state *cdt, const bool valid_bmesh) +{ + LinkNode *ln; + CDTEdge *e; + SymEdge *se, *se2; + CDTFace *fleft, *fright; + bool dissolve; + + for (ln = cdt->edges; ln; ln = ln->next) { + e = (CDTEdge *)ln->link; + dissolve = !is_deleted_edge(e) && !is_constrained_edge(e); + if (dissolve) { + se = &e->symedges[0]; + if (valid_bmesh) { + fleft = se->face; + fright = sym(se)->face; + if (fleft != cdt->outer_face && fright != cdt->outer_face && + (fleft->input_ids != NULL || fright->input_ids != NULL)) { + /* Is there another symedge with same left and right faces? */ + for (se2 = se->next; dissolve && se2 != se; se2 = se2->next) { + if (sym(se2)->face == fright) { + dissolve = false; + } + } + } + } + if (dissolve) { + dissolve_symedge(cdt, se); + } + } + } +} + +static void remove_outer_edges(CDT_state *cdt, const bool remove_until_constraints) +{ + LinkNode *fstack = NULL; + SymEdge *se, *se_start; + CDTFace *f, *fsym; + int visit = ++cdt->visit_count; +#ifdef DEBUG_CDT + int dbg_level = 0; + + if (dbg_level > 0) { + fprintf(stderr, "remove_outer_edges, until_constraints=%d\n", remove_until_constraints); + } +#endif + + cdt->outer_face->visit_index = visit; + + /* Find an f, not outer face, but touching outer face. */ + f = NULL; + se_start = se = cdt->vert_array[0]->symedge; + do { + if (se->face != cdt->outer_face) { + f = se->face; + break; + } + se = se->rot; + } while (se != se_start); + BLI_assert(f != NULL && f->symedge != NULL); + if (f == NULL) { + return; + } + BLI_linklist_prepend_pool(&fstack, f, cdt->listpool); + while (fstack != NULL) { + LinkNode *to_dissolve = NULL; + bool dissolvable; + f = (CDTFace *)BLI_linklist_pop_pool(&fstack, cdt->listpool); + if (f->visit_index == visit) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "skipping f=%p, already visited\n", f); + } +#endif + continue; + } + BLI_assert(f != cdt->outer_face); +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "top of loop, f=%p\n", f); + dump_se_cycle(f->symedge, "visit", 10000); + dump_cdt(cdt, "cdt at top of loop"); + } +#endif + f->visit_index = visit; + se_start = se = f->symedge; + do { + if (remove_until_constraints) { + dissolvable = !is_constrained_edge(se->edge); + } + else { + dissolvable = edge_touches_frame(se->edge); + } +#ifdef DEBUG_CDT + if (dbg_level > 1) { + dump_se(se, "edge in f"); + fprintf(stderr, " dissolvable=%d\n", dissolvable); + } +#endif + if (dissolvable) { + fsym = sym(se)->face; +#ifdef DEBUG_CDT + if (dbg_level > 1) { + dump_se_cycle(fsym->symedge, "fsym", 10000); + fprintf(stderr, " visited=%d\n", fsym->visit_index == visit); + } +#endif + if (fsym->visit_index != visit) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "pushing face %p\n", fsym); + dump_se_cycle(fsym->symedge, "pushed", 10000); + } +#endif + BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool); + } + else { + BLI_linklist_prepend_pool(&to_dissolve, se, cdt->listpool); + } + } + se = se->next; + } while (se != se_start); + while (to_dissolve != NULL) { + se = (SymEdge *)BLI_linklist_pop_pool(&to_dissolve, cdt->listpool); + if (se->next != NULL) { + dissolve_symedge(cdt, se); + } + } + } +} + +/** + * Remove edges and merge faces to get desired output, as per options. + * \note the cdt cannot be further changed after this. + */ +static void prepare_cdt_for_output(CDT_state *cdt, const CDT_output_type output_type) +{ + CDTFace *f; + CDTEdge *e; + LinkNode *ln; + + cdt->output_prepared = true; + + /* Make sure all non-deleted faces have a symedge. */ + for (ln = cdt->edges; ln; ln = ln->next) { + e = (CDTEdge *)ln->link; + if (e->symedges[0].face->symedge == NULL) { + e->symedges[0].face->symedge = &e->symedges[0]; + } + if (e->symedges[1].face->symedge == NULL) { + e->symedges[1].face->symedge = &e->symedges[1]; + } + } +#ifdef DEBUG_CDT + /* All non-deleted faces should have a symedge now. */ + for (ln = cdt->faces; ln; ln = ln->next) { + f = (CDTFace *)ln->link; + if (!f->deleted) { + BLI_assert(f->symedge != NULL); + } + } +#else + UNUSED_VARS(f); +#endif + + if (output_type == CDT_CONSTRAINTS || output_type == CDT_CONSTRAINTS_VALID_BMESH) { + remove_non_constraint_edges(cdt, output_type == CDT_CONSTRAINTS_VALID_BMESH); + } + else if (output_type == CDT_FULL || output_type == CDT_INSIDE) { + remove_outer_edges(cdt, output_type == CDT_INSIDE); + } +} + +#define NUM_BOUND_VERTS 4 +#define VERT_OUT_INDEX(v) ((v)->index - NUM_BOUND_VERTS) + +static CDT_result *cdt_get_output(CDT_state *cdt, const CDT_output_type output_type) +{ + int i, j, nv, ne, nf, faces_len_total; + int orig_map_size, orig_map_index; + CDT_result *result; + LinkNode *lne, *lnf, *ln; + SymEdge *se, *se_start; + CDTEdge *e; + CDTFace *f; + + prepare_cdt_for_output(cdt, output_type); + + result = (CDT_result *)MEM_callocN(sizeof(*result), __func__); + + /* All verts except first NUM_BOUND_VERTS will be output. */ + nv = cdt->vert_array_len - NUM_BOUND_VERTS; + if (nv <= 0) { + return result; + } + + result->verts_len = nv; + result->vert_coords = MEM_malloc_arrayN(nv, sizeof(result->vert_coords[0]), __func__); + + /* Make the vertex "orig" map arrays, mapping output verts to lists of input ones. */ + orig_map_size = 0; + for (i = 0; i < nv; i++) { + orig_map_size += BLI_linklist_count(cdt->vert_array[i + 4]->input_ids); + } + result->verts_orig_len_table = MEM_malloc_arrayN(nv, sizeof(int), __func__); + result->verts_orig_start_table = MEM_malloc_arrayN(nv, sizeof(int), __func__); + result->verts_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); + + orig_map_index = 0; + for (i = 0; i < nv; i++) { + j = i + NUM_BOUND_VERTS; + result->vert_coords[i][0] = (float)cdt->vert_array[j]->co[0]; + result->vert_coords[i][1] = (float)cdt->vert_array[j]->co[1]; + result->verts_orig_start_table[i] = orig_map_index; + for (ln = cdt->vert_array[j]->input_ids; ln; ln = ln->next) { + result->verts_orig[orig_map_index++] = POINTER_AS_INT(ln->link); + } + result->verts_orig_len_table[i] = orig_map_index - result->verts_orig_start_table[i]; + } + + ne = 0; + orig_map_size = 0; + for (ln = cdt->edges; ln; ln = ln->next) { + e = (CDTEdge *)ln->link; + if (!is_deleted_edge(e)) { + ne++; + if (e->input_ids) { + orig_map_size += BLI_linklist_count(e->input_ids); + } + } + } + if (ne != 0) { + result->edges_len = ne; + result->face_edge_offset = cdt->face_edge_offset; + result->edges = MEM_malloc_arrayN(ne, sizeof(result->edges[0]), __func__); + result->edges_orig_len_table = MEM_malloc_arrayN(ne, sizeof(int), __func__); + result->edges_orig_start_table = MEM_malloc_arrayN(ne, sizeof(int), __func__); + if (orig_map_size > 0) { + result->edges_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); + } + orig_map_index = 0; + i = 0; + for (lne = cdt->edges; lne; lne = lne->next) { + e = (CDTEdge *)lne->link; + if (!is_deleted_edge(e)) { + result->edges[i][0] = VERT_OUT_INDEX(e->symedges[0].vert); + result->edges[i][1] = VERT_OUT_INDEX(e->symedges[1].vert); + result->edges_orig_start_table[i] = orig_map_index; + for (ln = e->input_ids; ln; ln = ln->next) { + result->edges_orig[orig_map_index++] = POINTER_AS_INT(ln->link); + } + result->edges_orig_len_table[i] = orig_map_index - result->edges_orig_start_table[i]; + i++; + } + } + } + + nf = 0; + faces_len_total = 0; + orig_map_size = 0; + for (ln = cdt->faces; ln; ln = ln->next) { + f = (CDTFace *)ln->link; + if (!f->deleted && f != cdt->outer_face) { + nf++; + se = se_start = f->symedge; + BLI_assert(se != NULL); + do { + faces_len_total++; + se = se->next; + } while (se != se_start); + if (f->input_ids) { + orig_map_size += BLI_linklist_count(f->input_ids); + } + } + } + + if (nf != 0) { + result->faces_len = nf; + result->faces_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); + result->faces_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); + result->faces = MEM_malloc_arrayN(faces_len_total, sizeof(int), __func__); + result->faces_orig_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); + result->faces_orig_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); + if (orig_map_size > 0) { + result->faces_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); + } + orig_map_index = 0; + i = 0; + j = 0; + for (lnf = cdt->faces; lnf; lnf = lnf->next) { + f = (CDTFace *)lnf->link; + if (!f->deleted && f != cdt->outer_face) { + result->faces_start_table[i] = j; + se = se_start = f->symedge; + do { + result->faces[j++] = VERT_OUT_INDEX(se->vert); + se = se->next; + } while (se != se_start); + result->faces_len_table[i] = j - result->faces_start_table[i]; + result->faces_orig_start_table[i] = orig_map_index; + for (ln = f->input_ids; ln; ln = ln->next) { + result->faces_orig[orig_map_index++] = POINTER_AS_INT(ln->link); + } + result->faces_orig_len_table[i] = orig_map_index - result->faces_orig_start_table[i]; + i++; + } + } + } + return result; +} + +CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_type output_type) +{ + int nv = input->verts_len; + int ne = input->edges_len; + int nf = input->faces_len; + double epsilon = (double)input->epsilon; + int i, f, v1, v2; + int fedge_start, fedge_end; + double minx, maxx, miny, maxy; + float *xy; + double vert_co[2]; + CDT_state *cdt; + CDT_result *result; + CDTVert **verts; + LinkNode *edge_list; + CDTEdge *face_edge; + SymEdge *face_symedge; +#ifdef DEBUG_CDT + int dbg_level = 1; +#endif + + if ((nv > 0 && input->vert_coords == NULL) || (ne > 0 && input->edges == NULL) || + (nf > 0 && (input->faces == NULL || input->faces_start_table == NULL || + input->faces_len_table == NULL))) { +#ifdef DEBUG_CDT + fprintf(stderr, "invalid input: unexpected NULL array(s)\n"); +#endif + return NULL; + } + + if (nv > 0) { + minx = miny = DBL_MAX; + maxx = maxy = -DBL_MAX; + for (i = 0; i < nv; i++) { + xy = input->vert_coords[i]; + if (xy[0] < minx) { + minx = xy[0]; + } + if (xy[0] > maxx) { + maxx = xy[0]; + } + if (xy[1] < miny) { + miny = xy[1]; + } + if (xy[1] > maxy) { + maxy = xy[1]; + } + } + verts = (CDTVert **)MEM_mallocN(nv * sizeof(CDTVert *), "constrained delaunay"); + } + else { + minx = miny = maxx = maxy = 0; + verts = NULL; + } + + if (epsilon == 0.0) { + epsilon = 1e-8; + } + cdt = cdt_init(minx, maxx, miny, maxy, epsilon); + /* TODO: use a random permutation for order of adding the vertices. */ + for (i = 0; i < nv; i++) { + vert_co[0] = (double)input->vert_coords[i][0]; + vert_co[1] = (double)input->vert_coords[i][1]; + verts[i] = add_point_constraint(cdt, vert_co, i); + } + for (i = 0; i < ne; i++) { + v1 = input->edges[i][0]; + v2 = input->edges[i][1]; + if (v1 < 0 || v1 >= nv || v2 < 0 || v2 >= nv) { +#ifdef DEBUG_CDT + fprintf(stderr, "edge indices not valid: v1=%d, v2=%d, nv=%d\n", v1, v2, nv); +#endif + continue; + } + add_edge_constraint(cdt, verts[v1], verts[v2], i, NULL); + } + cdt->face_edge_offset = ne; + for (f = 0; f < nf; f++) { + int flen = input->faces_len_table[f]; + int fstart = input->faces_start_table[f]; + if (flen <= 2) { +#ifdef DEBUG_CDT + fprintf(stderr, "face %d has length %d; ignored\n", f, flen); +#endif + continue; + } + for (i = 0; i < flen; i++) { + int face_edge_id = cdt->face_edge_offset + fstart + i; + v1 = input->faces[fstart + i]; + v2 = input->faces[fstart + ((i + 1) % flen)]; + if (v1 < 0 || v1 >= nv || v2 < 0 || v2 >= nv) { +#ifdef DEBUG_CDT + fprintf(stderr, "face indices not valid: f=%d, v1=%d, v2=%d, nv=%d\n", f, v1, v2, nv); +#endif + continue; + } + add_edge_constraint(cdt, verts[v1], verts[v2], face_edge_id, &edge_list); +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "edges for edge %d:\n", i); + for (LinkNode *ln = edge_list; ln; ln = ln->next) { + CDTEdge *cdt_e = (CDTEdge *)ln->link; + fprintf(stderr, + " (%.2f,%.2f)->(%.2f,%.2f)\n", + F2(cdt_e->symedges[0].vert->co), + F2(cdt_e->symedges[1].vert->co)); + } + } +#endif + if (i == 0) { + face_edge = (CDTEdge *)edge_list->link; + face_symedge = &face_edge->symedges[0]; + if (face_symedge->vert != verts[v1]) { + face_symedge = &face_edge->symedges[1]; + BLI_assert(face_symedge->vert == verts[v1]); + } + } + BLI_linklist_free_pool(edge_list, NULL, cdt->listpool); + } + fedge_start = cdt->face_edge_offset + fstart; + fedge_end = fedge_start + flen - 1; + add_face_ids(cdt, face_symedge, f, fedge_start, fedge_end); + } +#ifdef DEBUG_CDT + if (dbg_level > 1) { + validate_cdt(cdt, true); + } + if (dbg_level > 1) { + cdt_draw(cdt, "before cdt_get_output"); + } +#endif + result = cdt_get_output(cdt, output_type); +#ifdef DEBUG_CDT + if (dbg_level > 0) { + cdt_draw(cdt, "final"); + } +#endif + if (verts) { + MEM_freeN(verts); + } + cdt_free(cdt); + return result; +} + +void BLI_delaunay_2d_cdt_free(CDT_result *result) +{ + if (result == NULL) { + return; + } + if (result->vert_coords) { + MEM_freeN(result->vert_coords); + } + if (result->edges) { + MEM_freeN(result->edges); + } + if (result->faces) { + MEM_freeN(result->faces); + } + if (result->faces_start_table) { + MEM_freeN(result->faces_start_table); + } + if (result->faces_len_table) { + MEM_freeN(result->faces_len_table); + } + if (result->verts_orig) { + MEM_freeN(result->verts_orig); + } + if (result->verts_orig_start_table) { + MEM_freeN(result->verts_orig_start_table); + } + if (result->verts_orig_len_table) { + MEM_freeN(result->verts_orig_len_table); + } + if (result->edges_orig) { + MEM_freeN(result->edges_orig); + } + if (result->edges_orig_start_table) { + MEM_freeN(result->edges_orig_start_table); + } + if (result->edges_orig_len_table) { + MEM_freeN(result->edges_orig_len_table); + } + if (result->faces_orig) { + MEM_freeN(result->faces_orig); + } + if (result->faces_orig_start_table) { + MEM_freeN(result->faces_orig_start_table); + } + if (result->faces_orig_len_table) { + MEM_freeN(result->faces_orig_len_table); + } + MEM_freeN(result); +} + +#ifdef DEBUG_CDT + +static void dump_se(const SymEdge *se, const char *lab) +{ + if (se->next) { + fprintf( + stderr, "%s((%.2f,%.2f)->(%.2f,%.2f))\n", lab, F2(se->vert->co), F2(se->next->vert->co)); + } + else { + fprintf(stderr, "%s((%.2f,%.2f)->NULL)\n", lab, F2(se->vert->co)); + } +} + +static void dump_v(const CDTVert *v, const char *lab) +{ + fprintf(stderr, "%s(%.2f,%.2f)\n", lab, F2(v->co)); +} + +static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit) +{ + int count = 0; + const SymEdge *s = se; + fprintf(stderr, "%s:\n", lab); + do { + dump_se(s, " "); + s = s->next; + count++; + } while (s != se && count < limit); + if (count == limit) { + fprintf(stderr, " limit hit without cycle!\n"); + } +} + +static void dump_id_list(const LinkNode *id_list, const char *lab) +{ + const LinkNode *ln; + if (!id_list) { + return; + } + fprintf(stderr, "%s", lab); + for (ln = id_list; ln; ln = ln->next) { + fprintf(stderr, "%d%c", POINTER_AS_INT(ln->link), ln->next ? ' ' : '\n'); + } +} + +# define PL(p) (POINTER_AS_UINT(p) & 0xFFFF) +static void dump_cdt(const CDT_state *cdt, const char *lab) +{ + LinkNode *ln; + CDTVert *v; + CDTEdge *e; + CDTFace *f; + SymEdge *se; + int i; + + fprintf(stderr, "\nCDT %s\n", lab); + fprintf(stderr, "\nVERTS\n"); + for (i = 0; i < cdt->vert_array_len; i++) { + v = cdt->vert_array[i]; + fprintf(stderr, "%x: (%f,%f) symedge=%x\n", PL(v), F2(v->co), PL(v->symedge)); + dump_id_list(v->input_ids, " "); + } + fprintf(stderr, "\nEDGES\n"); + for (ln = cdt->edges; ln; ln = ln->next) { + e = (CDTEdge *)ln->link; + if (e->symedges[0].next == NULL) { + continue; + } + fprintf(stderr, "%x:\n", PL(e)); + for (i = 0; i < 2; i++) { + se = &e->symedges[i]; + fprintf(stderr, + " se[%d] @%x: next=%x, rot=%x, vert=%x (%.2f,%.2f), edge=%x, face=%x\n", + i, + PL(se), + PL(se->next), + PL(se->rot), + PL(se->vert), + F2(se->vert->co), + PL(se->edge), + PL(se->face)); + } + dump_id_list(e->input_ids, " "); + } + fprintf(stderr, "\nFACES\n"); + for (ln = cdt->faces; ln; ln = ln->next) { + f = (CDTFace *)ln->link; + if (f->deleted) { + continue; + } + if (f == cdt->outer_face) { + fprintf(stderr, "outer"); + } + else { + fprintf(stderr, "%x: centroid (%f,%f)", PL(f), F2(f->centroid)); + } + fprintf(stderr, " symedge=%x\n", PL(f->symedge)); + dump_id_list(f->input_ids, " "); + } + fprintf(stderr, "\nOTHER\n"); + fprintf( + stderr, "minx=%f, maxx=%f, miny=%f, maxy=%f\n", cdt->minx, cdt->maxx, cdt->miny, cdt->maxy); + fprintf(stderr, "margin=%f\n", cdt->margin); +} +# undef PL + +/** + * Make an html file with svg in it to display the argument cdt. + * Mouse-overs will reveal the coordinates of vertices and edges. + * Constraint edges are drawn thicker than non-constraint edges. + * The first call creates DRAWFILE; subsequent calls append to it. + */ +# define DRAWFILE "/tmp/debug_draw.html" +# define MAX_DRAW_WIDTH 1000 +# define MAX_DRAW_HEIGHT 700 +static void cdt_draw(CDT_state *cdt, const char *lab) +{ + static bool append = false; + FILE *f = fopen(DRAWFILE, append ? "a" : "w"); + double draw_margin = (cdt->maxx - cdt->minx + cdt->maxy - cdt->miny + 1) * 0.05; + double minx = cdt->minx - draw_margin; + double maxx = cdt->maxx + draw_margin; + double miny = cdt->miny - draw_margin; + double maxy = cdt->maxy + draw_margin; + double width = maxx - minx; + double height = maxy - miny; + double aspect = height / width; + int view_width, view_height; + double scale; + LinkNode *ln; + CDTVert *v, *u; + CDTEdge *e; + int i, strokew; + + view_width = MAX_DRAW_WIDTH; + view_height = (int)(view_width * aspect); + if (view_height > MAX_DRAW_HEIGHT) { + view_height = MAX_DRAW_HEIGHT; + view_width = (int)(view_height / aspect); + } + scale = view_width / width; + +# define SX(x) ((x - minx) * scale) +# define SY(y) ((maxy - y) * scale) + + if (!f) { + printf("couldn't open file %s\n", DRAWFILE); + return; + } + fprintf(f, "<div>%s</div>\n<div>\n", lab); + fprintf(f, + "<svg version=\"1.1\" " + "xmlns=\"http://www.w3.org/2000/svg\" " + "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " + "xml:space=\"preserve\"\n"); + fprintf(f, "width=\"%d\" height=\"%d\">/n", view_width, view_height); + + for (ln = cdt->edges; ln; ln = ln->next) { + e = (CDTEdge *)ln->link; + if (is_deleted_edge(e)) { + continue; + } + u = e->symedges[0].vert; + v = e->symedges[1].vert; + strokew = is_constrained_edge(e) ? 5 : 2; + fprintf(f, + "<line fill=\"none\" stroke=\"black\" stroke-width=\"%d\" " + "x1=\"%.1f\" y1=\"%.1f\" x2=\"%.1f\" y2=\"%.1f\">\n", + strokew, + SX(u->co[0]), + SY(u->co[1]), + SX(v->co[0]), + SY(v->co[1])); + fprintf( + f, " <title>(%.3f,%.3f)(%.3f,%.3f)</title>\n", u->co[0], u->co[1], v->co[0], v->co[1]); + fprintf(f, "</line>\n"); + } + i = cdt->output_prepared ? NUM_BOUND_VERTS : 0; + for (; i < cdt->vert_array_len; i++) { + v = cdt->vert_array[i]; + fprintf(f, + "<circle fill=\"black\" cx=\"%.1f\" cy=\"%.1f\" r=\"5\">\n", + SX(v->co[0]), + SY(v->co[1])); + fprintf(f, " <title>(%.3f,%.3f)</title>\n", v->co[0], v->co[1]); + fprintf(f, "</circle>\n"); + } + + fprintf(f, "</svg>\n</div>\n"); + fclose(f); + append = true; +# undef SX +# undef SY +} + +# ifndef NDEBUG /* Only used in assert. */ +/** + * Is a visible from b: i.e., ab crosses no edge of cdt? + * If constrained is true, consider only constrained edges as possible crossers. + * In any case, don't count an edge ab itself. + */ +static bool is_visible(const CDTVert *a, const CDTVert *b, bool constrained, const CDT_state *cdt) +{ + const LinkNode *ln; + const CDTEdge *e; + const SymEdge *se, *senext; + int ikind; + + for (ln = cdt->edges; ln; ln = ln->next) { + e = (const CDTEdge *)ln->link; + if (is_deleted_edge(e) || is_border_edge(e, cdt)) { + continue; + } + if (constrained && !is_constrained_edge(e)) { + continue; + } + se = (const SymEdge *)&e->symedges[0]; + senext = se->next; + if ((a == se->vert || a == senext->vert) || b == se->vert || b == se->next->vert) { + continue; + } + ikind = isect_seg_seg_v2_lambda_mu_db( + a->co, b->co, se->vert->co, senext->vert->co, NULL, NULL); + if (ikind != ISECT_LINE_LINE_NONE) { + if (ikind == ISECT_LINE_LINE_COLINEAR) { + /* TODO: special test here for overlap. */ + continue; + } + return false; + } + } + return true; +} +# endif + +# ifndef NDEBUG /* Only used in assert. */ +/** + * Check that edge ab satisfies constrained delaunay condition: + * That is, for all non-constraint, non-border edges ab, + * (1) ab is visible in the constraint graph; and + * (2) there is a circle through a and b such that any vertex v connected by an edge to a or b + * is not inside that circle. + * The argument 'se' specifies ab by: a is se's vert and b is se->next's vert. + * Return true if check is OK. + */ +static bool is_delaunay_edge(const SymEdge *se, const double epsilon) +{ + int i; + CDTVert *a, *b, *c; + const SymEdge *sesym, *curse, *ss; + bool ok[2]; + + if (!is_constrained_edge(se->edge)) { + return true; + } + sesym = sym(se); + a = se->vert; + b = se->next->vert; + /* Try both the triangles adjacent to se's edge for circle. */ + for (i = 0; i < 2; i++) { + ok[i] = true; + curse = (i == 0) ? se : sesym; + a = curse->vert; + b = curse->next->vert; + c = curse->next->next->vert; + for (ss = curse->rot; ss != curse; ss = ss->rot) { + ok[i] |= delaunay_check(a, b, c, ss->next->vert, epsilon); + } + } + return ok[0] || ok[1]; +} +# endif + +# ifndef NDEBUG +static bool plausible_non_null_ptr(void *p) +{ + return p > (void *)0x1000; +} +# endif + +static void validate_face_centroid(SymEdge *se) +{ + SymEdge *senext; +# ifndef NDEBUG + double *centroidp = se->face->centroid; +# endif + double c[2]; + int count; + copy_v2_v2_db(c, se->vert->co); + BLI_assert(reachable(se->next, se, 100)); + count = 1; + for (senext = se->next; senext != se; senext = senext->next) { + add_v2_v2_db(c, senext->vert->co); + count++; + } + c[0] /= count; + c[1] /= count; + BLI_assert(fabs(c[0] - centroidp[0]) < 1e-8 && fabs(c[1] - centroidp[1]) < 1e-8); +} + +static void validate_cdt(CDT_state *cdt, bool check_all_tris) +{ + LinkNode *ln, *lne; + int totedges, totfaces, totverts, totborderedges; + CDTEdge *e; + SymEdge *se, *sesym, *s; + CDTVert *v; + CDTFace *f; + double *p; + double margin; + int i, limit; + bool isborder; + + if (cdt->output_prepared) { + return; + } + + BLI_assert(cdt != NULL); + BLI_assert(cdt->maxx >= cdt->minx); + BLI_assert(cdt->maxy >= cdt->miny); + totedges = 0; + totborderedges = 0; + for (ln = cdt->edges; ln; ln = ln->next) { + e = (CDTEdge *)ln->link; + se = &e->symedges[0]; + sesym = &e->symedges[1]; + if (is_deleted_edge(e)) { + BLI_assert(se->rot == NULL && sesym->next == NULL && sesym->rot == NULL); + continue; + } + totedges++; + isborder = is_border_edge(e, cdt); + if (isborder) { + totborderedges++; + BLI_assert((se->face == cdt->outer_face && sesym->face != cdt->outer_face) || + (se->face != cdt->outer_face && sesym->face == cdt->outer_face)); + } + /* BLI_assert(se->face != sesym->face); + * Not required because faces can have intruding wire edges. */ + BLI_assert(se->vert != sesym->vert); + BLI_assert(se->edge == sesym->edge && se->edge == e); + BLI_assert(sym(se) == sesym && sym(sesym) == se); + for (i = 0; i < 2; i++) { + se = &e->symedges[i]; + v = se->vert; + f = se->face; + p = v->co; + UNUSED_VARS_NDEBUG(p); + BLI_assert(plausible_non_null_ptr(v)); + if (f != NULL) { + BLI_assert(plausible_non_null_ptr(f)); + } + BLI_assert(plausible_non_null_ptr(se->next)); + BLI_assert(plausible_non_null_ptr(se->rot)); + if (check_all_tris && se->face != cdt->outer_face) { + limit = 3; + } + else { + limit = 10000; + } + BLI_assert(reachable(se->next, se, limit)); + UNUSED_VARS_NDEBUG(limit); + BLI_assert(se->next->next != se); + s = se; + do { + BLI_assert(prev(s)->next == s); + BLI_assert(s->rot == sym(prev(s))); + s = s->next; + } while (s != se); + } + BLI_assert(isborder || is_visible(se->vert, se->next->vert, false, cdt)); + BLI_assert(isborder || is_delaunay_edge(se, cdt->epsilon)); + } + totverts = 0; + margin = cdt->margin; + for (i = 0; i < cdt->vert_array_len; i++) { + totverts++; + v = cdt->vert_array[i]; + BLI_assert(plausible_non_null_ptr(v)); + p = v->co; + BLI_assert(p[0] >= cdt->minx - margin && p[0] <= cdt->maxx + margin); + UNUSED_VARS_NDEBUG(margin); + BLI_assert(v->symedge->vert == v); + } + totfaces = 0; + for (ln = cdt->faces; ln; ln = ln->next) { + f = (CDTFace *)ln->link; + BLI_assert(plausible_non_null_ptr(f)); + if (f->deleted) { + continue; + } + totfaces++; + if (f == cdt->outer_face) { + continue; + } + for (lne = cdt->edges; lne; lne = lne->next) { + e = (CDTEdge *)lne->link; + if (!is_deleted_edge(e)) { + for (i = 0; i < 2; i++) { + if (e->symedges[i].face == f) { + validate_face_centroid(&e->symedges[i]); + } + } + } + } + p = f->centroid; + BLI_assert(p[0] >= cdt->minx - margin && p[0] <= cdt->maxx + margin); + BLI_assert(p[1] >= cdt->miny - margin && p[1] <= cdt->maxy + margin); + } + /* Euler's formula for planar graphs. */ + if (check_all_tris) { + BLI_assert(totverts - totedges + totfaces == 2); + } +} +#endif diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index 7be7674069c..b5a950d6851 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -565,7 +565,7 @@ VChar *BLI_vfontchar_copy(const VChar *vchar_src, const int UNUSED(flag)) * between them * </pre> * - * Each glyph's original outline points are located on a grid of indivisible units. + * Each glyphs original outline points are located on a grid of indivisible units. * The points are stored in the font file as 16-bit integer grid coordinates, * with the grid origin's being at (0, 0); they thus range from -16384 to 16383. * diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index b4a96ff316a..c4bdc73e0e3 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2346,6 +2346,213 @@ bool isect_tri_tri_epsilon_v3(const float t_a0[3], return false; } +/* -------------------------------------------------------------------- */ +/** \name Tri-Tri Intersect 2D + * + * "Fast and Robust Triangle-Triangle Overlap Test + * Using Orientation Predicates" P. Guigue - O. Devillers + * Journal of Graphics Tools, 8(1), 2003. + * + * \{ */ + +static bool isect_tri_tri_v2_impl_vert(const float t_a0[2], + const float t_a1[2], + const float t_a2[2], + const float t_b0[2], + const float t_b1[2], + const float t_b2[2]) +{ + if (line_point_side_v2(t_b2, t_b0, t_a1) >= 0.0f) { + if (line_point_side_v2(t_b2, t_b1, t_a1) <= 0.0f) { + if (line_point_side_v2(t_a0, t_b0, t_a1) > 0.0f) { + if (line_point_side_v2(t_a0, t_b1, t_a1) <= 0.0f) { + return 1; + } + else { + return 0; + } + } + else { + if (line_point_side_v2(t_a0, t_b0, t_a2) >= 0.0f) { + if (line_point_side_v2(t_a1, t_a2, t_b0) >= 0.0f) { + return 1; + } + else { + return 0; + } + } + else { + return 0; + } + } + } + else if (line_point_side_v2(t_a0, t_b1, t_a1) <= 0.0f) { + if (line_point_side_v2(t_b2, t_b1, t_a2) <= 0.0f) { + if (line_point_side_v2(t_a1, t_a2, t_b1) >= 0.0f) { + return 1; + } + else { + return 0; + } + } + else { + return 0; + } + } + else { + return 0; + } + } + else if (line_point_side_v2(t_b2, t_b0, t_a2) >= 0.0f) { + if (line_point_side_v2(t_a1, t_a2, t_b2) >= 0.0f) { + if (line_point_side_v2(t_a0, t_b0, t_a2) >= 0.0f) { + return 1; + } + else { + return 0; + } + } + else if (line_point_side_v2(t_a1, t_a2, t_b1) >= 0.0f) { + if (line_point_side_v2(t_b2, t_a2, t_b1) >= 0.0f) { + return 1; + } + else { + return 0; + } + } + else { + return 0; + } + } + else { + return 0; + } +} + +static bool isect_tri_tri_v2_impl_edge(const float t_a0[2], + const float t_a1[2], + const float t_a2[2], + const float t_b0[2], + const float t_b1[2], + const float t_b2[2]) +{ + UNUSED_VARS(t_b1); + + if (line_point_side_v2(t_b2, t_b0, t_a1) >= 0.0f) { + if (line_point_side_v2(t_a0, t_b0, t_a1) >= 0.0f) { + if (line_point_side_v2(t_a0, t_a1, t_b2) >= 0.0f) { + return 1; + } + else { + return 0; + } + } + else { + if (line_point_side_v2(t_a1, t_a2, t_b0) >= 0.0f) { + if (line_point_side_v2(t_a2, t_a0, t_b0) >= 0.0f) { + return 1; + } + else { + return 0; + } + } + else { + return 0; + } + } + } + else { + if (line_point_side_v2(t_b2, t_b0, t_a2) >= 0.0f) { + if (line_point_side_v2(t_a0, t_b0, t_a2) >= 0.0f) { + if (line_point_side_v2(t_a0, t_a2, t_b2) >= 0.0f) { + return 1; + } + else { + if (line_point_side_v2(t_a1, t_a2, t_b2) >= 0.0f) { + return 1; + } + else { + return 0; + } + } + } + else { + return 0; + } + } + else { + return 0; + } + } +} + +static int isect_tri_tri_impl_ccw_v2(const float t_a0[2], + const float t_a1[2], + const float t_a2[2], + const float t_b0[2], + const float t_b1[2], + const float t_b2[2]) +{ + if (line_point_side_v2(t_b0, t_b1, t_a0) >= 0.0f) { + if (line_point_side_v2(t_b1, t_b2, t_a0) >= 0.0f) { + if (line_point_side_v2(t_b2, t_b0, t_a0) >= 0.0f) { + return 1; + } + else { + return isect_tri_tri_v2_impl_edge(t_a0, t_a1, t_a2, t_b0, t_b1, t_b2); + } + } + else { + if (line_point_side_v2(t_b2, t_b0, t_a0) >= 0.0f) { + return isect_tri_tri_v2_impl_edge(t_a0, t_a1, t_a2, t_b2, t_b0, t_b1); + } + else { + return isect_tri_tri_v2_impl_vert(t_a0, t_a1, t_a2, t_b0, t_b1, t_b2); + } + } + } + else { + if (line_point_side_v2(t_b1, t_b2, t_a0) >= 0.0f) { + if (line_point_side_v2(t_b2, t_b0, t_a0) >= 0.0f) { + return isect_tri_tri_v2_impl_edge(t_a0, t_a1, t_a2, t_b1, t_b2, t_b0); + } + else { + return isect_tri_tri_v2_impl_vert(t_a0, t_a1, t_a2, t_b1, t_b2, t_b0); + } + } + else { + return isect_tri_tri_v2_impl_vert(t_a0, t_a1, t_a2, t_b2, t_b0, t_b1); + } + } +} + +bool isect_tri_tri_v2(const float t_a0[2], + const float t_a1[2], + const float t_a2[2], + const float t_b0[2], + const float t_b1[2], + const float t_b2[2]) +{ + if (line_point_side_v2(t_a0, t_a1, t_a2) < 0.0f) { + if (line_point_side_v2(t_b0, t_b1, t_b2) < 0.0f) { + return isect_tri_tri_impl_ccw_v2(t_a0, t_a2, t_a1, t_b0, t_b2, t_b1); + } + else { + return isect_tri_tri_impl_ccw_v2(t_a0, t_a2, t_a1, t_b0, t_b1, t_b2); + } + } + else { + if (line_point_side_v2(t_b0, t_b1, t_b2) < 0.0f) { + return isect_tri_tri_impl_ccw_v2(t_a0, t_a1, t_a2, t_b0, t_b2, t_b1); + } + else { + return isect_tri_tri_impl_ccw_v2(t_a0, t_a1, t_a2, t_b0, t_b1, t_b2); + } + } +} + +/** \} */ + /* Adapted from the paper by Kasper Fauerby */ /* "Improved Collision detection and Response" */ @@ -3245,7 +3452,7 @@ bool clip_segment_v3_plane( } } - /* incase input/output values match (above also) */ + /* In case input/output values match (above also). */ const float p1_copy[3] = {UNPACK3(p1)}; copy_v3_v3(r_p2, p2); copy_v3_v3(r_p1, p1_copy); @@ -3304,7 +3511,7 @@ bool clip_segment_v3_plane_n(const float p1[3], } } - /* incase input/output values match */ + /* In case input/output values match. */ const float p1_copy[3] = {UNPACK3(p1)}; madd_v3_v3v3fl(r_p1, p1_copy, dp, p1_fac); @@ -5523,6 +5730,27 @@ float form_factor_hemi_poly( } /** + * Check if the edge is convex or concave + * (depends on face winding) + * Copied from BM_edge_is_convex(). + */ +bool is_edge_convex_v3(const float v1[3], + const float v2[3], + const float f1_no[3], + const float f2_no[3]) +{ + if (!equals_v3v3(f1_no, f2_no)) { + float cross[3]; + float l_dir[3]; + cross_v3_v3v3(cross, f1_no, f2_no); + /* we assume contiguous normals, otherwise the result isn't meaningful */ + sub_v3_v3v3(l_dir, v2, v1); + return (dot_v3v3(l_dir, cross) > 0.0f); + } + return false; +} + +/** * Evaluate if entire quad is a proper convex quad */ bool is_quad_convex_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3]) diff --git a/source/blender/blenlib/intern/math_statistics.c b/source/blender/blenlib/intern/math_statistics.c index d4fcb32ce37..18affbed708 100644 --- a/source/blender/blenlib/intern/math_statistics.c +++ b/source/blender/blenlib/intern/math_statistics.c @@ -96,7 +96,7 @@ static void covariance_m_vn_ex_task_cb(void *__restrict userdata, * \param center: the center (or mean point) of cos_vn. If NULL, * it is assumed cos_vn is already centered. * \param use_sample_correction: whether to apply sample correction - * (i.e. get 'sample varince' instead of 'population variance'). + * (i.e. get 'sample variance' instead of 'population variance'). * \return r_covmat the computed covariance matrix. */ void BLI_covariance_m_vn_ex(const int n, diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 111b530a527..18acbca00a1 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1685,6 +1685,24 @@ void BLI_split_file_part(const char *string, char *file, const size_t filelen) } /** + * Returns a pointer to the last extension (e.g. the position of the last period). + * Returns NULL if there is no extension. + */ +const char *BLI_path_extension(const char *filepath) +{ + const char *extension = strrchr(filepath, '.'); + if (extension == NULL) { + return NULL; + } + if (BLI_first_slash(extension) != NULL) { + /* There is a path separator in the extension, so the '.' was found in a + * directory component and not in the filename. */ + return NULL; + } + return extension; +} + +/** * Append a filename to a dir, ensuring slash separates. */ void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file) diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index f2b3ef2ad87..b956e1c0a7e 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -82,6 +82,21 @@ size_t BLI_split_name_num(char *left, int *nr, const char *name, const char deli return name_len; } +bool BLI_string_is_decimal(const char *string) +{ + if (*string == '\0') { + return false; + } + + /* Keep iterating over the string until a non-digit is found. */ + while (isdigit(*string)) { + string++; + } + + /* If the non-digit we found is the terminating \0, everything was digits. */ + return *string == '\0'; +} + static bool is_char_sep(const char c) { return ELEM(c, '.', ' ', '-', '_'); diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c index bea38a232cc..034a6d4017e 100644 --- a/source/blender/blenlib/intern/task.c +++ b/source/blender/blenlib/intern/task.c @@ -149,7 +149,7 @@ typedef struct TaskThreadLocalStorage { * without "interrupting" for task execution. * * We try to accumulate as much tasks as possible in a local queue without - * any locks first, and then we push all of them into a scheduler's queue + * any locks first, and then we push all of them into a schedulers queue * from within a single mutex lock. */ bool do_delayed_push; diff --git a/source/blender/blenloader/BLO_blend_defs.h b/source/blender/blenloader/BLO_blend_defs.h index 0787d054141..fec61605dca 100644 --- a/source/blender/blenloader/BLO_blend_defs.h +++ b/source/blender/blenloader/BLO_blend_defs.h @@ -18,7 +18,7 @@ /** \file * \ingroup blenloader - * \brief defines for blendfile codes + * \brief defines for blend-file codes. */ /* INTEGER CODES */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 737a70615ae..1e3342cef04 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -222,7 +222,7 @@ * which keeps large arrays in memory from data-blocks we may not even use. * * \note This is disabled when using compression, - * while zlib supports seek ist's unusably slow, see: T61880. + * while zlib supports seek it's unusably slow, see: T61880. */ #define USE_BHEAD_READ_ON_DEMAND @@ -271,13 +271,13 @@ typedef struct BHeadN { * because ID names are used in lookup tables. */ #define BHEAD_USE_READ_ON_DEMAND(bhead) ((bhead)->code == DATA) -/* this function ensures that reports are printed, - * in the case of libraray linking errors this is important! +/** + * This function ensures that reports are printed, + * in the case of library linking errors this is important! * * bit kludge but better then doubling up on prints, * we could alternatively have a versions of a report function which forces printing - campbell */ - void blo_reportf_wrap(ReportList *reports, ReportType type, const char *format, ...) { char fixed_buf[1024]; /* should be long enough */ @@ -533,7 +533,7 @@ static void split_libdata(ListBase *lb_src, Main **lib_main_array, const uint li if (id->lib) { if (((uint)id->lib->temp_index < lib_main_array_len) && - /* this check should never fail, just incase 'id->lib' is a dangling pointer. */ + /* this check should never fail, just in case 'id->lib' is a dangling pointer. */ (lib_main_array[id->lib->temp_index]->curlib == id->lib)) { Main *mainvar = lib_main_array[id->lib->temp_index]; ListBase *lb_dst = which_libbase(mainvar, GS(id->name)); @@ -679,8 +679,10 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab /* Add library data-block itself to 'main' Main, since libraries are **never** linked data. * Fixes bug where you could end with all ID_LI data-blocks having the same name... */ lib = BKE_libblock_alloc(mainlist->first, ID_LI, BLI_path_basename(filepath), 0); - lib->id.us = ID_FAKE_USERS( - lib); /* Important, consistency with main ID reading code from read_libblock(). */ + + /* Important, consistency with main ID reading code from read_libblock(). */ + lib->id.us = ID_FAKE_USERS(lib); + BLI_strncpy(lib->name, filepath, sizeof(lib->name)); BLI_strncpy(lib->filepath, name1, sizeof(lib->filepath)); @@ -2496,16 +2498,13 @@ static void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, FileData IDP_DirectLinkIDPArray(prop, switch_endian, fd); break; case IDP_DOUBLE: - /* erg, stupid doubles. since I'm storing them - * in the same field as int val; val2 in the - * IDPropertyData struct, they have to deal with - * endianness specifically + /* Workaround for doubles. + * They are stored in the same field as `int val, val2` in the IDPropertyData struct, + * they have to deal with endianness specifically. * - * in theory, val and val2 would've already been swapped + * In theory, val and val2 would've already been swapped * if switch_endian is true, so we have to first unswap - * them then reswap them as a single 64-bit entity. - */ - + * them then re-swap them as a single 64-bit entity. */ if (switch_endian) { BLI_endian_switch_int32(&prop->data.val); BLI_endian_switch_int32(&prop->data.val2); @@ -3367,8 +3366,10 @@ static void direct_link_workspace(FileData *fd, WorkSpace *workspace, const Main for (WorkSpaceDataRelation *relation = workspace->hook_layout_relations.first; relation; relation = relation->next) { - relation->parent = newglobadr( - fd, relation->parent); /* data from window - need to access through global oldnew-map */ + + /* data from window - need to access through global oldnew-map */ + relation->parent = newglobadr(fd, relation->parent); + relation->value = newdataadr(fd, relation->value); } @@ -7682,8 +7683,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) win->addmousemove = true; win->stereo3d_format = newdataadr(fd, win->stereo3d_format); - /* multiview always fallback to anaglyph at file opening - * otherwise quadbuffer saved files can break Blender */ + /* Multi-view always fallback to anaglyph at file opening + * otherwise quad-buffer saved files can break Blender. */ if (win->stereo3d_format) { win->stereo3d_format->display_mode = S3D_DISPLAY_ANAGLYPH; } @@ -8940,6 +8941,37 @@ static void direct_link_linestyle(FileData *fd, FreestyleLineStyle *linestyle) /** \name Read Library Data Block * \{ */ +static ID *create_placeholder(Main *mainvar, const short idcode, const char *idname, const int tag) +{ + ListBase *lb = which_libbase(mainvar, idcode); + ID *ph_id = BKE_libblock_alloc_notest(idcode); + + *((short *)ph_id->name) = idcode; + BLI_strncpy(ph_id->name + 2, idname, sizeof(ph_id->name) - 2); + BKE_libblock_init_empty(ph_id); + ph_id->lib = mainvar->curlib; + ph_id->tag = tag | LIB_TAG_MISSING; + ph_id->us = ID_FAKE_USERS(ph_id); + ph_id->icon_id = 0; + + BLI_addtail(lb, ph_id); + id_sort_by_name(lb, ph_id); + + return ph_id; +} + +static void placeholders_ensure_valid(Main *bmain) +{ + /* Placeholder ObData IDs won't have any material, we have to update their objects for that, + * otherwise the inconsistency between both will lead to crashes (especially in Eevee?). */ + for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { + ID *obdata = ob->data; + if (obdata != NULL && obdata->tag & LIB_TAG_MISSING) { + test_object_materials(bmain, ob, obdata); + } + } +} + static const char *dataname(short id_code) { switch (id_code) { @@ -9125,8 +9157,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int ta /* do after read_struct, for dna reconstruct */ lb = which_libbase(main, idcode); if (lb) { - oldnewmap_insert( - fd->libmap, bhead->old, id, bhead->code); /* for ID_LINK_PLACEHOLDER check */ + /* for ID_LINK_PLACEHOLDER check */ + oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); + BLI_addtail(lb, id); } else { @@ -9156,7 +9189,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int ta * flags dependency graph does not do animation update to avoid loss of unkeyed changes., * which conflicts with undo/redo of changes to animation data itself. * - * But for regular file load we clear the flag, since the flags might have been changed sinde + * But for regular file load we clear the flag, since the flags might have been changed since * the version the file has been saved with. */ if (!fd->memfile) { id->recalc = 0; @@ -9456,7 +9489,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) /* don't forget to set version number in BKE_blender_version.h! */ } -static void do_versions_after_linking(Main *main) +static void do_versions_after_linking(Main *main, ReportList *reports) { // printf("%s for %s (%s), %d.%d\n", __func__, main->curlib ? main->curlib->name : main->name, // main->curlib ? "LIB" : "MAIN", main->versionfile, main->subversionfile); @@ -9464,7 +9497,7 @@ static void do_versions_after_linking(Main *main) do_versions_after_linking_250(main); do_versions_after_linking_260(main); do_versions_after_linking_270(main); - do_versions_after_linking_280(main); + do_versions_after_linking_280(main, reports); do_versions_after_linking_cycles(main); } @@ -9493,8 +9526,10 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_material(fd, main); lib_link_texture(fd, main); lib_link_image(fd, main); - lib_link_ipo( - fd, main); /* XXX deprecated... still needs to be maintained for version patches still */ + + /* XXX deprecated... still needs to be maintained for version patches still. */ + lib_link_ipo(fd, main); + lib_link_key(fd, main); lib_link_world(fd, main); lib_link_light(fd, main); @@ -9508,8 +9543,10 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_armature(fd, main); lib_link_action(fd, main); lib_link_vfont(fd, main); - lib_link_nodetree(fd, - main); /* has to be done after scene/materials, this will verify group nodes */ + + /* Has to be done after scene/materials, this will verify group nodes. */ + lib_link_nodetree(fd, main); + lib_link_palette(fd, main); lib_link_brush(fd, main); lib_link_paint_curve(fd, main); @@ -9767,7 +9804,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) blo_split_main(&mainlist, bfd->main); for (Main *mainvar = mainlist.first; mainvar; mainvar = mainvar->next) { BLI_assert(mainvar->versionfile != 0); - do_versions_after_linking(mainvar); + do_versions_after_linking(mainvar, fd->reports); } blo_join_main(&mainlist); @@ -9775,6 +9812,8 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) ntreeUpdateAllNew(bfd->main); } + placeholders_ensure_valid(bfd->main); + BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false); /* Now that all our data-blocks are loaded, @@ -11096,6 +11135,19 @@ static bool object_in_any_scene(Main *bmain, Object *ob) return false; } +static bool object_in_any_collection(Main *bmain, Object *ob) +{ + Collection *collection; + + for (collection = bmain->collections.first; collection; collection = collection->id.next) { + if (BKE_collection_has_object(collection, ob)) { + return true; + } + } + + return false; +} + static void add_loose_objects_to_scene(Main *mainvar, Main *bmain, Scene *scene, @@ -11105,7 +11157,7 @@ static void add_loose_objects_to_scene(Main *mainvar, const short flag) { Collection *active_collection = NULL; - const bool is_link = (flag & FILE_LINK) != 0; + const bool do_append = (flag & FILE_LINK) == 0; BLI_assert(scene); @@ -11114,14 +11166,13 @@ static void add_loose_objects_to_scene(Main *mainvar, for (Object *ob = mainvar->objects.first; ob; ob = ob->id.next) { bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0; if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { - if (!is_link) { + if (do_append) { if (ob->id.us == 0) { do_it = true; } - else if ((ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) { - /* When appending, make sure any indirectly loaded objects get a base, - * else they cant be accessed at all - * (see T27437). */ + else if ((ob->id.lib == lib) && (object_in_any_collection(bmain, ob) == 0)) { + /* When appending, make sure any indirectly loaded object gets a base, + * when they are not part of any collection yet. */ do_it = true; } } @@ -11171,8 +11222,6 @@ static void add_collections_to_scene(Main *mainvar, Library *lib, const short flag) { - const bool do_append = (flag & FILE_LINK) == 0; - Collection *active_collection = scene->master_collection; if (flag & FILE_ACTIVE_COLLECTION) { LayerCollection *lc = BKE_layer_collection_get_active(view_layer); @@ -11211,12 +11260,10 @@ static void add_collections_to_scene(Main *mainvar, ob->transflag |= OB_DUPLICOLLECTION; copy_v3_v3(ob->loc, scene->cursor.location); } - /* We do not want to force instantiation of indirectly linked collections... - * Except when we are appending (since in that case, we'll end up instantiating all objects, - * it's better to do it via their own collections if possible). - * Reports showing that desired difference in behaviors between link and append: - * See T62570, T61796. */ - else if (do_append || (collection->id.tag & LIB_TAG_INDIRECT) == 0) { + /* We do not want to force instantiation of indirectly linked collections, + * not even when appending. Users can now easily instantiate collections (and their objects) + * as needed by themselves. See T67032. */ + else if ((collection->id.tag & LIB_TAG_INDIRECT) == 0) { bool do_add_collection = (collection->id.tag & LIB_TAG_DOIT) != 0; if (!do_add_collection) { /* We need to check that objects in that collections are already instantiated in a scene. @@ -11228,9 +11275,8 @@ static void add_collections_to_scene(Main *mainvar, for (CollectionObject *coll_ob = collection->gobject.first; coll_ob != NULL; coll_ob = coll_ob->next) { Object *ob = coll_ob->ob; - if ((ob->id.tag & LIB_TAG_PRE_EXISTING) == 0 && (ob->id.tag & LIB_TAG_DOIT) == 0 && - (do_append || (ob->id.tag & LIB_TAG_INDIRECT) == 0) && (ob->id.lib == lib) && - (object_in_any_scene(bmain, ob) == 0)) { + if ((ob->id.tag & (LIB_TAG_PRE_EXISTING | LIB_TAG_DOIT | LIB_TAG_INDIRECT)) == 0 && + (ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) { do_add_collection = true; break; } @@ -11252,6 +11298,7 @@ static void add_collections_to_scene(Main *mainvar, } } + /* Those are kept for safety and consistency, but should not be needed anymore? */ collection->id.tag &= ~LIB_TAG_INDIRECT; collection->id.tag |= LIB_TAG_EXTERN; } @@ -11259,25 +11306,6 @@ static void add_collections_to_scene(Main *mainvar, } } -static ID *create_placeholder(Main *mainvar, const short idcode, const char *idname, const int tag) -{ - ListBase *lb = which_libbase(mainvar, idcode); - ID *ph_id = BKE_libblock_alloc_notest(idcode); - - *((short *)ph_id->name) = idcode; - BLI_strncpy(ph_id->name + 2, idname, sizeof(ph_id->name) - 2); - BKE_libblock_init_empty(ph_id); - ph_id->lib = mainvar->curlib; - ph_id->tag = tag | LIB_TAG_MISSING; - ph_id->us = ID_FAKE_USERS(ph_id); - ph_id->icon_id = 0; - - BLI_addtail(lb, ph_id); - id_sort_by_name(lb, ph_id); - - return ph_id; -} - /* returns true if the item was found * but it may already have already been appended/linked */ static ID *link_named_part( @@ -11547,7 +11575,7 @@ static void library_link_end(Main *mainl, * or they will go again through do_versions - bad, very bad! */ split_main_newid(mainvar, main_newid); - do_versions_after_linking(main_newid); + do_versions_after_linking(main_newid, (*fd)->reports); add_main_to_main(mainvar, main_newid); } @@ -11559,10 +11587,12 @@ static void library_link_end(Main *mainl, /* After all data has been read and versioned, uses LIB_TAG_NEW. */ ntreeUpdateAllNew(mainvar); + placeholders_ensure_valid(mainvar); + BKE_main_id_tag_all(mainvar, LIB_TAG_NEW, false); - fix_relpaths_library(BKE_main_blendfile_path(mainvar), - mainvar); /* make all relative paths, relative to the open blend file */ + /* Make all relative paths, relative to the open blend file. */ + fix_relpaths_library(BKE_main_blendfile_path(mainvar), mainvar); /* Give a base to loose objects and collections. * Only directly linked objects & collections are instantiated by diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index 7cd5bb7ac93..10ee3d52a74 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -187,7 +187,7 @@ void blo_do_versions_cycles(struct FileData *fd, struct Library *lib, struct Mai void do_versions_after_linking_250(struct Main *bmain); void do_versions_after_linking_260(struct Main *bmain); void do_versions_after_linking_270(struct Main *bmain); -void do_versions_after_linking_280(struct Main *bmain); +void do_versions_after_linking_280(struct Main *bmain, ReportList *reports); void do_versions_after_linking_cycles(struct Main *bmain); #endif diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 0ef566d9e89..837c78d303f 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -125,8 +125,8 @@ static void do_version_workspaces_create_from_screens(Main *bmain) } if (screen_parent) { - /* fullscreen with "Back to Previous" option, don't create - * a new workspace, add layout workspace containing parent */ + /* Full-screen with "Back to Previous" option, don't create + * a new workspace, add layout workspace containing parent. */ workspace = BLI_findstring( &bmain->workspaces, screen_parent->id.name + 2, offsetof(ID, name) + 2); } @@ -734,7 +734,98 @@ static void do_versions_seq_alloc_transform_and_crop(ListBase *seqbase) } } -void do_versions_after_linking_280(Main *bmain) +/* Return true if there is something to convert. */ +static void do_versions_material_convert_legacy_blend_mode(bNodeTree *ntree, char blend_method) +{ + bool need_update = false; + + /* Iterate backwards from end so we don't encounter newly added links. */ + bNodeLink *prevlink; + for (bNodeLink *link = ntree->links.last; link; link = prevlink) { + prevlink = link->prev; + + /* Detect link to replace. */ + bNode *fromnode = link->fromnode; + bNodeSocket *fromsock = link->fromsock; + bNode *tonode = link->tonode; + bNodeSocket *tosock = link->tosock; + + if (!(tonode->type == SH_NODE_OUTPUT_MATERIAL && STREQ(tosock->identifier, "Surface"))) { + continue; + } + + /* Only do outputs that are enabled for EEVEE */ + if (!ELEM(tonode->custom1, SHD_OUTPUT_ALL, SHD_OUTPUT_EEVEE)) { + continue; + } + + if (blend_method == 1 /* MA_BM_ADD */) { + nodeRemLink(ntree, link); + + bNode *add_node = nodeAddStaticNode(NULL, ntree, SH_NODE_ADD_SHADER); + add_node->locx = 0.5f * (fromnode->locx + tonode->locx); + add_node->locy = 0.5f * (fromnode->locy + tonode->locy); + + bNodeSocket *shader1_socket = add_node->inputs.first; + bNodeSocket *shader2_socket = add_node->inputs.last; + bNodeSocket *add_socket = nodeFindSocket(add_node, SOCK_OUT, "Shader"); + + bNode *transp_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_TRANSPARENT); + transp_node->locx = add_node->locx; + transp_node->locy = add_node->locy - 110.0f; + + bNodeSocket *transp_socket = nodeFindSocket(transp_node, SOCK_OUT, "BSDF"); + + /* Link to input and material output node. */ + nodeAddLink(ntree, fromnode, fromsock, add_node, shader1_socket); + nodeAddLink(ntree, transp_node, transp_socket, add_node, shader2_socket); + nodeAddLink(ntree, add_node, add_socket, tonode, tosock); + + need_update = true; + } + else if (blend_method == 2 /* MA_BM_MULTIPLY */) { + nodeRemLink(ntree, link); + + bNode *transp_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_TRANSPARENT); + + bNodeSocket *color_socket = nodeFindSocket(transp_node, SOCK_IN, "Color"); + bNodeSocket *transp_socket = nodeFindSocket(transp_node, SOCK_OUT, "BSDF"); + + /* If incomming link is from a closure socket, we need to convert it. */ + if (fromsock->type == SOCK_SHADER) { + transp_node->locx = 0.33f * fromnode->locx + 0.66f * tonode->locx; + transp_node->locy = 0.33f * fromnode->locy + 0.66f * tonode->locy; + + bNode *shtorgb_node = nodeAddStaticNode(NULL, ntree, SH_NODE_SHADERTORGB); + shtorgb_node->locx = 0.66f * fromnode->locx + 0.33f * tonode->locx; + shtorgb_node->locy = 0.66f * fromnode->locy + 0.33f * tonode->locy; + + bNodeSocket *shader_socket = nodeFindSocket(shtorgb_node, SOCK_IN, "Shader"); + bNodeSocket *rgba_socket = nodeFindSocket(shtorgb_node, SOCK_OUT, "Color"); + + nodeAddLink(ntree, fromnode, fromsock, shtorgb_node, shader_socket); + nodeAddLink(ntree, shtorgb_node, rgba_socket, transp_node, color_socket); + } + else { + transp_node->locx = 0.5f * (fromnode->locx + tonode->locx); + transp_node->locy = 0.5f * (fromnode->locy + tonode->locy); + + nodeAddLink(ntree, fromnode, fromsock, transp_node, color_socket); + } + + /* Link to input and material output node. */ + nodeAddLink(ntree, transp_node, transp_socket, tonode, tosock); + + need_update = true; + } + } + + if (need_update) { + ntreeUpdateTree(NULL, ntree); + } +} + +void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) { bool use_collection_compat_28 = true; @@ -1129,6 +1220,28 @@ void do_versions_after_linking_280(Main *bmain) camera->dof_ob = NULL; } } + + if (!MAIN_VERSION_ATLEAST(bmain, 281, 2)) { + /* Replace Multiply and Additive blend mode by Alpha Blend + * now that we use dualsource blending. */ + /* We take care of doing only nodetrees that are always part of materials + * with old blending modes. */ + for (Material *ma = bmain->materials.first; ma; ma = ma->id.next) { + bNodeTree *ntree = ma->nodetree; + if (ma->blend_method == 1 /* MA_BM_ADD */) { + if (ma->use_nodes) { + do_versions_material_convert_legacy_blend_mode(ntree, 1 /* MA_BM_ADD */); + } + ma->blend_method = MA_BM_BLEND; + } + else if (ma->blend_method == 2 /* MA_BM_MULTIPLY */) { + if (ma->use_nodes) { + do_versions_material_convert_legacy_blend_mode(ntree, 2 /* MA_BM_MULTIPLY */); + } + ma->blend_method = MA_BM_BLEND; + } + } + } } /* NOTE: This version patch is intended for versions < 2.52.2, @@ -2668,9 +2781,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) navigation_region = MEM_callocN(sizeof(ARegion), "userpref navigation-region do_versions"); - BLI_insertlinkbefore(regionbase, - main_region, - navigation_region); /* order matters, addhead not addtail! */ + /* Order matters, addhead not addtail! */ + BLI_insertlinkbefore(regionbase, main_region, navigation_region); + navigation_region->regiontype = RGN_TYPE_NAV_BAR; navigation_region->alignment = RGN_ALIGN_LEFT; } @@ -3565,9 +3678,20 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) ar->alignment = RGN_ALIGN_RIGHT; } } + /* Mark outliners as dirty for syncing and enable synced selection */ + if (sl->spacetype == SPACE_OUTLINER) { + SpaceOutliner *soutliner = (SpaceOutliner *)sl; + soutliner->sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_ALL; + soutliner->flag |= SO_SYNC_SELECT; + } } } } + for (Mesh *mesh = bmain->meshes.first; mesh; mesh = mesh->id.next) { + if (mesh->remesh_voxel_size == 0.0f) { + mesh->remesh_voxel_size = 0.1f; + } + } } { diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index cebe15e2719..10f24cff61b 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -26,6 +26,7 @@ #include "BLI_math.h" #include "BLI_string.h" +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "DNA_color_types.h" @@ -185,7 +186,7 @@ static void square_roughness_node_insert(bNodeTree *ntree) /* Add sqrt node. */ bNode *node = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); - node->custom1 = NODE_MATH_POW; + node->custom1 = NODE_MATH_POWER; node->locx = 0.5f * (fromnode->locx + tonode->locx); node->locy = 0.5f * (fromnode->locy + tonode->locy); @@ -385,6 +386,46 @@ static void light_emission_unify(Light *light, const char *engine) } } +/* The B input of the Math node is no longer used for single-operand operators. + * Previously, if the B input was linked and the A input was not, the B input + * was used as the input of the operator. To correct this, we move the link + * from B to A if B is linked and A is not. + */ +static void update_math_node_single_operand_operators(bNodeTree *ntree) +{ + bool need_update = false; + + for (bNode *node = ntree->nodes.first; node; node = node->next) { + if (node->type == SH_NODE_MATH) { + if (ELEM(node->custom1, + NODE_MATH_SQRT, + NODE_MATH_CEIL, + NODE_MATH_SINE, + NODE_MATH_ROUND, + NODE_MATH_FLOOR, + NODE_MATH_COSINE, + NODE_MATH_ARCSINE, + NODE_MATH_TANGENT, + NODE_MATH_ABSOLUTE, + NODE_MATH_FRACTION, + NODE_MATH_ARCCOSINE, + NODE_MATH_ARCTANGENT)) { + bNodeSocket *sockA = BLI_findlink(&node->inputs, 0); + bNodeSocket *sockB = BLI_findlink(&node->inputs, 1); + if (!sockA->link && sockB->link) { + nodeAddLink(ntree, sockB->link->fromnode, sockB->link->fromsock, node, sockA); + nodeRemLink(ntree, sockB->link); + need_update = true; + } + } + } + } + + if (need_update) { + ntreeUpdateTree(NULL, ntree); + } +} + void blo_do_versions_cycles(FileData *UNUSED(fd), Library *UNUSED(lib), Main *bmain) { /* Particle shape shared with Eevee. */ @@ -526,4 +567,13 @@ void do_versions_after_linking_cycles(Main *bmain) } } } + + if (!MAIN_VERSION_ATLEAST(bmain, 281, 2)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + update_math_node_single_operand_operators(ntree); + } + } + FOREACH_NODETREE_END; + } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 02c777ee9b9..2ef1a5c096c 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -145,6 +145,11 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) * Include next version bump. */ { + FROM_DEFAULT_V4_UCHAR(space_outliner.selected_highlight); + FROM_DEFAULT_V4_UCHAR(space_outliner.active); + + + FROM_DEFAULT_V4_UCHAR(space_file.execution_buts); } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 5954ba9cf8e..0ff7ba0034f 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1142,9 +1142,12 @@ typedef struct RenderInfo { char scene_name[MAX_ID_NAME - 2]; } RenderInfo; -/* was for historic render-deamon feature, - * now write because it can be easily extracted without - * reading the whole blend file */ +/** + * This was originally added for the historic render-daemon feature, + * now write because it can be easily extracted without reading the whole blend file. + * + * See: `release/scripts/modules/blend_render_info.py` + */ static void write_renderinfo(WriteData *wd, Main *mainvar) { bScreen *curscreen; diff --git a/source/blender/blentranslation/msgfmt/msgfmt.c b/source/blender/blentranslation/msgfmt/msgfmt.c index 215c92f87de..4691d791301 100644 --- a/source/blender/blentranslation/msgfmt/msgfmt.c +++ b/source/blender/blentranslation/msgfmt/msgfmt.c @@ -24,7 +24,7 @@ * * Generate binary message catalog from textual translation description. * - * This program converts a textual Uniforum-style message catalog (.po file) + * This program converts a textual Uniform-style message catalog (.po file) * into a binary GNU catalog (.mo file). * This is essentially the same function as the GNU msgfmt program, * however, it is a simpler implementation. diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index 5aec59ccd5d..788edc348d9 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -870,9 +870,11 @@ void BM_editselection_normal(BMEditSelection *ese, float r_normal[3]) } } -/* Calculate a plane that is rightangles to the edge/vert/faces normal +/** + * Calculate a plane that is right angles to the edge/vert/faces normal * also make the plane run along an axis that is related to the geometry, - * because this is used for the gizmos Y axis. */ + * because this is used for the gizmos Y axis. + */ void BM_editselection_plane(BMEditSelection *ese, float r_plane[3]) { if (ese->htype == BM_VERT) { diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c index 2000689b496..9bab959f0a2 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -422,7 +422,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* don't use 'j' since we may have skipped some faces, hence some loops. */ BM_elem_index_set(l_iter, totloops++); /* set_ok */ - /* Save index of correspsonding MLoop */ + /* Save index of corresponding #MLoop. */ CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true); } while ((l_iter = l_iter->next) != l_first); diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index e8b35afdcb1..b9f0bcc05f0 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -336,7 +336,7 @@ static BMOpDefine bmo_find_doubles_def = { /* slots_in */ {{"verts", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT}}, /* input vertices */ {"keep_verts", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT}}, /* list of verts to keep */ - {"dist", BMO_OP_SLOT_FLT}, /* minimum distance */ + {"dist", BMO_OP_SLOT_FLT}, /* maximum distance */ {{'\0'}}, }, /* slots_out */ @@ -379,7 +379,7 @@ static BMOpDefine bmo_automerge_def = { "automerge", /* slots_in */ {{"verts", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT}}, /* input verts */ - {"dist", BMO_OP_SLOT_FLT}, /* minimum distance */ + {"dist", BMO_OP_SLOT_FLT}, /* maximum distance */ {{'\0'}}, }, {{{'\0'}}}, /* no output */ @@ -1173,7 +1173,7 @@ static BMOpDefine bmo_dissolve_limit_def = { static BMOpDefine bmo_dissolve_degenerate_def = { "dissolve_degenerate", /* slots_in */ - {{"dist", BMO_OP_SLOT_FLT}, /* minimum distance to consider degenerate */ + {{"dist", BMO_OP_SLOT_FLT}, /* maximum distance to consider degenerate */ {"edges", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, {{'\0'}}, }, diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index dc839054987..915ad6bf5c4 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -483,7 +483,7 @@ void BM_face_calc_tangent_edge_diagonal(const BMFace *f, float r_tangent[3]) l_iter = l_first = BM_FACE_FIRST_LOOP(f); - /* incase of degenerate faces */ + /* In case of degenerate faces. */ zero_v3(r_tangent); /* warning: O(n^2) loop here, take care! */ @@ -520,7 +520,7 @@ void BM_face_calc_tangent_vert_diagonal(const BMFace *f, float r_tangent[3]) l_iter = l_first = BM_FACE_FIRST_LOOP(f); - /* incase of degenerate faces */ + /* In case of degenerate faces. */ zero_v3(r_tangent); /* warning: O(n^2) loop here, take care! */ diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index 4a47bcccb25..51bc86e40eb 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -1542,7 +1542,7 @@ float BM_loop_calc_face_normal_safe_ex(const BMLoop *l, const float epsilon_sq, /** * #BM_loop_calc_face_normal_safe_ex with pre-defined sane epsilon. * - * Since this doesn't scale baed on triangle size, fixed value works well. + * Since this doesn't scale based on triangle size, fixed value works well. */ float BM_loop_calc_face_normal_safe(const BMLoop *l, float r_normal[3]) { diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c index ade6fdfcbed..f317c59b8d1 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_impl.c +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -648,7 +648,7 @@ static void *bmw_ConnectedVertexWalker_step(BMWalker *walker) * \note that this doesn't work on non-manifold geometry. * it might be better to rewrite this to extract * boundary info from the island walker, rather then directly walking - * over the boundary. raises an error if it encounters nonmanifold geometry. + * over the boundary. raises an error if it encounters non-manifold geometry. * * \todo Add restriction flag/callback for wire edges. */ diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index 287a7e93470..ff7dcc388b3 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -1560,9 +1560,10 @@ void BM_mesh_calc_uvs_cone(BMesh *bm, float inv_mat[4][4]; int loop_index; - mul_mat3_m4_v3( - mat, local_up); /* transform the upvector like we did the cone itself, without location. */ - normalize_v3(local_up); /* remove global scaling... */ + /* Transform the upvector like we did the cone itself, without location. */ + mul_mat3_m4_v3(mat, local_up); + /* Remove global scaling... */ + normalize_v3(local_up); invert_m4_m4(inv_mat, mat); diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index 616886deba1..d783842c017 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -80,10 +80,12 @@ static BMFace *remdoubles_createface(BMesh *bm, { BMEdge *e_new; - BMEdge **edges = BLI_array_alloca(edges, f->len); /* new ordered edges */ - BMVert **verts = BLI_array_alloca(verts, f->len); /* new ordered verts */ - BMLoop **loops = BLI_array_alloca( - loops, f->len); /* original ordered loops to copy attrs into the new face */ + /* New ordered edges. */ + BMEdge **edges = BLI_array_alloca(edges, f->len); + /* New ordered verts. */ + BMVert **verts = BLI_array_alloca(verts, f->len); + /* Original ordered loops to copy attributes into the new face. */ + BMLoop **loops = BLI_array_alloca(loops, f->len); STACK_DECLARE(edges); STACK_DECLARE(loops); diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 94935f2090b..797e2ca864e 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -3122,6 +3122,7 @@ static void adjust_offsets(BevelParams *bp, BMesh *bm) } if (!iscycle) { /* right->left direction, changing vchainstart at each step */ + v->adjchain = NULL; v = vchainstart; bvcur = bv; do { diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index e32a9334343..198b4c8e76b 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -363,7 +363,7 @@ static void bm_decim_build_edge_cost(BMesh *bm, struct KD_Symmetry_Data { /* pre-flipped coords */ float e_v1_co[3], e_v2_co[3]; - /* Use to compare the correct endpoints incase v1/v2 are swapped */ + /* Use to compare the correct endpoints in case v1/v2 are swapped. */ float e_dir[3]; int e_found_index; @@ -1371,8 +1371,8 @@ void BM_mesh_decimate_collapse(BMesh *bm, /* handy to detect corruptions elsewhere */ BLI_assert(BM_elem_index_get(e) < tot_edge_orig); - /* under normal conditions wont be accessed again, - * but NULL just incase so we don't use freed node */ + /* Under normal conditions wont be accessed again, + * but NULL just in case so we don't use freed node. */ eheap_table[BM_elem_index_get(e)] = NULL; bm_decim_edge_collapse(bm, diff --git a/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c b/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c index 27b4fa15f26..2cc86a7c93f 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c +++ b/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c @@ -231,7 +231,7 @@ void BM_mesh_decimate_unsubdivide_ex(BMesh *bm, const int iterations, const bool if (BMO_vert_flag_test(bm, v, ELE_VERT_TAG)) #endif { - /* check again incase the topology changed */ + /* Check again in case the topology changed. */ if (bm_vert_dissolve_fan_test(v)) { v_first = v; } diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c index 943f7532960..c30992fa296 100644 --- a/source/blender/bmesh/tools/bmesh_region_match.c +++ b/source/blender/bmesh/tools/bmesh_region_match.c @@ -1099,7 +1099,7 @@ static BMEdge *bm_face_region_pivot_edge_find(BMFace **faces_region, } } else { - /* use incase (depth == 0), no interior verts */ + /* Use in case (depth == 0), no interior verts. */ e_pivot_fallback = e; } } while ((l_iter = l_iter->next) != l_first); diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp index 657cce24a82..64031e10d77 100644 --- a/source/blender/collada/MeshImporter.cpp +++ b/source/blender/collada/MeshImporter.cpp @@ -1149,8 +1149,9 @@ Object *MeshImporter::create_mesh_object( BKE_mesh_assign_object(m_bmain, ob, new_mesh); BKE_mesh_calc_normals(new_mesh); - id_us_plus( - &old_mesh->id); /* Because BKE_mesh_assign_object would have already decreased it... */ + /* Because BKE_mesh_assign_object would have already decreased it... */ + id_us_plus(&old_mesh->id); + BKE_id_free_us(m_bmain, old_mesh); COLLADAFW::MaterialBindingArray &mat_array = geom->getMaterialBindings(); diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 308a95c0e0c..50b5951f99f 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -276,11 +276,12 @@ set(SRC nodes/COM_VectorBlurNode.h operations/COM_VectorBlurOperation.cpp operations/COM_VectorBlurOperation.h - nodes/COM_BlurNode.cpp nodes/COM_BlurNode.h nodes/COM_BokehBlurNode.cpp nodes/COM_BokehBlurNode.h + nodes/COM_DenoiseNode.h + nodes/COM_DenoiseNode.cpp nodes/COM_DespeckleNode.cpp nodes/COM_DespeckleNode.h nodes/COM_DilateErodeNode.cpp @@ -490,6 +491,8 @@ set(SRC operations/COM_ConvolutionEdgeFilterOperation.h operations/COM_ConvolutionFilterOperation.cpp operations/COM_ConvolutionFilterOperation.h + operations/COM_DenoiseOperation.h + operations/COM_DenoiseOperation.cpp operations/COM_DespeckleOperation.cpp operations/COM_DespeckleOperation.h operations/COM_DilateErodeOperation.cpp @@ -558,4 +561,12 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) + add_definitions(-DOIDN_STATIC_LIB) + list(APPEND INC_SYS + ${OPENIMAGEDENOISE_INCLUDE_DIRS} + ) +endif() + blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index f4cd60e3ee0..b28d5ff0cdf 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -215,7 +215,7 @@ class CompositorContext { } /** - * \brief get the current framenumber of the scene in this context + * \brief get the current frame-number of the scene in this context */ int getFramenumber() const; diff --git a/source/blender/compositor/intern/COM_Converter.cpp b/source/blender/compositor/intern/COM_Converter.cpp index 9dc55527f0d..704833389f8 100644 --- a/source/blender/compositor/intern/COM_Converter.cpp +++ b/source/blender/compositor/intern/COM_Converter.cpp @@ -53,6 +53,7 @@ extern "C" { #include "COM_CropNode.h" #include "COM_CryptomatteNode.h" #include "COM_DefocusNode.h" +#include "COM_DenoiseNode.h" #include "COM_DespeckleNode.h" #include "COM_DifferenceMatteNode.h" #include "COM_DilateErodeNode.h" @@ -122,7 +123,7 @@ bool Converter::is_fast_node(bNode *b_node) b_node->type == CMP_NODE_BOKEHBLUR || b_node->type == CMP_NODE_GLARE || b_node->type == CMP_NODE_DBLUR || b_node->type == CMP_NODE_MOVIEDISTORTION || b_node->type == CMP_NODE_LENSDIST || b_node->type == CMP_NODE_DOUBLEEDGEMASK || - b_node->type == CMP_NODE_DILATEERODE); + b_node->type == CMP_NODE_DILATEERODE || b_node->type == CMP_NODE_DENOISE); } Node *Converter::convert(bNode *b_node) @@ -402,6 +403,9 @@ Node *Converter::convert(bNode *b_node) case CMP_NODE_CRYPTOMATTE: node = new CryptomatteNode(b_node); break; + case CMP_NODE_DENOISE: + node = new DenoiseNode(b_node); + break; } return node; } diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index df936818f33..7e5b0264aa3 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -73,7 +73,7 @@ class MemoryBuffer { rcti m_rect; /** - * brief refers to the chunknumber within the executiongroup where related to the MemoryProxy + * brief refers to the chunk-number within the execution-group where related to the MemoryProxy * \see memoryProxy */ unsigned int m_chunkNumber; diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index af9ed2648c9..6b073452771 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -368,7 +368,7 @@ class NodeOperation : public SocketReader { return true; } - inline bool isBreaked() const + inline bool isBraked() const { return this->m_btree->test_break(this->m_btree->tbh); } diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cpp b/source/blender/compositor/intern/COM_OpenCLDevice.cpp index 0aa054b8c77..2529637801d 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cpp +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cpp @@ -248,7 +248,7 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } clFlush(this->m_queue); - if (operation->isBreaked()) { + if (operation->isBraked()) { breaked = false; } } diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h index 7a83bda162c..45ce77acac7 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.h +++ b/source/blender/compositor/intern/COM_OpenCLDevice.h @@ -78,8 +78,8 @@ class OpenCLDevice : public Device { bool initialize(); /** - * \brief deinitialize the device - * During deintiialization the command queue is cleared + * \brief de-initialize the device + * During de-initialization the command queue is cleared */ void deinitialize(); diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cpp b/source/blender/compositor/nodes/COM_DenoiseNode.cpp new file mode 100644 index 00000000000..7de120d1204 --- /dev/null +++ b/source/blender/compositor/nodes/COM_DenoiseNode.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2019, Blender Foundation. + * + * 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. + * + * Contributor: + * Stefan Werner + */ + +#include "COM_DenoiseNode.h" +#include "DNA_node_types.h" +#include "COM_SetValueOperation.h" +#include "COM_MixOperation.h" +#include "COM_DenoiseOperation.h" + +DenoiseNode::DenoiseNode(bNode *editorNode) : Node(editorNode) +{ + /* pass */ +} + +void DenoiseNode::convertToOperations(NodeConverter &converter, + const CompositorContext & /*context*/) const +{ + bNode *node = this->getbNode(); + NodeDenoise *denoise = (NodeDenoise *)node->storage; + + DenoiseOperation *operation = new DenoiseOperation(); + converter.addOperation(operation); + operation->setDenoiseSettings(denoise); + + converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); + converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); + converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); +} diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.h b/source/blender/compositor/nodes/COM_DenoiseNode.h new file mode 100644 index 00000000000..0924da8931c --- /dev/null +++ b/source/blender/compositor/nodes/COM_DenoiseNode.h @@ -0,0 +1,37 @@ +/* + * Copyright 2019, Blender Foundation. + * + * 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. + * + * Contributor: + * Stefan Werner + */ + +#ifndef __COM_DENOISENODE_H__ +#define __COM_DENOISENODE_H__ + +#include "COM_Node.h" + +/** + * \brief DenoiseNode + * \ingroup Node + */ +class DenoiseNode : public Node { + public: + DenoiseNode(bNode *editorNode); + void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; +}; + +#endif diff --git a/source/blender/compositor/nodes/COM_ImageNode.cpp b/source/blender/compositor/nodes/COM_ImageNode.cpp index dc3f65deb1f..6bce56ffd52 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cpp +++ b/source/blender/compositor/nodes/COM_ImageNode.cpp @@ -195,7 +195,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, } } - /* incase we can't load the layer */ + /* In case we can't load the layer. */ if (operation == NULL) { converter.setInvalidOutput(getOutputSocket(index)); } diff --git a/source/blender/compositor/nodes/COM_MathNode.cpp b/source/blender/compositor/nodes/COM_MathNode.cpp index 5a2f934f37f..d13b34bb6b5 100644 --- a/source/blender/compositor/nodes/COM_MathNode.cpp +++ b/source/blender/compositor/nodes/COM_MathNode.cpp @@ -29,61 +29,61 @@ void MathNode::convertToOperations(NodeConverter &converter, case NODE_MATH_ADD: operation = new MathAddOperation(); break; - case NODE_MATH_SUB: + case NODE_MATH_SUBTRACT: operation = new MathSubtractOperation(); break; - case NODE_MATH_MUL: + case NODE_MATH_MULTIPLY: operation = new MathMultiplyOperation(); break; case NODE_MATH_DIVIDE: operation = new MathDivideOperation(); break; - case NODE_MATH_SIN: + case NODE_MATH_SINE: operation = new MathSineOperation(); break; - case NODE_MATH_COS: + case NODE_MATH_COSINE: operation = new MathCosineOperation(); break; - case NODE_MATH_TAN: + case NODE_MATH_TANGENT: operation = new MathTangentOperation(); break; - case NODE_MATH_ASIN: + case NODE_MATH_ARCSINE: operation = new MathArcSineOperation(); break; - case NODE_MATH_ACOS: + case NODE_MATH_ARCCOSINE: operation = new MathArcCosineOperation(); break; - case NODE_MATH_ATAN: + case NODE_MATH_ARCTANGENT: operation = new MathArcTangentOperation(); break; - case NODE_MATH_POW: + case NODE_MATH_POWER: operation = new MathPowerOperation(); break; - case NODE_MATH_LOG: + case NODE_MATH_LOGARITHM: operation = new MathLogarithmOperation(); break; - case NODE_MATH_MIN: + case NODE_MATH_MINIMUM: operation = new MathMinimumOperation(); break; - case NODE_MATH_MAX: + case NODE_MATH_MAXIMUM: operation = new MathMaximumOperation(); break; case NODE_MATH_ROUND: operation = new MathRoundOperation(); break; - case NODE_MATH_LESS: + case NODE_MATH_LESS_THAN: operation = new MathLessThanOperation(); break; - case NODE_MATH_GREATER: + case NODE_MATH_GREATER_THAN: operation = new MathGreaterThanOperation(); break; - case NODE_MATH_MOD: + case NODE_MATH_MODULO: operation = new MathModuloOperation(); break; - case NODE_MATH_ABS: + case NODE_MATH_ABSOLUTE: operation = new MathAbsoluteOperation(); break; - case NODE_MATH_ATAN2: + case NODE_MATH_ARCTAN2: operation = new MathArcTan2Operation(); break; case NODE_MATH_FLOOR: @@ -92,7 +92,7 @@ void MathNode::convertToOperations(NodeConverter &converter, case NODE_MATH_CEIL: operation = new MathCeilOperation(); break; - case NODE_MATH_FRACT: + case NODE_MATH_FRACTION: operation = new MathFractOperation(); break; case NODE_MATH_SQRT: diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cpp b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cpp index 68b5af3089a..1578a805d1e 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cpp +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cpp @@ -22,7 +22,7 @@ inline float colorbalance_lgg(float in, float lift_lgg, float gamma_inv, float gain) { /* 1:1 match with the sequencer with linear/srgb conversions, the conversion isnt pretty - * but best keep it this way, sice testing for durian shows a similar calculation + * but best keep it this way, since testing for durian shows a similar calculation * without lin/srgb conversions gives bad results (over-saturated shadows) with colors * slightly below 1.0. some correction can be done but it ends up looking bad for shadows or * lighter tones - campbell */ diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cpp b/source/blender/compositor/operations/COM_CompositorOperation.cpp index 40315d217ce..5bd466658c0 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cpp +++ b/source/blender/compositor/operations/COM_CompositorOperation.cpp @@ -78,7 +78,7 @@ void CompositorOperation::deinitExecution() return; } - if (!isBreaked()) { + if (!isBraked()) { Render *re = RE_GetSceneRender(this->m_scene); RenderResult *rr = RE_AcquireResultWrite(re); @@ -207,7 +207,7 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) zbuffer[offset] = color[0]; offset4 += COM_NUM_CHANNELS_COLOR; offset++; - if (isBreaked()) { + if (isBraked()) { breaked = true; } } diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cpp b/source/blender/compositor/operations/COM_DenoiseOperation.cpp new file mode 100644 index 00000000000..ad53ab13def --- /dev/null +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2019, Blender Foundation. + * + * 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. + * + * Contributor: + * Stefan Werner + */ + +#include "COM_DenoiseOperation.h" +#include "BLI_math.h" +#ifdef WITH_OPENIMAGEDENOISE +# include <OpenImageDenoise/oidn.hpp> +#endif +#include <iostream> + +DenoiseOperation::DenoiseOperation() : SingleThreadedOperation() +{ + this->addInputSocket(COM_DT_COLOR); + this->addInputSocket(COM_DT_COLOR); + this->addInputSocket(COM_DT_VECTOR); + this->addOutputSocket(COM_DT_COLOR); + this->m_settings = NULL; +} +void DenoiseOperation::initExecution() +{ + SingleThreadedOperation::initExecution(); + this->m_inputProgramColor = getInputSocketReader(0); + this->m_inputProgramAlbedo = getInputSocketReader(1); + this->m_inputProgramNormal = getInputSocketReader(2); +} + +void DenoiseOperation::deinitExecution() +{ + this->m_inputProgramColor = NULL; + this->m_inputProgramAlbedo = NULL; + this->m_inputProgramNormal = NULL; + SingleThreadedOperation::deinitExecution(); +} + +MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) +{ + MemoryBuffer *tileColor = (MemoryBuffer *)this->m_inputProgramColor->initializeTileData(rect2); + MemoryBuffer *tileAlbedo = (MemoryBuffer *)this->m_inputProgramAlbedo->initializeTileData(rect2); + MemoryBuffer *tileNormal = (MemoryBuffer *)this->m_inputProgramNormal->initializeTileData(rect2); + rcti rect; + rect.xmin = 0; + rect.ymin = 0; + rect.xmax = getWidth(); + rect.ymax = getHeight(); + MemoryBuffer *result = new MemoryBuffer(COM_DT_COLOR, &rect); + float *data = result->getBuffer(); + this->generateDenoise(data, tileColor, tileAlbedo, tileNormal, this->m_settings); + return result; +} + +bool DenoiseOperation::determineDependingAreaOfInterest(rcti * /*input*/, + ReadBufferOperation *readOperation, + rcti *output) +{ + if (isCached()) { + return false; + } + else { + rcti newInput; + newInput.xmax = this->getWidth(); + newInput.xmin = 0; + newInput.ymax = this->getHeight(); + newInput.ymin = 0; + return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + } +} + +void DenoiseOperation::generateDenoise(float *data, + MemoryBuffer *inputTileColor, + MemoryBuffer *inputTileAlbedo, + MemoryBuffer *inputTileNormal, + NodeDenoise *settings) +{ + float *inputBufferColor = inputTileColor->getBuffer(); + BLI_assert(inputBufferColor); + if (!inputBufferColor) { + return; + } +#ifdef WITH_OPENIMAGEDENOISE + oidn::DeviceRef device = oidn::newDevice(); + device.commit(); + + oidn::FilterRef filter = device.newFilter("RT"); + filter.setImage("color", + inputBufferColor, + oidn::Format::Float3, + inputTileColor->getWidth(), + inputTileColor->getHeight(), + 0, + 4 * sizeof(float)); + if (inputTileAlbedo && inputTileAlbedo->getBuffer()) { + filter.setImage("albedo", + inputTileAlbedo->getBuffer(), + oidn::Format::Float3, + inputTileAlbedo->getWidth(), + inputTileAlbedo->getHeight(), + 0, + 4 * sizeof(float)); + } + if (inputTileNormal && inputTileNormal->getBuffer()) { + filter.setImage("normal", + inputTileNormal->getBuffer(), + oidn::Format::Float3, + inputTileNormal->getWidth(), + inputTileNormal->getHeight(), + 0, + 3 * sizeof(float)); + } + filter.setImage("output", + data, + oidn::Format::Float3, + inputTileColor->getWidth(), + inputTileColor->getHeight(), + 0, + 4 * sizeof(float)); + + BLI_assert(settings); + if (settings) { + filter.set("hdr", settings->hdr); + filter.set("srgb", false); + } + + filter.commit(); + filter.execute(); + + /* copy the alpha channel, OpenImageDenoise currently only supports RGB */ + size_t numPixels = inputTileColor->getWidth() * inputTileColor->getHeight(); + for (size_t i = 0; i < numPixels; ++i) { + data[i * 4 + 3] = inputBufferColor[i * 4 + 3]; + } +#else + UNUSED_VARS(inputTileAlbedo, inputTileNormal, settings); + ::memcpy(data, + inputBufferColor, + inputTileColor->getWidth() * inputTileColor->getHeight() * sizeof(float) * 4); +#endif +} diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h new file mode 100644 index 00000000000..6e19bd6034a --- /dev/null +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -0,0 +1,71 @@ +/* + * Copyright 2019, Blender Foundation. + * + * 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. + * + * Contributor: + * Stefan Werner + */ + +#ifndef __COM_DENOISEBASEOPERATION_H__ +#define __COM_DENOISEBASEOPERATION_H__ + +#include "COM_SingleThreadedOperation.h" +#include "DNA_node_types.h" + +class DenoiseOperation : public SingleThreadedOperation { + private: + /** + * \brief Cached reference to the input programs + */ + SocketReader *m_inputProgramColor; + SocketReader *m_inputProgramAlbedo; + SocketReader *m_inputProgramNormal; + + /** + * \brief settings of the denoise node. + */ + NodeDenoise *m_settings; + + public: + DenoiseOperation(); + /** + * Initialize the execution + */ + void initExecution(); + + /** + * Deinitialize the execution + */ + void deinitExecution(); + + void setDenoiseSettings(NodeDenoise *settings) + { + this->m_settings = settings; + } + bool determineDependingAreaOfInterest(rcti *input, + ReadBufferOperation *readOperation, + rcti *output); + + protected: + void generateDenoise(float *data, + MemoryBuffer *inputTileColor, + MemoryBuffer *inputTileAlbedo, + MemoryBuffer *inputTileNormal, + NodeDenoise *settings); + + MemoryBuffer *createMemoryBuffer(rcti *rect); +}; +#endif diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.cpp b/source/blender/compositor/operations/COM_GlareGhostOperation.cpp index 944a1d9c5dc..9f01cf5d63a 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.cpp +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cpp @@ -51,7 +51,7 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No if (!breaked) { FastGaussianBlurOperation::IIR_gauss(tbuf1, s1, 1, 3); } - if (isBreaked()) { + if (isBraked()) { breaked = true; } if (!breaked) { @@ -60,19 +60,19 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No MemoryBuffer *tbuf2 = tbuf1->duplicate(); - if (isBreaked()) { + if (isBraked()) { breaked = true; } if (!breaked) { FastGaussianBlurOperation::IIR_gauss(tbuf2, s2, 0, 3); } - if (isBreaked()) { + if (isBraked()) { breaked = true; } if (!breaked) { FastGaussianBlurOperation::IIR_gauss(tbuf2, s2, 1, 3); } - if (isBreaked()) { + if (isBraked()) { breaked = true; } if (!breaked) { @@ -117,7 +117,7 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No gbuf->writePixel(x, y, c); } - if (isBreaked()) { + if (isBraked()) { breaked = true; } } @@ -142,7 +142,7 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No } tbuf1->addPixel(x, y, tc); } - if (isBreaked()) { + if (isBraked()) { breaked = true; } } diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cpp b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cpp index 1ceeba18960..75c2ae51bde 100644 --- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cpp +++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cpp @@ -58,7 +58,7 @@ void GlareSimpleStarOperation::generateGlare(float *data, c[3] = 1.0f; tbuf2->writePixel(x, y, c); } - if (isBreaked()) { + if (isBraked()) { breaked = true; } } @@ -87,7 +87,7 @@ void GlareSimpleStarOperation::generateGlare(float *data, c[3] = 1.0f; tbuf2->writePixel(x, y, c); } - if (isBreaked()) { + if (isBraked()) { breaked = true; } } diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cpp b/source/blender/compositor/operations/COM_GlareStreaksOperation.cpp index 78ca373faaf..951dec9281e 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cpp +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cpp @@ -78,7 +78,7 @@ void GlareStreaksOperation::generateGlare(float *data, tdstcol[2] = 0.5f * (tdstcol[2] + c1[2] + wt * (c2[2] + wt * (c3[2] + wt * c4[2]))); tdstcol[3] = 1.0f; } - if (isBreaked()) { + if (isBraked()) { breaked = true; } } diff --git a/source/blender/compositor/operations/COM_MaskOperation.cpp b/source/blender/compositor/operations/COM_MaskOperation.cpp index 0c9208fb6bb..88a3a5c535c 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cpp +++ b/source/blender/compositor/operations/COM_MaskOperation.cpp @@ -148,7 +148,7 @@ void MaskOperation::executePixelSampled(float output[4], } } else { - /* incase loop below fails */ + /* In case loop below fails. */ output[0] = 0.0f; for (unsigned int i = 0; i < this->m_rasterMaskHandleTot; i++) { diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cpp b/source/blender/compositor/operations/COM_ViewerOperation.cpp index d5b9edae719..3f7619523e3 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cpp +++ b/source/blender/compositor/operations/COM_ViewerOperation.cpp @@ -111,7 +111,7 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) offset++; offset4 += 4; } - if (isBreaked()) { + if (isBraked()) { breaked = true; } offset += offsetadd; diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cpp b/source/blender/compositor/operations/COM_WriteBufferOperation.cpp index beba29e6755..6c5e45472a8 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cpp +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cpp @@ -76,7 +76,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ this->m_input->read(&(buffer[offset4]), x, y, data); offset4 += num_channels; } - if (isBreaked()) { + if (isBraked()) { breaked = true; } } @@ -100,7 +100,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ this->m_input->readSampled(&(buffer[offset4]), x, y, COM_PS_NEAREST); offset4 += num_channels; } - if (isBreaked()) { + if (isBraked()) { breaked = true; } } @@ -119,7 +119,7 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, /* * 1. create cl_mem from outputbuffer * 2. call NodeOperation (input) executeOpenCLChunk(.....) - * 3. schedule readback from opencl to main device (outputbuffer) + * 3. schedule read back from opencl to main device (outputbuffer) * 4. schedule native callback * * note: list of cl_mem will be filled by 2, and needs to be cleaned up by 4 diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 986f65df3fc..fd4c1e251e4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -944,8 +944,9 @@ void DepsgraphNodeBuilder::build_driver_id_property(ID *id, const char *rna_path } PointerRNA id_ptr, ptr; PropertyRNA *prop; + int index; RNA_id_pointer_create(id, &id_ptr); - if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, NULL)) { + if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, &index)) { return; } if (prop == NULL) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 0e608bdf903..3c226338bfd 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -531,7 +531,7 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll /* If we came from layer collection we don't go deeper, view layer * builder takes care of going deeper. * - * NOTE: Do early output before tagging build as done, so possbile + * NOTE: Do early output before tagging build as done, so possible * subsequent builds from outside of the layer collection properly * recurses into all the nested objects and collections. */ return; @@ -1531,8 +1531,9 @@ void DepsgraphRelationBuilder::build_driver_id_property(ID *id, const char *rna_ } PointerRNA id_ptr, ptr; PropertyRNA *prop; + int index; RNA_id_pointer_create(id, &id_ptr); - if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, NULL)) { + if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, &index)) { return; } if (prop == NULL) { diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 392514990e3..b73a3c08e10 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -638,7 +638,7 @@ void graph_id_tag_update( * This way IDs in the undo steps will have this flag preserved, making it possible to restore * all needed tags when new dependency graph is created on redo. * This is the only way to ensure modifications to animation data (such as keyframes i.e.) - * properly triggers animation update for the newely constructed dependency graph on redo (while + * properly triggers animation update for the newly constructed dependency graph on redo (while * usually newly created dependency graph skips animation update to avoid loss of unkeyed * changes). */ if (update_source == DEG_UPDATE_SOURCE_USER_EDIT) { 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 1f310957896..1f9c12f604d 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 @@ -1027,7 +1027,7 @@ class SceneBackup { * * NOTE: Scene can not disappear after relations update, because otherwise the entire dependency * graph will be gone. This means we don't need to compare original scene pointer, or worry about - * freeing those if they cant' be restorted: we just copy them over to a new scene. */ + * freeing those if they cant' be restored: we just copy them over to a new scene. */ void *sound_scene; void *playback_handle; void *sound_scrub_handle; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 27328084f31..1112a7a87db 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC intern/draw_anim_viz.c intern/draw_armature.c intern/draw_cache.c + intern/draw_cache_extract_mesh.c intern/draw_cache_impl_curve.c intern/draw_cache_impl_displist.c intern/draw_cache_impl_lattice.c @@ -69,6 +70,7 @@ set(SRC intern/draw_manager_shader.c intern/draw_manager_text.c intern/draw_manager_texture.c + intern/draw_select_buffer.c intern/draw_view.c modes/edit_armature_mode.c modes/edit_curve_mode.c @@ -127,14 +129,14 @@ set(SRC engines/gpencil/gpencil_engine.h engines/gpencil/gpencil_render.c engines/gpencil/gpencil_shader_fx.c - engines/select/select_engine.c engines/select/select_draw_utils.c - engines/select/select_buffer.c + engines/select/select_engine.c DRW_engine.h DRW_select_buffer.h intern/DRW_render.h intern/draw_cache.h + intern/draw_cache_extract.h intern/draw_cache_impl.h intern/draw_cache_inline.h intern/draw_common.h diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 53cec599b82..58cf0c2a8e0 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -141,9 +141,7 @@ void DRW_draw_depth_object(struct ARegion *ar, void DRW_draw_select_id(struct Depsgraph *depsgraph, struct ARegion *ar, struct View3D *v3d, - struct Base **bases, - const uint bases_len, - short select_mode); + const struct rcti *rect); /* grease pencil render */ bool DRW_render_check_grease_pencil(struct Depsgraph *depsgraph); @@ -168,8 +166,8 @@ void DRW_opengl_context_disable_ex(bool restore); void DRW_opengl_render_context_enable(void *re_gl_context); void DRW_opengl_render_context_disable(void *re_gl_context); -void DRW_gawain_render_context_enable(void *re_gpu_context); -void DRW_gawain_render_context_disable(void *re_gpu_context); +void DRW_gpu_render_context_enable(void *re_gpu_context); +void DRW_gpu_render_context_disable(void *re_gpu_context); void DRW_deferred_shader_remove(struct GPUMaterial *mat); diff --git a/source/blender/draw/DRW_select_buffer.h b/source/blender/draw/DRW_select_buffer.h index cc3cb94175a..4aa1c403710 100644 --- a/source/blender/draw/DRW_select_buffer.h +++ b/source/blender/draw/DRW_select_buffer.h @@ -33,33 +33,105 @@ struct View3D; struct ViewLayer; struct rcti; -/* select_buffer.c */ -void DRW_select_buffer_context_create(struct Base **bases, - const uint bases_len, - short select_mode); +typedef struct SELECTID_ObjectData { + DrawData dd; + + uint drawn_index; + bool is_drawn; +} SELECTID_ObjectData; + +struct ObjectOffsets { + /* For convenience only. */ + union { + uint offset; + uint face_start; + }; + union { + uint face; + uint edge_start; + }; + union { + uint edge; + uint vert_start; + }; + uint vert; +}; + +struct SELECTID_Context { + struct GPUFrameBuffer *framebuffer_select_id; + struct GPUTexture *texture_u32; + + /* All context objects */ + struct Object **objects; + uint objects_len; + + /* Array with only drawn objects. When a new object is found within the rect, + * it is added to the end of the list. + * The list is reset to any viewport or context update. */ + struct ObjectOffsets *index_offsets; + struct Object **objects_drawn; + uint objects_drawn_len; + + /** Total number of element indices `index_offsets[object_drawn_len - 1].vert`. */ + uint index_drawn_len; + + short select_mode; + + /* To check for updates. */ + float persmat[4][4]; + bool is_dirty; + + /* rect is used to check which objects whose indexes need to be drawn. */ + rcti last_rect; +}; + +/* draw_select_buffer.c */ bool DRW_select_buffer_elem_get(const uint sel_id, uint *r_elem, uint *r_base_index, char *r_elem_type); -uint DRW_select_buffer_context_offset_for_object_elem(const uint base_index, char elem_type); -uint *DRW_select_buffer_read(const struct rcti *rect, uint *r_buf_len); -void DRW_draw_select_id_object(struct Depsgraph *depsgraph, - struct ViewLayer *view_layer, - struct ARegion *ar, - struct View3D *v3d, - struct Object *ob, - short select_mode); -uint *DRW_select_buffer_bitmap_from_rect(const struct rcti *rect, uint *r_bitmap_len); -uint *DRW_select_buffer_bitmap_from_circle(const int center[2], +uint DRW_select_buffer_context_offset_for_object_elem(struct Depsgraph *depsgraph, + struct Object *object, + char elem_type); +uint *DRW_select_buffer_read(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const rcti *rect, + uint *r_buf_len); +uint *DRW_select_buffer_bitmap_from_rect(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const struct rcti *rect, + uint *r_bitmap_len); +uint *DRW_select_buffer_bitmap_from_circle(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const int center[2], const int radius, uint *r_bitmap_len); -uint *DRW_select_buffer_bitmap_from_poly(const int poly[][2], +uint *DRW_select_buffer_bitmap_from_poly(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const int poly[][2], const int poly_len, - const struct rcti *rect); -uint DRW_select_buffer_sample_point(const int center[2]); -uint DRW_select_buffer_find_nearest_to_point(const int center[2], + const struct rcti *rect, + uint *r_bitmap_len); +uint DRW_select_buffer_sample_point(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const int center[2]); +uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const int center[2], const uint id_min, const uint id_max, uint *dist); +void DRW_select_buffer_context_create(struct Base **bases, + const uint bases_len, + short select_mode); + +/* select_engine.c */ +struct SELECTID_Context *DRW_select_engine_context_get(void); #endif /* __DRW_SELECT_BUFFER_H__ */ diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 93521c71127..0ca1e0b2858 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -509,7 +509,7 @@ void EEVEE_downsample_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int le } /** - * Simple downsampling algorithm for cubemap. Reconstruct mip chain up to mip level. + * Simple down-sampling algorithm for cubemap. Reconstruct mip chain up to mip level. */ void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level) { @@ -580,7 +580,7 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) /* NOTE: Lookdev drawing happens before TAA but after * motion blur and dof to avoid distortions. * Velocity resolve use a hack to exclude lookdev - * spheres from creating shimering reprojection vectors. */ + * spheres from creating shimmering re-projection vectors. */ EEVEE_lookdev_draw(vedata); EEVEE_velocity_resolve(vedata); diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index b36ad540ef9..ab4eb7b8532 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -295,7 +295,13 @@ static void eevee_draw_background(void *vedata) EEVEE_volumes_resolve(sldata, vedata); /* Transparent */ + /* TODO(fclem): should be its own Framebuffer. + * This is needed because dualsource blending only works with 1 color buffer. */ + GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0); + GPU_framebuffer_bind(fbl->main_color_fb); DRW_draw_pass(psl->transparent_pass); + GPU_framebuffer_bind(fbl->main_fb); + GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth); /* Post Process */ DRW_stats_group_start("Post FX"); diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index c82a112b343..275e185a0e4 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -80,8 +80,8 @@ extern void DRW_opengl_context_disable(void); extern void DRW_opengl_render_context_enable(void *re_gl_context); extern void DRW_opengl_render_context_disable(void *re_gl_context); -extern void DRW_gawain_render_context_enable(void *re_gpu_context); -extern void DRW_gawain_render_context_disable(void *re_gpu_context); +extern void DRW_gpu_render_context_enable(void *re_gpu_context); +extern void DRW_gpu_render_context_disable(void *re_gpu_context); typedef struct EEVEE_LightBake { Depsgraph *depsgraph; @@ -412,7 +412,7 @@ static void eevee_lightbake_context_enable(EEVEE_LightBake *lbake) if (lbake->gpu_context == NULL) { lbake->gpu_context = GPU_context_create(0); } - DRW_gawain_render_context_enable(lbake->gpu_context); + DRW_gpu_render_context_enable(lbake->gpu_context); } else { DRW_opengl_context_enable(); @@ -422,7 +422,7 @@ static void eevee_lightbake_context_enable(EEVEE_LightBake *lbake) static void eevee_lightbake_context_disable(EEVEE_LightBake *lbake) { if (lbake->gl_context) { - DRW_gawain_render_context_disable(lbake->gpu_context); + DRW_gpu_render_context_disable(lbake->gpu_context); DRW_opengl_render_context_disable(lbake->gl_context); } else { @@ -654,7 +654,7 @@ static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake) if (lbake->gl_context) { DRW_opengl_render_context_enable(lbake->gl_context); - DRW_gawain_render_context_enable(lbake->gpu_context); + DRW_gpu_render_context_enable(lbake->gpu_context); } else if (!lbake->resource_only) { DRW_opengl_context_enable(); @@ -675,8 +675,8 @@ static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake) } if (lbake->gpu_context) { - DRW_gawain_render_context_disable(lbake->gpu_context); - DRW_gawain_render_context_enable(lbake->gpu_context); + DRW_gpu_render_context_disable(lbake->gpu_context); + DRW_gpu_render_context_enable(lbake->gpu_context); GPU_context_discard(lbake->gpu_context); } diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 2026b44fe87..8b1309e8537 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -1213,7 +1213,7 @@ void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata, DRW_draw_pass(psl->probe_visibility_compute); } -/* Actually a simple downsampling */ +/* Actually a simple down-sampling. */ static void downsample_planar(void *vedata, int level) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index d23287264b3..0bfc23b8354 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -779,7 +779,7 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli) /** * Special ball distribution: - * Point are distributed in a way that when they are orthogonaly + * Point are distributed in a way that when they are orthogonally * projected into any plane, the resulting distribution is (close to) * a uniform disc distribution. */ @@ -1166,9 +1166,8 @@ static void eevee_shadow_cascade_setup(Object *ob, /* Compute offset. */ sub_v2_v2(shadow_texco, shadow_origin); - mul_v2_fl(shadow_texco, - (2.0f * sh_data->radius[c]) / - linfo->shadow_cascade_size); /* Texture to light space. */ + /* Texture to light space. */ + mul_v2_fl(shadow_texco, (2.0f * sh_data->radius[c]) / linfo->shadow_cascade_size); /* Apply offset. */ add_v2_v2(center, shadow_texco); diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index e6e699bef10..f52fcf31267 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -75,22 +75,21 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, if (LOOK_DEV_OVERLAY_ENABLED(v3d)) { /* Viewport / Spheres size. */ - rcti rect; - ED_region_visible_rect(draw_ctx->ar, &rect); + const rcti *rect = ED_region_visible_rect(draw_ctx->ar); /* Make the viewport width scale the lookdev spheres a bit. * Scale between 1000px and 2000px. */ const float viewport_scale = clamp_f( - BLI_rcti_size_x(&rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f); + BLI_rcti_size_x(rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f); const int sphere_size = U.lookdev_sphere_size * U.dpi_fac * viewport_scale; - if (sphere_size != effects->sphere_size || rect.xmax != effects->anchor[0] || - rect.ymin != effects->anchor[1]) { + if (sphere_size != effects->sphere_size || rect->xmax != effects->anchor[0] || + rect->ymin != effects->anchor[1]) { /* If sphere size or anchor point moves, reset TAA to avoid ghosting issue. * This needs to happen early because we are changing taa_current_sample. */ effects->sphere_size = sphere_size; - effects->anchor[0] = rect.xmax; - effects->anchor[1] = rect.ymin; + effects->anchor[0] = rect->xmax; + effects->anchor[1] = rect->ymin; EEVEE_temporal_sampling_reset(vedata); } } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 61da9e21cc8..738745f3072 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -382,11 +382,6 @@ static void add_standard_uniforms(DRWShadingGroup *shgrp, LightCache *lcache = vedata->stl->g_data->light_cache; EEVEE_EffectsInfo *effects = vedata->stl->effects; - if (ssr_id == NULL) { - static int no_ssr = -1.0f; - ssr_id = &no_ssr; - } - DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo); @@ -394,6 +389,8 @@ static void add_standard_uniforms(DRWShadingGroup *shgrp, DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_int_copy(shgrp, "outputSssId", 1); + if (use_diffuse || use_glossy || use_refract) { DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool); @@ -411,7 +408,7 @@ static void add_standard_uniforms(DRWShadingGroup *shgrp, } if (use_glossy) { DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &vedata->txl->planar_pool); - DRW_shgroup_uniform_int(shgrp, "outputSsrId", ssr_id, 1); + DRW_shgroup_uniform_int_copy(shgrp, "outputSsrId", ssr_id ? *ssr_id : 0); } if (use_refract) { DRW_shgroup_uniform_float_copy( @@ -736,7 +733,6 @@ struct GPUMaterial *EEVEE_material_mesh_get(struct Scene *scene, Material *ma, EEVEE_Data *vedata, bool use_blend, - bool use_multiply, bool use_refract, bool use_translucency, int shadow_method) @@ -746,7 +742,6 @@ struct GPUMaterial *EEVEE_material_mesh_get(struct Scene *scene, int options = VAR_MAT_MESH; SET_FLAG_FROM_TEST(options, use_blend, VAR_MAT_BLEND); - SET_FLAG_FROM_TEST(options, use_multiply, VAR_MAT_MULT); SET_FLAG_FROM_TEST(options, use_refract, VAR_MAT_REFRACT); SET_FLAG_FROM_TEST(options, effects->sss_separate_albedo, VAR_MAT_SSSALBED); SET_FLAG_FROM_TEST(options, use_translucency, VAR_MAT_TRANSLUC); @@ -1191,15 +1186,11 @@ static void material_opaque(Material *ma, *shgrp_depth_clip = emsg->depth_clip_grp; /* This will have been created already, just perform a lookup. */ - *gpumat = (use_gpumat) ? EEVEE_material_mesh_get(scene, - ma, - vedata, - false, - false, - use_ssrefract, - use_translucency, - linfo->shadow_method) : - NULL; + *gpumat = + (use_gpumat) ? + EEVEE_material_mesh_get( + scene, ma, vedata, false, use_ssrefract, use_translucency, linfo->shadow_method) : + NULL; *gpumat_depth = (use_gpumat) ? EEVEE_material_mesh_depth_get( scene, ma, (ma->blend_method == MA_BM_HASHED), false) : NULL; @@ -1213,7 +1204,7 @@ static void material_opaque(Material *ma, /* Shading */ *gpumat = EEVEE_material_mesh_get( - scene, ma, vedata, false, false, use_ssrefract, use_translucency, linfo->shadow_method); + scene, ma, vedata, false, use_ssrefract, use_translucency, linfo->shadow_method); eGPUMaterialStatus status_mat_surface = GPU_material_status(*gpumat); @@ -1286,7 +1277,7 @@ static void material_opaque(Material *ma, switch (status_mat_surface) { case GPU_MAT_SUCCESS: { - static int no_ssr = -1; + static int no_ssr = 0; static int first_ssr = 1; int *ssr_id = (((effects->enabled_effects & EFFECT_SSR) != 0) && !use_ssrefract) ? &first_ssr : @@ -1426,22 +1417,16 @@ static void material_transparent(Material *ma, static float half = 0.5f; /* Shading */ - *gpumat = EEVEE_material_mesh_get(scene, - ma, - vedata, - true, - (ma->blend_method == MA_BM_MULTIPLY), - use_ssrefract, - false, - linfo->shadow_method); + *gpumat = EEVEE_material_mesh_get( + scene, ma, vedata, true, use_ssrefract, false, linfo->shadow_method); switch (GPU_material_status(*gpumat)) { case GPU_MAT_SUCCESS: { static int ssr_id = -1; /* TODO transparent SSR */ - bool use_blend = (ma->blend_method & MA_BM_BLEND) != 0; *shgrp = DRW_shgroup_material_create(*gpumat, psl->transparent_pass); + bool use_blend = true; bool use_diffuse = GPU_material_flag_get(*gpumat, GPU_MATFLAG_DIFFUSE); bool use_glossy = GPU_material_flag_get(*gpumat, GPU_MATFLAG_GLOSSY); bool use_refract = GPU_material_flag_get(*gpumat, GPU_MATFLAG_REFRACT); @@ -1487,27 +1472,12 @@ static void material_transparent(Material *ma, DRWState all_state = (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_CULL_BACK | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_DEPTH_EQUAL | - DRW_STATE_BLEND_ALPHA | DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_MUL); + DRW_STATE_BLEND_CUSTOM); - DRWState cur_state = DRW_STATE_WRITE_COLOR; + DRWState cur_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; cur_state |= (use_prepass) ? DRW_STATE_DEPTH_EQUAL : DRW_STATE_DEPTH_LESS_EQUAL; cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0; - switch (ma->blend_method) { - case MA_BM_ADD: - cur_state |= DRW_STATE_BLEND_ADD; - break; - case MA_BM_MULTIPLY: - cur_state |= DRW_STATE_BLEND_MUL; - break; - case MA_BM_BLEND: - cur_state |= DRW_STATE_BLEND_ALPHA; - break; - default: - BLI_assert(0); - break; - } - /* Disable other blend modes and use the one we want. */ DRW_shgroup_state_disable(*shgrp, all_state); DRW_shgroup_state_enable(*shgrp, cur_state); @@ -1583,8 +1553,6 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, &shgrp_depth_array[i], &shgrp_depth_clip_array[i]); break; - case MA_BM_ADD: - case MA_BM_MULTIPLY: case MA_BM_BLEND: material_transparent(ma_array[i], sldata, diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c index 2e56b64bd50..7209651a1d4 100644 --- a/source/blender/draw/engines/eevee/eevee_mist.c +++ b/source/blender/draw/engines/eevee/eevee_mist.c @@ -66,8 +66,9 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) } /* Create FrameBuffer. */ - DRW_texture_ensure_fullscreen_2d( - &txl->mist_accum, GPU_R32F, 0); /* Should be enough precision for many samples. */ + + /* Should be enough precision for many samples. */ + DRW_texture_ensure_fullscreen_2d(&txl->mist_accum, GPU_R32F, 0); GPU_framebuffer_ensure_config(&fbl->mist_accum_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->mist_accum)}); diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c index 49f62c6bf5e..924b3d3b19b 100644 --- a/source/blender/draw/engines/eevee/eevee_occlusion.c +++ b/source/blender/draw/engines/eevee/eevee_occlusion.c @@ -146,8 +146,8 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - DRW_texture_ensure_fullscreen_2d( - &txl->ao_accum, GPU_R32F, 0); /* Should be enough precision for many samples. */ + /* Should be enough precision for many samples. */ + DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, GPU_R32F, 0); GPU_framebuffer_ensure_config(&fbl->ao_accum_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)}); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 8aeddc72316..27edd44075b 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -897,7 +897,6 @@ struct GPUMaterial *EEVEE_material_mesh_get(struct Scene *scene, Material *ma, EEVEE_Data *vedata, bool use_blend, - bool use_multiply, bool use_refract, bool use_translucency, int shadow_method); diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index ebd13ef1cf5..f840fa23bd2 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -628,7 +628,11 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl /* Mist output */ EEVEE_mist_output_accumulate(sldata, vedata); /* Transparent */ + GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0); + GPU_framebuffer_bind(fbl->main_color_fb); DRW_draw_pass(psl->transparent_pass); + GPU_framebuffer_bind(fbl->main_fb); + GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth); /* Result Z */ eevee_render_result_z(rl, viewname, rect, vedata, sldata); /* Post Process */ diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index 274269846bc..7f795eaac2b 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -164,6 +164,19 @@ float sum(vec4 v) return dot(vec4(1.0), v); } +float avg(vec2 v) +{ + return dot(vec2(1.0 / 2.0), v); +} +float avg(vec3 v) +{ + return dot(vec3(1.0 / 3.0), v); +} +float avg(vec4 v) +{ + return dot(vec4(1.0 / 4.0), v); +} + float saturate(float a) { return clamp(a, 0.0, 1.0); @@ -716,6 +729,7 @@ float cone_cosine(float r) } /* --------- Closure ---------- */ + #ifdef VOLUMETRICS struct Closure { @@ -725,6 +739,8 @@ struct Closure { float anisotropy; }; +Closure nodetree_exec(void); /* Prototype */ + # define CLOSURE_DEFAULT Closure(vec3(0.0), vec3(0.0), vec3(0.0), 0.0) Closure closure_mix(Closure cl1, Closure cl2, float fac) @@ -758,7 +774,8 @@ Closure closure_emission(vec3 rgb) struct Closure { vec3 radiance; - float opacity; + vec3 transmittance; + float holdout; # ifdef USE_SSS vec4 sss_data; # ifdef USE_SSS_ALBEDO @@ -767,110 +784,117 @@ struct Closure { # endif vec4 ssr_data; vec2 ssr_normal; - int ssr_id; + int flag; }; -/* This is hacking ssr_id to tag transparent bsdf */ -# define TRANSPARENT_CLOSURE_FLAG -2 -# define REFRACT_CLOSURE_FLAG -3 -# define NO_SSR -999 +Closure nodetree_exec(void); /* Prototype */ + +# define FLAG_TEST(flag, val) (((flag) & (val)) != 0) + +# define CLOSURE_SSR_FLAG 1 +# define CLOSURE_SSS_FLAG 2 +# define CLOSURE_HOLDOUT_FLAG 4 # ifdef USE_SSS # ifdef USE_SSS_ALBEDO # define CLOSURE_DEFAULT \ - Closure(vec3(0.0), 1.0, vec4(0.0), vec3(0.0), vec4(0.0), vec2(0.0), -1) + Closure(vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec3(0.0), vec4(0.0), vec2(0.0), 0) # else -# define CLOSURE_DEFAULT Closure(vec3(0.0), 1.0, vec4(0.0), vec4(0.0), vec2(0.0), -1) +# define CLOSURE_DEFAULT \ + Closure(vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec4(0.0), vec2(0.0), 0) # endif # else -# define CLOSURE_DEFAULT Closure(vec3(0.0), 1.0, vec4(0.0), vec2(0.0), -1) +# define CLOSURE_DEFAULT Closure(vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec2(0.0), 0) # endif -uniform int outputSsrId; +uniform int outputSsrId = 1; +uniform int outputSssId = 1; -Closure closure_mix(Closure cl1, Closure cl2, float fac) +void closure_load_ssr_data( + vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout Closure cl) { - Closure cl; + /* Still encode to avoid artifacts in the SSR pass. */ + vec3 vN = normalize(mat3(ViewMatrix) * N); + cl.ssr_normal = normal_encode(vN, viewVec); + + if (ssr_id == outputSsrId) { + cl.ssr_data = vec4(ssr_spec, roughness); + cl.flag |= CLOSURE_SSR_FLAG; + } +} - if (cl1.ssr_id == TRANSPARENT_CLOSURE_FLAG) { - cl.ssr_normal = cl2.ssr_normal; - cl.ssr_data = cl2.ssr_data; - cl.ssr_id = cl2.ssr_id; # ifdef USE_SSS - cl1.sss_data = cl2.sss_data; +void closure_load_sss_data(float radius, + vec3 sss_radiance, # ifdef USE_SSS_ALBEDO - cl1.sss_albedo = cl2.sss_albedo; + vec3 sss_albedo, # endif -# endif - } - else if (cl2.ssr_id == TRANSPARENT_CLOSURE_FLAG) { - cl.ssr_normal = cl1.ssr_normal; - cl.ssr_data = cl1.ssr_data; - cl.ssr_id = cl1.ssr_id; -# ifdef USE_SSS - cl2.sss_data = cl1.sss_data; + int sss_id, + inout Closure cl) +{ + if (sss_id == outputSssId) { + cl.sss_data = vec4(sss_radiance, radius); # ifdef USE_SSS_ALBEDO - cl2.sss_albedo = cl1.sss_albedo; + cl.sss_albedo = sss_albedo; # endif -# endif - } - else if (cl1.ssr_id == outputSsrId) { - /* When mixing SSR don't blend roughness. - * - * It makes no sense to mix them really, so we take either one of them and - * tone down its specularity (ssr_data.xyz) while keeping its roughness (ssr_data.w). - */ - cl.ssr_data = mix(cl1.ssr_data.xyzw, vec4(vec3(0.0), cl1.ssr_data.w), fac); - cl.ssr_normal = cl1.ssr_normal; - cl.ssr_id = cl1.ssr_id; + cl.flag |= CLOSURE_SSS_FLAG; } else { - cl.ssr_data = mix(vec4(vec3(0.0), cl2.ssr_data.w), cl2.ssr_data.xyzw, fac); - cl.ssr_normal = cl2.ssr_normal; - cl.ssr_id = cl2.ssr_id; + cl.radiance += sss_radiance; +# ifdef USE_SSS_ALBEDO + cl.radiance += sss_radiance * sss_albedo; +# endif } +} +# endif - cl.opacity = mix(cl1.opacity, cl2.opacity, fac); - cl.radiance = mix(cl1.radiance * cl1.opacity, cl2.radiance * cl2.opacity, fac); - cl.radiance /= max(1e-8, cl.opacity); +Closure closure_mix(Closure cl1, Closure cl2, float fac) +{ + Closure cl; + cl.holdout = mix(cl1.holdout, cl2.holdout, fac); + cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac); + cl.radiance = mix(cl1.radiance, cl2.radiance, fac); + cl.flag = cl1.flag | cl2.flag; + cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac); + bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); + /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz).*/ + cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; + cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; # ifdef USE_SSS - /* Apply Mix on input */ - cl1.sss_data.rgb *= 1.0 - fac; - cl2.sss_data.rgb *= fac; - - /* Select biggest radius. */ - bool use_cl1 = (cl1.sss_data.a > cl2.sss_data.a); - cl.sss_data = (use_cl1) ? cl1.sss_data : cl2.sss_data; - + cl.sss_data = mix(cl1.sss_data, cl2.sss_data, fac); + bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); + /* It also does not make sense to mix SSS radius or albedo. */ + cl.sss_data.w = (use_cl1_sss) ? cl1.sss_data.w : cl2.sss_data.w; # ifdef USE_SSS_ALBEDO - /* TODO Find a solution to this. Dither? */ - cl.sss_albedo = (use_cl1) ? cl1.sss_albedo : cl2.sss_albedo; - /* Add radiance that was supposed to be filtered but was rejected. */ - cl.radiance += (use_cl1) ? cl2.sss_data.rgb * cl2.sss_albedo : cl1.sss_data.rgb * cl1.sss_albedo; -# else - /* Add radiance that was supposed to be filtered but was rejected. */ - cl.radiance += (use_cl1) ? cl2.sss_data.rgb : cl1.sss_data.rgb; + cl.sss_albedo = (use_cl1_sss) ? cl1.sss_albedo : cl2.sss_albedo; # endif # endif - return cl; } Closure closure_add(Closure cl1, Closure cl2) { - Closure cl = (cl1.ssr_id == outputSsrId) ? cl1 : cl2; + Closure cl; + cl.transmittance = cl1.transmittance + cl2.transmittance; cl.radiance = cl1.radiance + cl2.radiance; + cl.holdout = cl1.holdout + cl2.holdout; + cl.flag = cl1.flag | cl2.flag; + cl.ssr_data = cl1.ssr_data + cl2.ssr_data; + bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); + /* When mixing SSR don't blend roughness and normals.*/ + cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; + cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; + # ifdef USE_SSS - cl.sss_data = (cl1.sss_data.a > 0.0) ? cl1.sss_data : cl2.sss_data; - /* Add radiance that was supposed to be filtered but was rejected. */ - cl.radiance += (cl1.sss_data.a > 0.0) ? cl2.sss_data.rgb : cl1.sss_data.rgb; + cl.sss_data = cl1.sss_data + cl2.sss_data; + bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); + /* It also does not make sense to mix SSS radius or albedo. */ + cl.sss_data.w = (use_cl1_sss) ? cl1.sss_data.w : cl2.sss_data.w; # ifdef USE_SSS_ALBEDO - /* TODO Find a solution to this. Dither? */ - cl.sss_albedo = (cl1.sss_data.a > 0.0) ? cl1.sss_albedo : cl2.sss_albedo; + cl.sss_albedo = (use_cl1_sss) ? cl1.sss_albedo : cl2.sss_albedo; # endif # endif - cl.opacity = saturate(cl1.opacity + cl2.opacity); return cl; } @@ -883,19 +907,23 @@ Closure closure_emission(vec3 rgb) /* Breaking this across multiple lines causes issues for some older GLSL compilers. */ /* clang-format off */ -# if defined(MESH_SHADER) && !defined(USE_ALPHA_HASH) && !defined(USE_ALPHA_CLIP) && !defined(SHADOW_SHADER) && !defined(USE_MULTIPLY) +# if defined(MESH_SHADER) && !defined(USE_ALPHA_HASH) && !defined(USE_ALPHA_CLIP) && !defined(SHADOW_SHADER) /* clang-format on */ -layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 ssrNormals; +# ifndef USE_ALPHA_BLEND +layout(location = 0) out vec4 outRadiance; +layout(location = 1) out vec2 ssrNormals; layout(location = 2) out vec4 ssrData; -# ifdef USE_SSS +# ifdef USE_SSS layout(location = 3) out vec4 sssData; -# ifdef USE_SSS_ALBEDO +# ifdef USE_SSS_ALBEDO layout(location = 4) out vec4 sssAlbedo; -# endif /* USE_SSS_ALBEDO */ -# endif /* USE_SSS */ - -Closure nodetree_exec(void); /* Prototype */ +# endif +# endif +# else /* USE_ALPHA_BLEND */ +/* Use dual source blending to be able to make a whole range of effects. */ +layout(location = 0, index = 0) out vec4 outRadiance; +layout(location = 0, index = 1) out vec4 outTransmittance; +# endif /* USE_ALPHA_BLEND */ # if defined(USE_ALPHA_BLEND) /* Prototype because this file is included before volumetric_lib.glsl */ @@ -909,27 +937,26 @@ void volumetric_resolve(vec2 frag_uvs, void main() { Closure cl = nodetree_exec(); -# ifndef USE_ALPHA_BLEND - /* Prevent alpha hash material writing into alpha channel. */ - cl.opacity = 1.0; -# endif -# if defined(USE_ALPHA_BLEND) + float holdout = 1.0 - saturate(cl.holdout); + +# ifdef USE_ALPHA_BLEND vec2 uvs = gl_FragCoord.xy * volCoordScale.zw; - vec3 transmittance, scattering; - volumetric_resolve(uvs, gl_FragCoord.z, transmittance, scattering); - fragColor.rgb = cl.radiance * transmittance + scattering; - fragColor.a = cl.opacity; -# else - fragColor = vec4(cl.radiance, cl.opacity); -# endif + vec3 vol_transmit, vol_scatter; + volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter); - ssrNormals = cl.ssr_normal.xyyy; + float transmit = saturate(avg(cl.transmittance)); + outRadiance = vec4(cl.radiance * vol_transmit + vol_scatter, (1.0 - transmit) * holdout); + outTransmittance = vec4(cl.transmittance, transmit * holdout); +# else + outRadiance = vec4(cl.radiance, holdout); + ssrNormals = cl.ssr_normal; ssrData = cl.ssr_data; -# ifdef USE_SSS +# ifdef USE_SSS sssData = cl.sss_data; -# ifdef USE_SSS_ALBEDO +# ifdef USE_SSS_ALBEDO sssAlbedo = cl.sss_albedo.rgbb; +# endif # endif # endif @@ -945,9 +972,9 @@ void main() # endif # ifdef USE_SSS_ALBEDO - fragColor.rgb += cl.sss_data.rgb * cl.sss_albedo.rgb * fac; + outRadiance.rgb += cl.sss_data.rgb * cl.sss_albedo.rgb * fac; # else - fragColor.rgb += cl.sss_data.rgb * fac; + outRadiance.rgb += cl.sss_data.rgb * fac; # endif # endif } @@ -955,18 +982,3 @@ void main() # endif /* MESH_SHADER && !SHADOW_SHADER */ #endif /* VOLUMETRICS */ - -Closure nodetree_exec(void); /* Prototype */ - -/* TODO find a better place */ -#ifdef USE_MULTIPLY - -out vec4 fragColor; - -# define NODETREE_EXEC -void main() -{ - Closure cl = nodetree_exec(); - fragColor = vec4(mix(vec3(1.0), cl.radiance, cl.opacity), 1.0); -} -#endif diff --git a/source/blender/draw/engines/eevee/shaders/default_frag.glsl b/source/blender/draw/engines/eevee/shaders/default_frag.glsl index 64a1c725a6b..1f60661d234 100644 --- a/source/blender/draw/engines/eevee/shaders/default_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/default_frag.glsl @@ -33,15 +33,13 @@ Closure nodetree_exec(void) vec3 out_diff, out_spec, ssr_spec; eevee_closure_default(N, albedo, f0, f90, 1, roughness, 1.0, out_diff, out_spec, ssr_spec); - Closure result = Closure(out_spec + out_diff * albedo, - 1.0, - vec4(ssr_spec, roughness), - normal_encode(vN, viewCameraVec), - 0); + Closure cl = CLOSURE_DEFAULT; + cl.radiance = out_spec + out_diff * albedo; + closure_load_ssr_data(ssr_spec, roughness, N, viewCameraVec, 1, cl); #ifdef LOOKDEV gl_FragDepth = 0.0; #endif - return result; + return cl; } diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl index 2d14f52d7e6..c3643cccbfc 100644 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl @@ -393,8 +393,8 @@ vec3 light_translucent(LightData ld, vec3 W, vec3 N, vec4 l_vector, float scale) return vec3(0.0); } - float range = abs(data.sh_far - - data.sh_near); /* Same factor as in get_cascade_world_distance(). */ + /* Same factor as in get_cascade_world_distance(). */ + float range = abs(data.sh_far - data.sh_near); vec4 shpos = shadows_cascade_data[scd_id].shadowmat[int(id)] * vec4(W, 1.0); float dist = shpos.z * range; diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl index e7b31b94f81..dea6bc020ec 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl @@ -71,14 +71,16 @@ void main() Closure cl = nodetree_exec(); + float opacity = saturate(1.0 - avg(cl.transmittance)); + # if defined(USE_ALPHA_HASH) /* Hashed Alpha Testing */ - if (cl.opacity < hashed_alpha_threshold(worldPosition)) { + if (opacity < hashed_alpha_threshold(worldPosition)) { discard; } # elif defined(USE_ALPHA_CLIP) /* Alpha clip */ - if (cl.opacity <= alphaThreshold) { + if (opacity <= alphaThreshold) { discard; } # endif diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 16162645f3d..efe67e1ead0 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -564,6 +564,8 @@ static void gpencil_add_draw_data(void *vedata, Object *ob) GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const DRWContextState *draw_ctx = DRW_context_state_get(); + const View3D *v3d = draw_ctx->v3d; int i = stl->g_data->gp_cache_used - 1; tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i]; @@ -580,7 +582,9 @@ static void gpencil_add_draw_data(void *vedata, Object *ob) /* FX passses */ cache_ob->has_fx = false; - if ((!stl->storage->simplify_fx) && (!ELEM(cache_ob->shading_type[0], OB_WIRE, OB_SOLID)) && + if ((!stl->storage->simplify_fx) && + ((!ELEM(cache_ob->shading_type[0], OB_WIRE, OB_SOLID)) || + ((v3d->spacetype != SPACE_VIEW3D))) && (BKE_shaderfx_has_gpencil(ob))) { cache_ob->has_fx = true; if ((!stl->storage->simplify_fx) && (!is_multiedit)) { diff --git a/source/blender/draw/engines/select/select_draw_utils.c b/source/blender/draw/engines/select/select_draw_utils.c index c3ee7f962a1..f3b6d324f33 100644 --- a/source/blender/draw/engines/select/select_draw_utils.c +++ b/source/blender/draw/engines/select/select_draw_utils.c @@ -23,6 +23,8 @@ */ #include "BKE_editmesh.h" +#include "BKE_mesh.h" +#include "BKE_object.h" #include "DNA_mesh_types.h" #include "DNA_scene_types.h" @@ -32,6 +34,8 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "DRW_select_buffer.h" + #include "draw_cache_impl.h" #include "select_private.h" @@ -40,7 +44,7 @@ /** \name Draw Utilities * \{ */ -void draw_select_framebuffer_select_id_setup(struct SELECTID_Context *select_ctx) +static void select_id_framebuffer_setup(struct SELECTID_Context *select_ctx) { DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); int size[2]; @@ -71,6 +75,32 @@ void draw_select_framebuffer_select_id_setup(struct SELECTID_Context *select_ctx } } +/* Remove all tags from drawn or culled objects. */ +void select_id_context_clear(struct SELECTID_Context *select_ctx) +{ + select_ctx->objects_drawn_len = 0; + select_ctx->index_drawn_len = 1; + select_id_framebuffer_setup(select_ctx); + GPU_framebuffer_bind(select_ctx->framebuffer_select_id); + GPU_framebuffer_clear_color_depth( + select_ctx->framebuffer_select_id, (const float[4]){0.0f}, 1.0f); +} + +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) { + /* Use Object Texture Space. */ + bb = BKE_mesh_texspace_get(em->mesh_eval_cage, NULL, NULL, NULL); + } + else { + bb = BKE_object_boundbox_get(obj); + } + copy_v3_v3(r_min, bb->vec[0]); + copy_v3_v3(r_max, bb->vec[6]); +} + short select_id_get_object_select_mode(Scene *scene, Object *ob) { short r_select_mode = 0; @@ -121,32 +151,33 @@ static void draw_select_id_edit_mesh(SELECTID_StorageList *stl, BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE | BM_FACE); - struct GPUBatch *geom_faces; - DRWShadingGroup *face_shgrp; if (select_mode & SCE_SELECT_FACE) { - geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(me); - face_shgrp = DRW_shgroup_create_sub(stl->g_data->shgrp_face_flat); + struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(me); + DRWShadingGroup *face_shgrp = DRW_shgroup_create_sub(stl->g_data->shgrp_face_flat); DRW_shgroup_uniform_int_copy(face_shgrp, "offset", *(int *)&initial_offset); + DRW_shgroup_call_no_cull(face_shgrp, geom_faces, ob); if (draw_facedot) { struct GPUBatch *geom_facedots = DRW_mesh_batch_cache_get_facedots_with_select_id(me); - DRW_shgroup_call(face_shgrp, geom_facedots, ob); + DRW_shgroup_call_no_cull(face_shgrp, geom_facedots, ob); } *r_face_offset = initial_offset + em->bm->totface; } else { - geom_faces = DRW_mesh_batch_cache_get_surface(me); - face_shgrp = stl->g_data->shgrp_face_unif; + if (ob->dt >= OB_SOLID) { + struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_surface(me); + DRWShadingGroup *face_shgrp = stl->g_data->shgrp_face_unif; + DRW_shgroup_call_no_cull(face_shgrp, geom_faces, ob); + } *r_face_offset = initial_offset; } - DRW_shgroup_call(face_shgrp, geom_faces, ob); /* Unlike faces, only draw edges if edge select mode. */ if (select_mode & SCE_SELECT_EDGE) { struct GPUBatch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(me); DRWShadingGroup *edge_shgrp = DRW_shgroup_create_sub(stl->g_data->shgrp_edge); DRW_shgroup_uniform_int_copy(edge_shgrp, "offset", *(int *)r_face_offset); - DRW_shgroup_call(edge_shgrp, geom_edges, ob); + DRW_shgroup_call_no_cull(edge_shgrp, geom_edges, ob); *r_edge_offset = *r_face_offset + em->bm->totedge; } else { @@ -160,7 +191,7 @@ static void draw_select_id_edit_mesh(SELECTID_StorageList *stl, struct GPUBatch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(me); DRWShadingGroup *vert_shgrp = DRW_shgroup_create_sub(stl->g_data->shgrp_vert); DRW_shgroup_uniform_int_copy(vert_shgrp, "offset", *(int *)r_edge_offset); - DRW_shgroup_call(vert_shgrp, geom_verts, ob); + DRW_shgroup_call_no_cull(vert_shgrp, geom_verts, ob); *r_vert_offset = *r_edge_offset + em->bm->totvert; } else { @@ -190,13 +221,13 @@ static void draw_select_id_mesh(SELECTID_StorageList *stl, face_shgrp = stl->g_data->shgrp_face_unif; *r_face_offset = initial_offset; } - DRW_shgroup_call(face_shgrp, geom_faces, ob); + DRW_shgroup_call_no_cull(face_shgrp, geom_faces, ob); if (select_mode & SCE_SELECT_EDGE) { struct GPUBatch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(me); DRWShadingGroup *edge_shgrp = DRW_shgroup_create_sub(stl->g_data->shgrp_edge); DRW_shgroup_uniform_int_copy(edge_shgrp, "offset", *(int *)r_face_offset); - DRW_shgroup_call(edge_shgrp, geom_edges, ob); + DRW_shgroup_call_no_cull(edge_shgrp, geom_edges, ob); *r_edge_offset = *r_face_offset + me->totedge; } else { @@ -207,7 +238,7 @@ static void draw_select_id_mesh(SELECTID_StorageList *stl, struct GPUBatch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(me); DRWShadingGroup *vert_shgrp = DRW_shgroup_create_sub(stl->g_data->shgrp_vert); DRW_shgroup_uniform_int_copy(vert_shgrp, "offset", *r_edge_offset); - DRW_shgroup_call(vert_shgrp, geom_verts, ob); + DRW_shgroup_call_no_cull(vert_shgrp, geom_verts, ob); *r_vert_offset = *r_edge_offset + me->totvert; } else { diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 3e4e4861f50..d0347891120 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -24,11 +24,13 @@ #include "DNA_screen_types.h" -#include "GPU_shader.h" - #include "UI_resources.h" #include "DRW_engine.h" +#include "DRW_select_buffer.h" + +#include "draw_cache_impl.h" +#include "draw_manager.h" #include "select_private.h" #include "select_engine.h" @@ -40,6 +42,7 @@ static struct { SELECTID_Shaders sh_data[GPU_SHADER_CFG_LEN]; struct SELECTID_Context context; + uint runtime_new_objects; } e_data = {{{NULL}}}; /* Engine data */ /* Shaders */ @@ -89,8 +92,23 @@ static void select_engine_init(void *vedata) } { + /* Create view from a subregion */ + const DRWView *view_default = DRW_view_default_get(); + float viewmat[4][4], winmat[4][4], winmat_subregion[4][4]; + DRW_view_viewmat_get(view_default, viewmat, false); + DRW_view_winmat_get(view_default, winmat, false); + projmat_from_subregion(winmat, + (int[2]){draw_ctx->ar->winx, draw_ctx->ar->winy}, + e_data.context.last_rect.xmin, + e_data.context.last_rect.xmax, + e_data.context.last_rect.ymin, + e_data.context.last_rect.ymax, + winmat_subregion); + + stl->g_data->view_subregion = DRW_view_create(viewmat, winmat_subregion, NULL, NULL, NULL); + /* Create view with depth offset */ - stl->g_data->view_faces = (DRWView *)DRW_view_default_get(); + stl->g_data->view_faces = (DRWView *)view_default; stl->g_data->view_edges = DRW_view_create_with_zoffset(draw_ctx->rv3d, 1.0f); stl->g_data->view_verts = DRW_view_create_with_zoffset(draw_ctx->rv3d, 1.1f); } @@ -106,11 +124,19 @@ static void select_cache_init(void *vedata) if (e_data.context.select_mode == -1) { e_data.context.select_mode = select_id_get_object_select_mode(draw_ctx->scene, - OBACT(draw_ctx->view_layer)); + draw_ctx->obact); BLI_assert(e_data.context.select_mode != 0); } { + psl->depth_only_pass = DRW_pass_create("Depth Only Pass", DRW_STATE_DEFAULT); + stl->g_data->shgrp_depth_only = DRW_shgroup_create(sh_data->select_id_uniform, + psl->depth_only_pass); + + if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { + DRW_shgroup_state_enable(stl->g_data->shgrp_depth_only, DRW_STATE_CLIP_PLANES); + } + psl->select_id_face_pass = DRW_pass_create("Face Pass", DRW_STATE_DEFAULT); if (e_data.context.select_mode & SCE_SELECT_FACE) { @@ -156,28 +182,82 @@ static void select_cache_init(void *vedata) } } - e_data.context.last_object_drawn = 0; - e_data.context.last_index_drawn = 1; + /* Check if the viewport has changed. */ + float(*persmat)[4] = draw_ctx->rv3d->persmat; + e_data.context.is_dirty = !compare_m4m4(e_data.context.persmat, persmat, FLT_EPSILON); + if (e_data.context.is_dirty) { + copy_m4_m4(e_data.context.persmat, persmat); + select_id_context_clear(&e_data.context); + } + e_data.runtime_new_objects = 0; } static void select_cache_populate(void *vedata, Object *ob) { + SELECTID_StorageList *stl = ((SELECTID_Data *)vedata)->stl; const DRWContextState *draw_ctx = DRW_context_state_get(); - struct BaseOffset *base_ofs = &e_data.context.index_offsets[e_data.context.last_object_drawn++]; - uint offset = e_data.context.last_index_drawn; + SELECTID_ObjectData *sel_data = (SELECTID_ObjectData *)DRW_drawdata_get( + &ob->id, &draw_engine_select_type); + + if (!e_data.context.is_dirty && sel_data && sel_data->is_drawn) { + /* The object indices have already been drawn. Fill depth pass. + * Opti: Most of the time this depth pass is not used. */ + struct Mesh *me = ob->data; + if (e_data.context.select_mode & SCE_SELECT_FACE) { + struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(me); + DRW_shgroup_call_obmat(stl->g_data->shgrp_depth_only, geom_faces, ob->obmat); + } + else if (ob->dt >= OB_SOLID) { + struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_surface(me); + DRW_shgroup_call_obmat(stl->g_data->shgrp_depth_only, geom_faces, ob->obmat); + } + + if (e_data.context.select_mode & SCE_SELECT_EDGE) { + struct GPUBatch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(me); + DRW_shgroup_call_obmat(stl->g_data->shgrp_depth_only, geom_edges, ob->obmat); + } + + if (e_data.context.select_mode & SCE_SELECT_VERTEX) { + struct GPUBatch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(me); + DRW_shgroup_call_obmat(stl->g_data->shgrp_depth_only, geom_verts, ob->obmat); + } + return; + } - select_id_draw_object(vedata, - draw_ctx->v3d, - ob, - e_data.context.select_mode, - offset, - &base_ofs->vert, - &base_ofs->edge, - &base_ofs->face); + float min[3], max[3]; + select_id_object_min_max(ob, min, max); - base_ofs->offset = offset; - e_data.context.last_index_drawn = base_ofs->vert; + if (DRW_culling_min_max_test(stl->g_data->view_subregion, ob->obmat, min, max)) { + if (sel_data == NULL) { + sel_data = (SELECTID_ObjectData *)DRW_drawdata_ensure( + &ob->id, &draw_engine_select_type, sizeof(SELECTID_ObjectData), NULL, NULL); + } + sel_data->drawn_index = e_data.context.objects_drawn_len; + sel_data->is_drawn = true; + + struct ObjectOffsets *ob_offsets = + &e_data.context.index_offsets[e_data.context.objects_drawn_len]; + + uint offset = e_data.context.index_drawn_len; + select_id_draw_object(vedata, + draw_ctx->v3d, + ob, + e_data.context.select_mode, + offset, + &ob_offsets->vert, + &ob_offsets->edge, + &ob_offsets->face); + + ob_offsets->offset = offset; + e_data.context.index_drawn_len = ob_offsets->vert; + e_data.context.objects_drawn[e_data.context.objects_drawn_len] = ob; + e_data.context.objects_drawn_len++; + e_data.runtime_new_objects++; + } + else if (sel_data) { + sel_data->is_drawn = false; + } } static void select_draw_scene(void *vedata) @@ -185,17 +265,26 @@ static void select_draw_scene(void *vedata) SELECTID_StorageList *stl = ((SELECTID_Data *)vedata)->stl; SELECTID_PassList *psl = ((SELECTID_Data *)vedata)->psl; - /* Setup framebuffer */ - draw_select_framebuffer_select_id_setup(&e_data.context); - GPU_framebuffer_bind(e_data.context.framebuffer_select_id); + if (!e_data.runtime_new_objects) { + /* Nothing new needs to be drawn. */ + return; + } /* dithering and AA break color coding, so disable */ glDisable(GL_DITHER); - GPU_framebuffer_clear_color_depth( - e_data.context.framebuffer_select_id, (const float[4]){0.0f}, 1.0f); - DRW_view_set_active(stl->g_data->view_faces); + + if (!DRW_pass_is_empty(psl->depth_only_pass)) { + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + GPU_framebuffer_bind(dfbl->depth_only_fb); + GPU_framebuffer_clear_depth(dfbl->depth_only_fb, 1.0f); + DRW_draw_pass(psl->depth_only_pass); + } + + /* Setup framebuffer */ + GPU_framebuffer_bind(e_data.context.framebuffer_select_id); + DRW_draw_pass(psl->select_id_face_pass); if (e_data.context.select_mode & SCE_SELECT_EDGE) { @@ -219,7 +308,9 @@ static void select_engine_free(void) DRW_TEXTURE_FREE_SAFE(e_data.context.texture_u32); GPU_FRAMEBUFFER_FREE_SAFE(e_data.context.framebuffer_select_id); + MEM_SAFE_FREE(e_data.context.objects); MEM_SAFE_FREE(e_data.context.index_offsets); + MEM_SAFE_FREE(e_data.context.objects_drawn); } /** \} */ @@ -272,7 +363,7 @@ RenderEngineType DRW_engine_viewport_select_type = { /** \name Exposed `select_private.h` functions * \{ */ -struct SELECTID_Context *select_context_get(void) +struct SELECTID_Context *DRW_select_engine_context_get(void) { return &e_data.context; } diff --git a/source/blender/draw/engines/select/select_private.h b/source/blender/draw/engines/select/select_private.h index 2104f1485e7..e48ce4314ae 100644 --- a/source/blender/draw/engines/select/select_private.h +++ b/source/blender/draw/engines/select/select_private.h @@ -32,6 +32,7 @@ typedef struct SELECTID_StorageList { } SELECTID_StorageList; typedef struct SELECTID_PassList { + struct DRWPass *depth_only_pass; struct DRWPass *select_id_face_pass; struct DRWPass *select_id_edge_pass; struct DRWPass *select_id_vert_pass; @@ -52,51 +53,21 @@ typedef struct SELECTID_Shaders { } SELECTID_Shaders; typedef struct SELECTID_PrivateData { + DRWShadingGroup *shgrp_depth_only; DRWShadingGroup *shgrp_face_unif; DRWShadingGroup *shgrp_face_flat; DRWShadingGroup *shgrp_edge; DRWShadingGroup *shgrp_vert; + DRWView *view_subregion; DRWView *view_faces; DRWView *view_edges; DRWView *view_verts; } SELECTID_PrivateData; /* Transient data */ -struct BaseOffset { - /* For convenience only. */ - union { - uint offset; - uint face_start; - }; - union { - uint face; - uint edge_start; - }; - union { - uint edge; - uint vert_start; - }; - uint vert; -}; - -struct SELECTID_Context { - struct GPUFrameBuffer *framebuffer_select_id; - struct GPUTexture *texture_u32; - - struct BaseOffset *index_offsets; - uint objects_len; - uint last_object_drawn; - /** Total number of items `base_array_index_offsets[bases_len - 1].vert`. */ - uint last_index_drawn; - - short select_mode; -}; - -/* select_engine.c */ -struct SELECTID_Context *select_context_get(void); - /* select_draw_utils.c */ -void draw_select_framebuffer_select_id_setup(struct SELECTID_Context *r_select_ctx); +void select_id_context_clear(struct SELECTID_Context *select_ctx); +void select_id_object_min_max(struct Object *obj, float r_min[3], float r_max[3]); short select_id_get_object_select_mode(Scene *scene, Object *ob); void select_id_draw_object(void *vedata, View3D *v3d, diff --git a/source/blender/draw/engines/workbench/workbench_studiolight.c b/source/blender/draw/engines/workbench/workbench_studiolight.c index 944bca73993..1a09498b228 100644 --- a/source/blender/draw/engines/workbench/workbench_studiolight.c +++ b/source/blender/draw/engines/workbench/workbench_studiolight.c @@ -82,8 +82,9 @@ void studiolight_update_world(WORKBENCH_PrivateData *wpd, -sl->spherical_harmonics_coefs[3][i], sl->spherical_harmonics_coefs[2][i], -sl->spherical_harmonics_coefs[1][i]); - mul_v3_fl(wd->spherical_harmonics_coefs[i + 1], - M_1_PI * 1.5f); /* 1.5f is to improve the contrast a bit. */ + + /* 1.5f is to improve the contrast a bit. */ + mul_v3_fl(wd->spherical_harmonics_coefs[i + 1], M_1_PI * 1.5f); } /* Precompute as much as we can. See shader code for derivation. */ diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index a8f67e10a4d..3379aa28d0f 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -597,6 +597,7 @@ bool DRW_view_is_persp_get(const DRWView *view); bool DRW_culling_sphere_test(const DRWView *view, const BoundSphere *bsphere); bool DRW_culling_box_test(const DRWView *view, const BoundBox *bbox); bool DRW_culling_plane_test(const DRWView *view, const float plane[4]); +bool DRW_culling_min_max_test(const DRWView *view, float obmat[4][4], float min[3], float max[3]); void DRW_culling_frustum_corners_get(const DRWView *view, BoundBox *corners); void DRW_culling_frustum_planes_get(const DRWView *view, float planes[6][4]); diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index e2e98a2db5a..520932bc429 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -4001,7 +4001,7 @@ GPUBatch *DRW_cache_cursor_get(bool crosshair_lines) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Batch Cache Impl. common +/** \name Batch Cache Implementation (common) * \{ */ void drw_batch_cache_validate(Object *ob) @@ -4033,7 +4033,7 @@ void drw_batch_cache_validate(Object *ob) void drw_batch_cache_generate_requested(Object *ob) { const DRWContextState *draw_ctx = DRW_context_state_get(); - const ToolSettings *ts = draw_ctx->scene->toolsettings; + const Scene *scene = draw_ctx->scene; const enum eContextObjectMode mode = CTX_data_mode_enum_ex( draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode); const bool is_paint_mode = ELEM( @@ -4047,13 +4047,13 @@ void drw_batch_cache_generate_requested(Object *ob) struct Mesh *mesh_eval = ob->runtime.mesh_eval; switch (ob->type) { case OB_MESH: - DRW_mesh_batch_cache_create_requested(ob, (Mesh *)ob->data, ts, is_paint_mode, use_hide); + DRW_mesh_batch_cache_create_requested(ob, (Mesh *)ob->data, scene, is_paint_mode, use_hide); break; case OB_CURVE: case OB_FONT: case OB_SURF: if (mesh_eval) { - DRW_mesh_batch_cache_create_requested(ob, mesh_eval, ts, is_paint_mode, use_hide); + DRW_mesh_batch_cache_create_requested(ob, mesh_eval, scene, is_paint_mode, use_hide); } DRW_curve_batch_cache_create_requested(ob); break; diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 129b180957a..5dadcdc1457 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -27,6 +27,7 @@ struct GPUBatch; struct GPUMaterial; struct ModifierData; struct Object; +struct ParticleSystem; struct PTCacheEdit; void DRW_shape_cache_free(void); @@ -58,7 +59,7 @@ struct GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, char **auto_layer_names, int **auto_layer_is_srgb, int *auto_layer_count); -struct GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob); +struct GPUBatch *DRW_cache_object_face_wireframe_get(struct Object *ob); /* Empties */ struct GPUBatch *DRW_cache_plain_axes_get(void); @@ -152,7 +153,7 @@ struct GPUBatch **DRW_cache_curve_surface_shaded_get(struct Object *ob, uint gpumat_array_len); struct GPUBatch *DRW_cache_curve_loose_edges_get(struct Object *ob); struct GPUBatch *DRW_cache_curve_edge_wire_get(struct Object *ob); -struct GPUBatch *DRW_cache_curve_face_wireframe_get(Object *ob); +struct GPUBatch *DRW_cache_curve_face_wireframe_get(struct Object *ob); struct GPUBatch *DRW_cache_curve_edge_detection_get(struct Object *ob, bool *r_is_manifold); /* edit-mode */ struct GPUBatch *DRW_cache_curve_edge_normal_get(struct Object *ob); @@ -161,13 +162,13 @@ struct GPUBatch *DRW_cache_curve_vert_overlay_get(struct Object *ob, bool handle /* Font */ struct GPUBatch *DRW_cache_text_surface_get(struct Object *ob); -struct GPUBatch *DRW_cache_text_edge_detection_get(Object *ob, bool *r_is_manifold); +struct GPUBatch *DRW_cache_text_edge_detection_get(struct Object *ob, bool *r_is_manifold); struct GPUBatch *DRW_cache_text_loose_edges_get(struct Object *ob); struct GPUBatch *DRW_cache_text_edge_wire_get(struct Object *ob); struct GPUBatch **DRW_cache_text_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len); -struct GPUBatch *DRW_cache_text_face_wireframe_get(Object *ob); +struct GPUBatch *DRW_cache_text_face_wireframe_get(struct Object *ob); /* Surface */ struct GPUBatch *DRW_cache_surf_surface_get(struct Object *ob); @@ -176,7 +177,7 @@ struct GPUBatch *DRW_cache_surf_loose_edges_get(struct Object *ob); struct GPUBatch **DRW_cache_surf_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len); -struct GPUBatch *DRW_cache_surf_face_wireframe_get(Object *ob); +struct GPUBatch *DRW_cache_surf_face_wireframe_get(struct Object *ob); struct GPUBatch *DRW_cache_surf_edge_detection_get(struct Object *ob, bool *r_is_manifold); /* Lattice */ @@ -206,7 +207,7 @@ struct GPUBatch *DRW_cache_mball_surface_get(struct Object *ob); struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len); -struct GPUBatch *DRW_cache_mball_face_wireframe_get(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); #endif /* __DRAW_CACHE_H__ */ diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h new file mode 100644 index 00000000000..9305dc6eef7 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -0,0 +1,250 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +#ifndef __DRAW_CACHE_EXTRACT_MESH_H__ +#define __DRAW_CACHE_EXTRACT_MESH_H__ + +/* Vertex Group Selection and display options */ +typedef struct DRW_MeshWeightState { + int defgroup_active; + int defgroup_len; + + short flags; + char alert_mode; + + /* Set of all selected bones for Multipaint. */ + bool *defgroup_sel; /* [defgroup_len] */ + int defgroup_sel_count; +} DRW_MeshWeightState; + +/* DRW_MeshWeightState.flags */ +enum { + DRW_MESH_WEIGHT_STATE_MULTIPAINT = (1 << 0), + DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE = (1 << 1), +}; + +typedef struct DRW_MeshCDMask { + uint32_t uv : 8; + uint32_t tan : 8; + uint32_t vcol : 8; + uint32_t orco : 1; + uint32_t tan_orco : 1; +} DRW_MeshCDMask; + +typedef enum eMRIterType { + MR_ITER_LOOPTRI = 1 << 0, + MR_ITER_LOOP = 1 << 1, + MR_ITER_LEDGE = 1 << 2, + MR_ITER_LVERT = 1 << 3, +} eMRIterType; + +typedef enum eMRDataType { + MR_DATA_POLY_NOR = 1 << 1, + MR_DATA_LOOP_NOR = 1 << 2, + MR_DATA_LOOPTRI = 1 << 3, + /** Force loop normals calculation. */ + MR_DATA_TAN_LOOP_NOR = 1 << 4, +} eMRDataType; + +typedef enum eMRExtractType { + MR_EXTRACT_BMESH, + MR_EXTRACT_MAPPED, + MR_EXTRACT_MESH, +} eMRExtractType; + +BLI_INLINE int mesh_render_mat_len_get(Mesh *me) +{ + return MAX2(1, me->totcol); +} + +typedef struct MeshBufferCache { + /* Every VBO below contains at least enough + * data for every loops in the mesh (except fdots). + * For some VBOs, it extends to (in this exact order) : + * loops + loose_edges*2 + loose_verts */ + struct { + GPUVertBuf *pos_nor; /* extend */ + GPUVertBuf *lnor; /* extend */ + GPUVertBuf *edge_fac; /* extend */ + GPUVertBuf *weights; /* extend */ + GPUVertBuf *uv; + GPUVertBuf *tan; + GPUVertBuf *vcol; + GPUVertBuf *orco; + /* Only for edit mode. */ + GPUVertBuf *edit_data; /* extend */ + GPUVertBuf *edituv_data; + GPUVertBuf *stretch_area; + GPUVertBuf *stretch_angle; + GPUVertBuf *mesh_analysis; + GPUVertBuf *fdots_pos; + GPUVertBuf *fdots_nor; + GPUVertBuf *fdots_uv; + // GPUVertBuf *fdots_edit_data; /* inside fdots_nor for now. */ + GPUVertBuf *fdots_edituv_data; + /* Selection */ + GPUVertBuf *vert_idx; /* extend */ + GPUVertBuf *edge_idx; /* extend */ + GPUVertBuf *poly_idx; + GPUVertBuf *fdot_idx; + } vbo; + /* Index Buffers: + * Only need to be updated when topology changes. */ + struct { + /* Indices to vloops. */ + GPUIndexBuf *tris; /* Ordered per material. */ + GPUIndexBuf *lines; /* Loose edges last. */ + GPUIndexBuf *points; + GPUIndexBuf *fdots; + /* 3D overlays. */ + GPUIndexBuf *lines_paint_mask; /* no loose edges. */ + GPUIndexBuf *lines_adjacency; + /* Uv overlays. (visibility can differ from 3D view) */ + GPUIndexBuf *edituv_tris; + GPUIndexBuf *edituv_lines; + GPUIndexBuf *edituv_points; + GPUIndexBuf *edituv_fdots; + } ibo; +} MeshBufferCache; + +typedef enum DRWBatchFlag { + MBC_SURFACE = (1 << 0), + MBC_SURFACE_WEIGHTS = (1 << 1), + MBC_EDIT_TRIANGLES = (1 << 2), + MBC_EDIT_VERTICES = (1 << 3), + MBC_EDIT_EDGES = (1 << 4), + MBC_EDIT_VNOR = (1 << 5), + MBC_EDIT_LNOR = (1 << 6), + MBC_EDIT_FACEDOTS = (1 << 7), + MBC_EDIT_MESH_ANALYSIS = (1 << 8), + MBC_EDITUV_FACES_STRECH_AREA = (1 << 9), + MBC_EDITUV_FACES_STRECH_ANGLE = (1 << 10), + MBC_EDITUV_FACES = (1 << 11), + MBC_EDITUV_EDGES = (1 << 12), + MBC_EDITUV_VERTS = (1 << 13), + MBC_EDITUV_FACEDOTS = (1 << 14), + MBC_EDIT_SELECTION_VERTS = (1 << 15), + MBC_EDIT_SELECTION_EDGES = (1 << 16), + MBC_EDIT_SELECTION_FACES = (1 << 17), + MBC_EDIT_SELECTION_FACEDOTS = (1 << 18), + MBC_ALL_VERTS = (1 << 19), + MBC_ALL_EDGES = (1 << 20), + MBC_LOOSE_EDGES = (1 << 21), + MBC_EDGE_DETECTION = (1 << 22), + MBC_WIRE_EDGES = (1 << 23), + MBC_WIRE_LOOPS = (1 << 24), + MBC_WIRE_LOOPS_UVS = (1 << 25), + MBC_SURF_PER_MAT = (1 << 26), +} DRWBatchFlag; + +#define MBC_EDITUV \ + (MBC_EDITUV_FACES_STRECH_AREA | MBC_EDITUV_FACES_STRECH_ANGLE | MBC_EDITUV_FACES | \ + MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS | MBC_WIRE_LOOPS_UVS) + +#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \ + for (MeshBufferCache *mbc = &batch_cache->final; \ + mbc == &batch_cache->final || mbc == &batch_cache->cage || mbc == &batch_cache->uv_cage; \ + mbc = (mbc == &batch_cache->final) ? \ + &batch_cache->cage : \ + ((mbc == &batch_cache->cage) ? &batch_cache->uv_cage : NULL)) + +typedef struct MeshBatchCache { + MeshBufferCache final, cage, uv_cage; + + struct { + /* Surfaces / Render */ + GPUBatch *surface; + GPUBatch *surface_weights; + /* Edit mode */ + GPUBatch *edit_triangles; + GPUBatch *edit_vertices; + GPUBatch *edit_edges; + GPUBatch *edit_vnor; + GPUBatch *edit_lnor; + GPUBatch *edit_fdots; + GPUBatch *edit_mesh_analysis; + /* Edit UVs */ + GPUBatch *edituv_faces_strech_area; + GPUBatch *edituv_faces_strech_angle; + GPUBatch *edituv_faces; + GPUBatch *edituv_edges; + GPUBatch *edituv_verts; + GPUBatch *edituv_fdots; + /* Edit selection */ + GPUBatch *edit_selection_verts; + GPUBatch *edit_selection_edges; + GPUBatch *edit_selection_faces; + GPUBatch *edit_selection_fdots; + /* Common display / Other */ + GPUBatch *all_verts; + GPUBatch *all_edges; + GPUBatch *loose_edges; + GPUBatch *edge_detection; + GPUBatch *wire_edges; /* Individual edges with face normals. */ + GPUBatch *wire_loops; /* Loops around faces. no edges between selected faces */ + GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */ + } batch; + + GPUBatch **surface_per_mat; + + /* arrays of bool uniform names (and value) that will be use to + * set srgb conversion for auto attributes.*/ + char *auto_layer_names; + int *auto_layer_is_srgb; + int auto_layer_len; + + DRWBatchFlag batch_requested; + DRWBatchFlag batch_ready; + + /* settings to determine if cache is invalid */ + int edge_len; + int tri_len; + int poly_len; + int vert_len; + int mat_len; + bool is_dirty; /* Instantly invalidates cache, skipping mesh check */ + bool is_editmode; + bool is_uvsyncsel; + + struct DRW_MeshWeightState weight_state; + + DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; + + int lastmatch; + + /* Valid only if edge_detection is up to date. */ + bool is_manifold; + + bool no_loose_wire; +} MeshBatchCache; + +void mesh_buffer_cache_create_requested(MeshBatchCache *cache, + MeshBufferCache mbc, + Mesh *me, + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const DRW_MeshCDMask *cd_layer_used, + const ToolSettings *ts, + const bool use_hide); + +#endif /* __DRAW_CACHE_EXTRACT_MESH_H__ */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c new file mode 100644 index 00000000000..5b56067d355 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -0,0 +1,4313 @@ +/* + * 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) 2017 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_buffer.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_math_bits.h" +#include "BLI_string.h" +#include "BLI_alloca.h" +#include "BLI_edgehash.h" +#include "BLI_task.h" +#include "BLI_jitter_2d.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_customdata.h" +#include "BKE_deform.h" +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_editmesh_tangent.h" +#include "BKE_editmesh_bvh.h" +#include "BKE_mesh.h" +#include "BKE_mesh_tangent.h" +#include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" +#include "BKE_object_deform.h" + +#include "atomic_ops.h" + +#include "bmesh.h" + +#include "GPU_batch.h" +#include "GPU_extensions.h" +#include "GPU_material.h" + +#include "DRW_render.h" + +#include "ED_mesh.h" +#include "ED_uvedit.h" + +#include "draw_cache_inline.h" +#include "draw_cache_impl.h" + +#include "draw_cache_extract.h" + +// #define DEBUG_TIME + +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif + +/* ---------------------------------------------------------------------- */ +/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). + * \{ */ + +typedef struct MeshRenderData { + eMRExtractType extract_type; + + int poly_len, edge_len, vert_len, loop_len; + int edge_loose_len; + int vert_loose_len; + int loop_loose_len; + int tri_len; + int mat_len; + + bool use_hide; + bool use_subsurf_fdots; + bool use_final_mesh; + + const ToolSettings *toolsettings; + /* HACK not supposed to be there but it's needed. */ + struct MeshBatchCache *cache; + /** Edit Mesh */ + BMEditMesh *edit_bmesh; + BMesh *bm; + EditMeshData *edit_data; + int *v_origindex, *e_origindex, *p_origindex; + int crease_ofs; + int bweight_ofs; + int freestyle_edge_ofs; + int freestyle_face_ofs; + /** Mesh */ + Mesh *me; + const MVert *mvert; + const MEdge *medge; + const MLoop *mloop; + const MPoly *mpoly; + BMVert *eve_act; + BMEdge *eed_act; + BMFace *efa_act; + BMFace *efa_act_uv; + /* Data created on-demand (usually not for bmesh-based data). */ + MLoopTri *mlooptri; + float (*loop_normals)[3]; + float (*poly_normals)[3]; + int *lverts, *ledges; +} MeshRenderData; + +static MeshRenderData *mesh_render_data_create(Mesh *me, + const bool do_final, + const bool do_uvedit, + const eMRIterType iter_type, + const eMRDataType data_flag, + const DRW_MeshCDMask *UNUSED(cd_used), + const ToolSettings *ts) +{ + MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); + mr->toolsettings = ts; + mr->mat_len = mesh_render_mat_len_get(me); + + const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; + const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; + + if (me->edit_mesh) { + BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); + mr->bm = me->edit_mesh->bm; + mr->edit_bmesh = me->edit_mesh; + mr->edit_data = me->runtime.edit_data; + mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; + bool use_mapped = !do_uvedit && mr->me && !mr->me->runtime.is_original; + + int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; + + BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); + BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); + + mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); + mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); + mr->eed_act = BM_mesh_active_edge_get(mr->bm); + mr->eve_act = BM_mesh_active_vert_get(mr->bm); + + mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); + mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); +#ifdef WITH_FREESTYLE + mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); + mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); +#endif + + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; + + /* Seems like the mesh_eval_final do not have the right origin indices. + * Force not mapped in this case. */ + if (do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { + // mr->edit_bmesh = NULL; + mr->extract_type = MR_EXTRACT_MESH; + } + } + else { + mr->me = me; + mr->edit_bmesh = NULL; + mr->extract_type = MR_EXTRACT_MESH; + } + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + mr->vert_len = mr->me->totvert; + mr->edge_len = mr->me->totedge; + mr->loop_len = mr->me->totloop; + mr->poly_len = mr->me->totpoly; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + + mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); + mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); + mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); + mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); + + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { + mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); + BKE_mesh_calc_normals_poly((MVert *)mr->mvert, + NULL, + mr->vert_len, + mr->mloop, + mr->mpoly, + mr->loop_len, + mr->poly_len, + mr->poly_normals, + true); + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); + BKE_mesh_normals_loop_split(mr->me->mvert, + mr->vert_len, + mr->me->medge, + mr->edge_len, + mr->me->mloop, + mr->loop_normals, + mr->loop_len, + mr->me->mpoly, + mr->poly_normals, + mr->poly_len, + is_auto_smooth, + split_angle, + NULL, + clnors, + NULL); + } + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); + BKE_mesh_recalc_looptri(mr->me->mloop, + mr->me->mpoly, + mr->me->mvert, + mr->me->totloop, + mr->me->totpoly, + mr->mlooptri); + } + if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { + mr->vert_loose_len = 0; + mr->edge_loose_len = 0; + + BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, "lvert map"); + + mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__); + const MEdge *medge = mr->medge; + for (int e = 0; e < mr->edge_len; e++, medge++) { + if (medge->flag & ME_LOOSEEDGE) { + mr->ledges[mr->edge_loose_len++] = e; + } + /* Tag verts as not loose. */ + BLI_BITMAP_ENABLE(lvert_map, medge->v1); + BLI_BITMAP_ENABLE(lvert_map, medge->v2); + } + if (mr->edge_loose_len < mr->edge_len) { + mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); + } + + mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); + for (int v = 0; v < mr->vert_len; v++) { + if (!BLI_BITMAP_TEST(lvert_map, v)) { + mr->lverts[mr->vert_loose_len++] = v; + } + } + if (mr->vert_loose_len < mr->vert_len) { + mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); + } + + MEM_freeN(lvert_map); + + mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; + } + } + else { + /* BMesh */ + BMesh *bm = mr->bm; + + mr->vert_len = bm->totvert; + mr->edge_len = bm->totedge; + mr->loop_len = bm->totloop; + mr->poly_len = bm->totface; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + + if (data_flag & MR_DATA_POLY_NOR) { + /* Use bmface->no instead. */ + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); + BM_loops_calc_normal_vcos(mr->bm, + NULL, + NULL, + NULL, + is_auto_smooth, + split_angle, + mr->loop_normals, + NULL, + NULL, + clnors_offset, + false); + } + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + /* Edit mode ensures this is valid, no need to calculate. */ + BLI_assert((bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); + } + if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { + int elem_id; + BMIter iter; + BMVert *eve; + BMEdge *ede; + mr->vert_loose_len = 0; + mr->edge_loose_len = 0; + + mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { + if (eve->e == NULL) { + mr->lverts[mr->vert_loose_len++] = elem_id; + } + } + if (mr->vert_loose_len < mr->vert_len) { + mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); + } + + mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__); + BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { + if (ede->l == NULL) { + mr->ledges[mr->edge_loose_len++] = elem_id; + } + } + if (mr->edge_loose_len < mr->edge_len) { + mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); + } + + mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; + } + } + return mr; +} + +static void mesh_render_data_free(MeshRenderData *mr) +{ + MEM_SAFE_FREE(mr->mlooptri); + MEM_SAFE_FREE(mr->poly_normals); + MEM_SAFE_FREE(mr->loop_normals); + + MEM_SAFE_FREE(mr->lverts); + MEM_SAFE_FREE(mr->ledges); + + MEM_freeN(mr); +} + +BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) +{ + return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_face_at_index(mr->bm, mr->p_origindex[idx]) : + NULL; +} + +BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) +{ + return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : + NULL; +} + +BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) +{ + return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : + NULL; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Iter + * \{ */ + +typedef void *(ExtractInitFn)(const MeshRenderData *mr, void *buffer); +typedef void(ExtractEditTriFn)(const MeshRenderData *mr, int t, BMLoop **e, void *data); +typedef void(ExtractEditLoopFn)(const MeshRenderData *mr, int l, BMLoop *el, void *data); +typedef void(ExtractEditLedgeFn)(const MeshRenderData *mr, int e, BMEdge *ed, void *data); +typedef void(ExtractEditLvertFn)(const MeshRenderData *mr, int v, BMVert *ev, void *data); +typedef void(ExtractTriFn)(const MeshRenderData *mr, int t, const MLoopTri *mlt, void *data); +typedef void(ExtractLoopFn)( + const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data); +typedef void(ExtractLedgeFn)(const MeshRenderData *mr, int e, const MEdge *medge, void *data); +typedef void(ExtractLvertFn)(const MeshRenderData *mr, int v, const MVert *mvert, void *data); +typedef void(ExtractFinishFn)(const MeshRenderData *mr, void *buffer, void *data); + +typedef struct MeshExtract { + /** Executed on main thread and return user data for iter functions. */ + ExtractInitFn *init; + /** Executed on one (or more if use_threading) worker thread(s). */ + ExtractEditTriFn *iter_looptri_bm; + ExtractTriFn *iter_looptri; + ExtractEditLoopFn *iter_loop_bm; + ExtractLoopFn *iter_loop; + ExtractEditLedgeFn *iter_ledge_bm; + ExtractLedgeFn *iter_ledge; + ExtractEditLvertFn *iter_lvert_bm; + ExtractLvertFn *iter_lvert; + /** Executed on one worker thread after all elements iterations. */ + ExtractFinishFn *finish; + /** Used to request common data. */ + const eMRDataType data_flag; + /** Used to know if the element callbacks are threadsafe and can be parallelized. */ + const bool use_threading; +} MeshExtract; + +BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext) +{ + eMRIterType type = 0; + SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri), MR_ITER_LOOPTRI); + SET_FLAG_FROM_TEST(type, (ext->iter_loop_bm || ext->iter_loop), MR_ITER_LOOP); + SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge), MR_ITER_LEDGE); + SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert), MR_ITER_LVERT); + return type; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Triangles Indices + * \{ */ + +typedef struct MeshExtract_Tri_Data { + GPUIndexBufBuilder elb; + int *tri_mat_start; + int *tri_mat_end; +} MeshExtract_Tri_Data; + +static void *extract_tris_init(const MeshRenderData *mr, void *UNUSED(ibo)) +{ + MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__); + + size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; + data->tri_mat_start = MEM_callocN(mat_tri_idx_size, __func__); + data->tri_mat_end = MEM_callocN(mat_tri_idx_size, __func__); + + int *mat_tri_len = data->tri_mat_start; + /* Count how many triangle for each material. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMFace *efa; + BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + mat_tri_len[efa->mat_nr] += efa->len - 2; + } + } + } + else { + const MPoly *mpoly = mr->mpoly; + for (int p = 0; p < mr->poly_len; p++, mpoly++) { + if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) { + mat_tri_len[mpoly->mat_nr] += mpoly->totloop - 2; + } + } + } + /* Accumulate tri len per mat to have correct offsets. */ + int ofs = mat_tri_len[0]; + mat_tri_len[0] = 0; + for (int i = 1; i < mr->mat_len; i++) { + int tmp = mat_tri_len[i]; + mat_tri_len[i] = ofs; + ofs += tmp; + } + + memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size); + + int visible_tri_tot = ofs; + GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len); + + return data; +} + +static void extract_tris_looptri_bmesh(const MeshRenderData *UNUSED(mr), + int UNUSED(t), + BMLoop **elt, + void *_data) +{ + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + MeshExtract_Tri_Data *data = _data; + int *mat_tri_ofs = data->tri_mat_end; + GPU_indexbuf_set_tri_verts(&data->elb, + mat_tri_ofs[elt[0]->f->mat_nr]++, + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); + } +} + +static void extract_tris_looptri_mesh(const MeshRenderData *mr, + int UNUSED(t), + const MLoopTri *mlt, + void *_data) +{ + const MPoly *mpoly = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) { + MeshExtract_Tri_Data *data = _data; + int *mat_tri_ofs = data->tri_mat_end; + GPU_indexbuf_set_tri_verts( + &data->elb, mat_tri_ofs[mpoly->mat_nr]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); + } +} + +static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data) +{ + MeshExtract_Tri_Data *data = _data; + GPU_indexbuf_build_in_place(&data->elb, ibo); + /* HACK Create ibo subranges and assign them to each GPUBatch. */ + if (mr->use_final_mesh && mr->cache->surface_per_mat && mr->cache->surface_per_mat[0]) { + BLI_assert(mr->cache->surface_per_mat[0]->elem == ibo); + for (int i = 0; i < mr->mat_len; ++i) { + /* Multiply by 3 because these are triangle indices. */ + int start = data->tri_mat_start[i] * 3; + int len = data->tri_mat_end[i] * 3 - data->tri_mat_start[i] * 3; + GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len); + /* WARNING: We modify the GPUBatch here! */ + GPU_batch_elembuf_set(mr->cache->surface_per_mat[i], sub_ibo, true); + } + } + MEM_freeN(data->tri_mat_start); + MEM_freeN(data->tri_mat_end); + MEM_freeN(data); +} + +const MeshExtract extract_tris = {extract_tris_init, + extract_tris_looptri_bmesh, + extract_tris_looptri_mesh, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + extract_tris_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edges Indices + * \{ */ + +static void *extract_lines_init(const MeshRenderData *mr, void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + /* Put loose edges at the end. */ + GPU_indexbuf_init( + elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len); + return elb; +} + +static void extract_lines_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *elb) +{ + if (!BM_elem_flag_test(loop->e, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_line_verts(elb, BM_elem_index_get(loop->e), l, BM_elem_index_get(loop->next)); + } + else { + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(loop->e)); + } +} + +static void extract_lines_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *mpoly, + void *elb) +{ + const MEdge *medge = &mr->medge[mloop->e]; + if (!((mr->use_hide && (medge->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && + (mr->e_origindex[mloop->e] == ORIGINDEX_NONE)))) { + int loopend = mpoly->totloop + mpoly->loopstart - 1; + int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1); + GPU_indexbuf_set_line_verts(elb, mloop->e, l, other_loop); + } + else { + GPU_indexbuf_set_line_restart(elb, mloop->e); + } +} + +static void extract_lines_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *elb) +{ + int ledge_idx = mr->edge_len + e; + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + int l = mr->loop_len + e * 2; + GPU_indexbuf_set_line_verts(elb, ledge_idx, l, l + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, ledge_idx); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); +} + +static void extract_lines_ledge_mesh(const MeshRenderData *mr, + int e, + const MEdge *medge, + void *elb) +{ + int ledge_idx = mr->edge_len + e; + int edge_idx = mr->ledges[e]; + if (!((mr->use_hide && (medge->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && + (mr->e_origindex[edge_idx] == ORIGINDEX_NONE)))) { + int l = mr->loop_len + e * 2; + GPU_indexbuf_set_line_verts(elb, ledge_idx, l, l + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, ledge_idx); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, edge_idx); +} + +static void extract_lines_finish(const MeshRenderData *mr, void *ibo, void *elb) +{ + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); + /* HACK Create ibo subranges and assign them to GPUBatch. */ + if (mr->use_final_mesh && mr->cache->batch.loose_edges) { + BLI_assert(mr->cache->batch.loose_edges->elem == ibo); + /* Multiply by 2 because these are edges indices. */ + int start = mr->edge_len * 2; + int len = mr->edge_loose_len * 2; + GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len); + /* WARNING: We modify the GPUBatch here! */ + GPU_batch_elembuf_set(mr->cache->batch.loose_edges, sub_ibo, true); + } +} + +const MeshExtract extract_lines = {extract_lines_init, + NULL, + NULL, + extract_lines_loop_bmesh, + extract_lines_loop_mesh, + extract_lines_ledge_bmesh, + extract_lines_ledge_mesh, + NULL, + NULL, + extract_lines_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Point Indices + * \{ */ + +static void *extract_points_init(const MeshRenderData *mr, void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len); + return elb; +} + +BLI_INLINE void vert_set_bmesh(GPUIndexBufBuilder *elb, BMVert *eve, int loop) +{ + int vert_idx = BM_elem_index_get(eve); + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, vert_idx, loop); + } + else { + GPU_indexbuf_set_point_restart(elb, vert_idx); + } +} + +BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, + const MeshRenderData *mr, + int vert_idx, + int loop) +{ + const MVert *mvert = &mr->mvert[vert_idx]; + if (!((mr->use_hide && (mvert->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && + (mr->v_origindex[vert_idx] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_point_vert(elb, vert_idx, loop); + } + else { + GPU_indexbuf_set_point_restart(elb, vert_idx); + } +} + +static void extract_points_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *elb) +{ + vert_set_bmesh(elb, loop->v, l); +} + +static void extract_points_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *UNUSED(mpoly), + void *elb) +{ + vert_set_mesh(elb, mr, mloop->v, l); +} + +static void extract_points_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *elb) +{ + vert_set_bmesh(elb, eed->v1, mr->loop_len + e * 2); + vert_set_bmesh(elb, eed->v2, mr->loop_len + e * 2 + 1); +} + +static void extract_points_ledge_mesh(const MeshRenderData *mr, + int e, + const MEdge *medge, + void *elb) +{ + vert_set_mesh(elb, mr, medge->v1, mr->loop_len + e * 2); + vert_set_mesh(elb, mr, medge->v2, mr->loop_len + e * 2 + 1); +} + +static void extract_points_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *elb) +{ + vert_set_bmesh(elb, eve, mr->loop_len + mr->edge_loose_len * 2 + v); +} + +static void extract_points_lvert_mesh(const MeshRenderData *mr, + int v, + const MVert *UNUSED(mvert), + void *elb) +{ + vert_set_mesh(elb, mr, mr->lverts[v], mr->loop_len + mr->edge_loose_len * 2 + v); +} + +static void extract_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb) +{ + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +const MeshExtract extract_points = {extract_points_init, + NULL, + NULL, + extract_points_loop_bmesh, + extract_points_loop_mesh, + extract_points_ledge_bmesh, + extract_points_ledge_mesh, + extract_points_lvert_bmesh, + extract_points_lvert_mesh, + extract_points_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Indices + * \{ */ + +static void *extract_fdots_init(const MeshRenderData *mr, void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); + return elb; +} + +static void extract_fdots_loop_bmesh(const MeshRenderData *UNUSED(mr), + int UNUSED(l), + BMLoop *loop, + void *elb) +{ + int face_idx = BM_elem_index_get(loop->f); + if (!BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, face_idx, face_idx); + } + else { + GPU_indexbuf_set_point_restart(elb, face_idx); + } +} + +static void extract_fdots_loop_mesh(const MeshRenderData *mr, + int UNUSED(l), + const MLoop *mloop, + int p, + const MPoly *mpoly, + void *elb) +{ + const MVert *mvert = &mr->mvert[mloop->v]; + if ((!mr->use_subsurf_fdots || (mvert->flag & ME_VERT_FACEDOT)) && + !(mr->use_hide && (mpoly->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, p, p); + } + else { + GPU_indexbuf_set_point_restart(elb, p); + } +} + +static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb) +{ + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +const MeshExtract extract_fdots = {extract_fdots_init, + NULL, + NULL, + extract_fdots_loop_bmesh, + extract_fdots_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_fdots_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Paint Mask Line Indices + * \{ */ + +typedef struct MeshExtract_LinePaintMask_Data { + GPUIndexBufBuilder elb; + /** One bit per edge set if face is selected. */ + BLI_bitmap select_map[0]; +} MeshExtract_LinePaintMask_Data; + +static void *extract_lines_paint_mask_init(const MeshRenderData *mr, void *UNUSED(buf)) +{ + size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); + MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len); + return data; +} + +static void extract_lines_paint_mask_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *mpoly, + void *_data) +{ + MeshExtract_LinePaintMask_Data *data = (MeshExtract_LinePaintMask_Data *)_data; + if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) { + int loopend = mpoly->totloop + mpoly->loopstart - 1; + int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1); + int edge_idx = mloop->e; + if (mpoly->flag & ME_FACE_SEL) { + if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, edge_idx)) { + /* Hide edge as it has more than 2 selected loop. */ + GPU_indexbuf_set_line_restart(&data->elb, edge_idx); + } + else { + /* First selected loop. Set edge visible, overwritting any unsel loop. */ + GPU_indexbuf_set_line_verts(&data->elb, edge_idx, l, other_loop); + } + } + else { + /* Set theses unselected loop only if this edge has no other selected loop. */ + if (!BLI_BITMAP_TEST(data->select_map, edge_idx)) { + GPU_indexbuf_set_line_verts(&data->elb, edge_idx, l, other_loop); + } + } + } +} +static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), + void *ibo, + void *_data) +{ + MeshExtract_LinePaintMask_Data *data = (MeshExtract_LinePaintMask_Data *)_data; + + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +const MeshExtract extract_lines_paint_mask = {extract_lines_paint_mask_init, + NULL, + NULL, + NULL, + extract_lines_paint_mask_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_lines_paint_mask_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Line Adjacency Indices + * \{ */ + +#define NO_EDGE INT_MAX + +typedef struct MeshExtract_LineAdjacency_Data { + GPUIndexBufBuilder elb; + EdgeHash *eh; + bool is_manifold; + /* Array to convert vert index to any loop index of this vert. */ + uint vert_to_loop[0]; +} MeshExtract_LineAdjacency_Data; + +static void *extract_lines_adjacency_init(const MeshRenderData *mr, void *UNUSED(buf)) +{ + /* Similar to poly_to_tri_count(). + * There is always loop + tri - 1 edges inside a polygon. + * Accumulate for all polys and you get : */ + uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len; + + size_t vert_to_loop_size = sizeof(uint) * mr->vert_len; + + MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len); + data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len); + data->is_manifold = true; + return data; +} + +BLI_INLINE void lines_adjacency_triangle( + uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data) +{ + GPUIndexBufBuilder *elb = &data->elb; + /* Iter around the triangle's edges. */ + for (int e = 0; e < 3; e++) { + uint tmp = v1; + v1 = v2, v2 = v3, v3 = tmp; + tmp = l1; + l1 = l2, l2 = l3, l3 = tmp; + + bool inv_indices = (v2 > v3); + void **pval; + bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval); + int v_data = POINTER_AS_INT(*pval); + if (!value_is_init || v_data == NO_EDGE) { + /* Save the winding order inside the sign bit. Because the + * edgehash sort the keys and we need to compare winding later. */ + int value = (int)l1 + 1; /* 0 cannot be signed so add one. */ + *pval = POINTER_FROM_INT((inv_indices) ? -value : value); + /* Store loop indices for remaining non-manifold edges. */ + data->vert_to_loop[v2] = l2; + data->vert_to_loop[v3] = l3; + } + else { + /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */ + *pval = POINTER_FROM_INT(NO_EDGE); + bool inv_opposite = (v_data < 0); + uint l_opposite = (uint)abs(v_data) - 1; + /* TODO Make this part threadsafe. */ + if (inv_opposite == inv_indices) { + /* Don't share edge if triangles have non matching winding. */ + GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1); + GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite); + data->is_manifold = false; + } + else { + GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite); + } + } + } +} + +static void extract_lines_adjacency_looptri_bmesh(const MeshRenderData *UNUSED(mr), + int UNUSED(t), + BMLoop **elt, + void *data) +{ + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), + BM_elem_index_get(elt[1]->v), + BM_elem_index_get(elt[2]->v), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2]), + data); + } +} + +static void extract_lines_adjacency_looptri_mesh(const MeshRenderData *mr, + int UNUSED(t), + const MLoopTri *mlt, + void *data) +{ + const MPoly *mpoly = &mr->mpoly[mlt->poly]; + if (!(mpoly->flag & ME_HIDE)) { + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); + } +} + +static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo, void *_data) +{ + MeshExtract_LineAdjacency_Data *data = (MeshExtract_LineAdjacency_Data *)_data; + /* Create edges for remaining non manifold edges. */ + EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); + for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + uint v2, v3, l1, l2, l3; + int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi)); + if (v_data != NO_EDGE) { + BLI_edgehashIterator_getKey(ehi, &v2, &v3); + l1 = (uint)abs(v_data) - 1; + if (v_data < 0) { /* inv_opposite */ + SWAP(uint, v2, v3); + } + l2 = data->vert_to_loop[v2]; + l3 = data->vert_to_loop[v3]; + GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1); + data->is_manifold = false; + } + } + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(data->eh, NULL); + + mr->cache->is_manifold = data->is_manifold; + + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +#undef NO_EDGE + +const MeshExtract extract_lines_adjacency = {extract_lines_adjacency_init, + extract_lines_adjacency_looptri_bmesh, + extract_lines_adjacency_looptri_mesh, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + extract_lines_adjacency_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Triangles Indices + * \{ */ + +typedef struct MeshExtract_EditUvElem_Data { + GPUIndexBufBuilder elb; + bool sync_selection; +} MeshExtract_EditUvElem_Data; + +static void *extract_edituv_tris_init(const MeshRenderData *mr, void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_tri_add( + MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3); + } +} + +static void extract_edituv_tris_looptri_bmesh(const MeshRenderData *UNUSED(mr), + int UNUSED(t), + BMLoop **elt, + void *data) +{ + edituv_tri_add(data, + BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); +} + +static void extract_edituv_tris_looptri_mesh(const MeshRenderData *mr, + int UNUSED(t), + const MLoopTri *mlt, + void *data) +{ + const MPoly *mpoly = &mr->mpoly[mlt->poly]; + edituv_tri_add(data, + (mpoly->flag & ME_HIDE) != 0, + (mpoly->flag & ME_FACE_SEL) != 0, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2]); +} + +static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +{ + MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_tris = {extract_edituv_tris_init, + extract_edituv_tris_looptri_bmesh, + extract_edituv_tris_looptri_mesh, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + extract_edituv_tris_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Line Indices around faces + * \{ */ + +static void *extract_edituv_lines_init(const MeshRenderData *mr, void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_edge_add( + MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_line_verts(&data->elb, v1, v2); + } +} + +static void extract_edituv_lines_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *data) +{ + edituv_edge_add(data, + BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(loop->f, BM_ELEM_SELECT), + l, + BM_elem_index_get(loop->next)); +} + +static void extract_edituv_lines_loop_mesh(const MeshRenderData *mr, + int loop_idx, + const MLoop *mloop, + int UNUSED(p), + const MPoly *mpoly, + void *data) +{ + int loopend = mpoly->totloop + mpoly->loopstart - 1; + int loop_next_idx = (loop_idx == loopend) ? mpoly->loopstart : (loop_idx + 1); + const bool real_edge = (mr->extract_type == MR_EXTRACT_MAPPED && + mr->e_origindex[mloop->e] != ORIGINDEX_NONE); + edituv_edge_add(data, + (mpoly->flag & ME_HIDE) != 0 || !real_edge, + (mpoly->flag & ME_FACE_SEL) != 0, + loop_idx, + loop_next_idx); +} + +static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +{ + MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_lines = {extract_edituv_lines_init, + NULL, + NULL, + extract_edituv_lines_loop_bmesh, + extract_edituv_lines_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_edituv_lines_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Points Indices + * \{ */ + +static void *extract_edituv_points_init(const MeshRenderData *mr, void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, + bool hidden, + bool selected, + int v1) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_point_vert(&data->elb, v1); + } +} + +static void extract_edituv_points_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *data) +{ + edituv_point_add(data, + BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(loop->f, BM_ELEM_SELECT), + l); +} + +static void extract_edituv_points_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *mpoly, + void *data) +{ + const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && + mr->v_origindex[mloop->v] != ORIGINDEX_NONE); + edituv_point_add( + data, ((mpoly->flag & ME_HIDE) != 0) || !real_vert, (mpoly->flag & ME_FACE_SEL) != 0, l); +} + +static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +{ + MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_points = {extract_edituv_points_init, + NULL, + NULL, + extract_edituv_points_loop_bmesh, + extract_edituv_points_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_edituv_points_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Facedots Indices + * \{ */ + +static void *extract_edituv_fdots_init(const MeshRenderData *mr, void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, + bool hidden, + bool selected, + int face_idx) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_set_point_vert(&data->elb, face_idx, face_idx); + } + else { + GPU_indexbuf_set_point_restart(&data->elb, face_idx); + } +} + +static void extract_edituv_fdots_loop_bmesh(const MeshRenderData *UNUSED(mr), + int UNUSED(l), + BMLoop *loop, + void *data) +{ + edituv_facedot_add(data, + BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(loop->f, BM_ELEM_SELECT), + BM_elem_index_get(loop->f)); +} + +static void extract_edituv_fdots_loop_mesh(const MeshRenderData *mr, + int UNUSED(l), + const MLoop *mloop, + int p, + const MPoly *mpoly, + void *data) +{ + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && + mr->p_origindex[p] != ORIGINDEX_NONE); + const bool subd_fdot = (!mr->use_subsurf_fdots || + (mr->mvert[mloop->v].flag & ME_VERT_FACEDOT) != 0); + edituv_facedot_add(data, + ((mpoly->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, + (mpoly->flag & ME_FACE_SEL) != 0, + p); +} + +static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *_data) +{ + MeshExtract_EditUvElem_Data *data = (MeshExtract_EditUvElem_Data *)_data; + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +const MeshExtract extract_edituv_fdots = {extract_edituv_fdots_init, + NULL, + NULL, + extract_edituv_fdots_loop_bmesh, + extract_edituv_fdots_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_edituv_fdots_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Position and Vertex Normal + * \{ */ + +typedef struct PosNorLoop { + float pos[3]; + GPUPackedNormal nor; +} PosNorLoop; + +typedef struct MeshExtract_PosNor_Data { + PosNorLoop *vbo_data; + GPUPackedNormal packed_nor[]; +} MeshExtract_PosNor_Data; + +static void *extract_pos_nor_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING Adjust PosNorLoop struct accordingly. */ + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "vnor"); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + /* Pack normals per vert, reduce amount of computation. */ + size_t packed_nor_len = sizeof(GPUPackedNormal) * mr->vert_len; + MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); + data->vbo_data = (PosNorLoop *)vbo->data; + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMVert *eve; + int v; + BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { + data->packed_nor[v] = GPU_normal_convert_i10_v3(eve->no); + } + } + else { + const MVert *mvert = mr->mvert; + for (int v = 0; v < mr->vert_len; v++, mvert++) { + data->packed_nor[v] = GPU_normal_convert_i10_s3(mvert->no); + } + } + return data; +} + +static void extract_pos_nor_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + PosNorLoop *vert = data->vbo_data + l; + copy_v3_v3(vert->pos, loop->v->co); + vert->nor = data->packed_nor[BM_elem_index_get(loop->v)]; +} + +static void extract_pos_nor_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *mpoly, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + PosNorLoop *vert = data->vbo_data + l; + const MVert *mvert = &mr->mvert[mloop->v]; + copy_v3_v3(vert->pos, mvert->co); + vert->nor = data->packed_nor[mloop->v]; + /* Flag for paint mode overlay. */ + if (mpoly->flag & ME_HIDE) + vert->nor.w = -1; + else if (mpoly->flag & ME_FACE_SEL) + vert->nor.w = 1; + else + vert->nor.w = 0; +} + +static void extract_pos_nor_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *_data) +{ + int l = mr->loop_len + e * 2; + MeshExtract_PosNor_Data *data = _data; + PosNorLoop *vert = data->vbo_data + l; + copy_v3_v3(vert[0].pos, eed->v1->co); + copy_v3_v3(vert[1].pos, eed->v2->co); + vert[0].nor = data->packed_nor[BM_elem_index_get(eed->v1)]; + vert[1].nor = data->packed_nor[BM_elem_index_get(eed->v2)]; +} + +static void extract_pos_nor_ledge_mesh(const MeshRenderData *mr, + int e, + const MEdge *medge, + void *_data) +{ + int l = mr->loop_len + e * 2; + MeshExtract_PosNor_Data *data = _data; + PosNorLoop *vert = data->vbo_data + l; + copy_v3_v3(vert[0].pos, mr->mvert[medge->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[medge->v2].co); + vert[0].nor = data->packed_nor[medge->v1]; + vert[1].nor = data->packed_nor[medge->v2]; +} + +static void extract_pos_nor_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *_data) +{ + int l = mr->loop_len + mr->edge_loose_len * 2 + v; + MeshExtract_PosNor_Data *data = _data; + PosNorLoop *vert = data->vbo_data + l; + copy_v3_v3(vert->pos, eve->co); + vert->nor = data->packed_nor[BM_elem_index_get(eve)]; +} + +static void extract_pos_nor_lvert_mesh(const MeshRenderData *mr, + int v, + const MVert *mvert, + void *_data) +{ + int l = mr->loop_len + mr->edge_loose_len * 2 + v; + int v_idx = mr->lverts[v]; + MeshExtract_PosNor_Data *data = _data; + PosNorLoop *vert = data->vbo_data + l; + copy_v3_v3(vert->pos, mvert->co); + vert->nor = data->packed_nor[v_idx]; +} + +static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(vbo), void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_pos_nor = {extract_pos_nor_init, + NULL, + NULL, + extract_pos_nor_loop_bmesh, + extract_pos_nor_loop_mesh, + extract_pos_nor_ledge_bmesh, + extract_pos_nor_ledge_mesh, + extract_pos_nor_lvert_bmesh, + extract_pos_nor_lvert_mesh, + extract_pos_nor_finish, + 0, + true}; +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loop Normal + * \{ */ + +static void *extract_lnor_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "lnor"); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return vbo->data; +} + +static void extract_lnor_loop_bmesh(const MeshRenderData *mr, int l, BMLoop *loop, void *data) +{ + if (mr->loop_normals) { + ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->loop_normals[l]); + } + else if (BM_elem_flag_test(loop->f, BM_ELEM_SMOOTH)) { + ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(loop->v->no); + } + else { + ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(loop->f->no); + } +} + +static void extract_lnor_loop_mesh( + const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data) +{ + if (mr->loop_normals) { + ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->loop_normals[l]); + } + else if (mpoly->flag & ME_SMOOTH) { + ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_s3(mr->mvert[mloop->v].no); + } + else { + ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->poly_normals[p]); + } +} + +const MeshExtract extract_lnor = {extract_lnor_init, + NULL, + NULL, + extract_lnor_loop_bmesh, + extract_lnor_loop_mesh, + NULL, + NULL, + NULL, + NULL, + NULL, + MR_DATA_LOOP_NOR, + true}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract UV layers + * \{ */ + +static void *extract_uv_init(const MeshRenderData *mr, void *buf) +{ + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + uint32_t uv_layers = mr->cache->cd_used.uv; + + for (int i = 0; i < MAX_MTFACE; i++) { + if (uv_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); + + GPU_vertformat_safe_attrib_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); + /* UV layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* Auto layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + /* Active render layer name. */ + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "u"); + } + /* Active display layer name. */ + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "au"); + /* Alias to pos for edit uvs. */ + GPU_vertformat_alias_add(&format, "pos"); + } + /* Stencil mask uv layer name. */ + if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "mu"); + } + } + } + + int v_len = mr->loop_len; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + /* VBO will not be used, only allocate minimum of memory. */ + v_len = 1; + } + + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, v_len); + + float(*uv_data)[2] = (float(*)[2])vbo->data; + for (int i = 0; i < MAX_MTFACE; i++) { + if (uv_layers & (1 << i)) { + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i); + BMIter f_iter, l_iter; + BMFace *efa; + BMLoop *loop; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs); + memcpy(uv_data, luv->uv, sizeof(*uv_data)); + uv_data++; + } + } + } + else { + MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i); + for (int l = 0; l < mr->loop_len; l++, uv_data++, layer_data++) { + memcpy(uv_data, layer_data->uv, sizeof(*uv_data)); + } + } + } + } + + return NULL; +} + +const MeshExtract extract_uv = { + extract_uv_init, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, false}; +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Tangent layers + * \{ */ + +static void *extract_tan_init(const MeshRenderData *mr, void *buf) +{ + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + uint32_t tan_layers = mr->cache->cd_used.tan; + float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO); + bool orco_allocated = false; + const bool use_orco_tan = mr->cache->cd_used.tan_orco != 0; + + int tan_len = 0; + char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; + + for (int i = 0; i < MAX_MTFACE; i++) { + if (tan_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); + GPU_vertformat_safe_attrib_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); + /* Tangent layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + /* Active render layer name. */ + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "t"); + } + /* Active display layer name. */ + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "at"); + } + + BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); + } + } + if (use_orco_tan && orco == NULL) { + /* If orco is not available compute it ourselves */ + orco_allocated = true; + orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMesh *bm = mr->bm; + for (int v = 0; v < mr->vert_len; v++) { + copy_v3_v3(orco[v], BM_vert_at_index(bm, v)->co); + } + } + else { + const MVert *mvert = mr->mvert; + for (int v = 0; v < mr->vert_len; v++, mvert++) { + copy_v3_v3(orco[v], mvert->co); + } + } + BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0); + } + + /* Start Fresh */ + CustomData_free_layers(cd_ldata, CD_TANGENT, mr->loop_len); + + if (tan_len != 0 || use_orco_tan) { + short tangent_mask = 0; + bool calc_active_tangent = false; + if (mr->extract_type == MR_EXTRACT_BMESH) { + BKE_editmesh_loop_tangent_calc(mr->edit_bmesh, + calc_active_tangent, + tangent_names, + tan_len, + mr->poly_normals, + mr->loop_normals, + orco, + cd_ldata, + mr->loop_len, + &tangent_mask); + } + else { + BKE_mesh_calc_loop_tangent_ex(mr->mvert, + mr->mpoly, + mr->poly_len, + mr->mloop, + mr->mlooptri, + mr->tri_len, + cd_ldata, + calc_active_tangent, + tangent_names, + tan_len, + mr->poly_normals, + mr->loop_normals, + orco, + cd_ldata, + mr->loop_len, + &tangent_mask); + } + } + + if (use_orco_tan) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_TANGENT, 0); + GPU_vertformat_safe_attrib_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); + BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_alias_add(&format, "t"); + GPU_vertformat_alias_add(&format, "at"); + } + + if (orco_allocated) { + MEM_SAFE_FREE(orco); + } + + int v_len = mr->loop_len; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + /* VBO will not be used, only allocate minimum of memory. */ + v_len = 1; + } + + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, v_len); + + float(*tan_data)[4] = (float(*)[4])vbo->data; + for (int i = 0; i < tan_len; i++) { + void *layer_data = CustomData_get_layer_named(cd_ldata, CD_TANGENT, tangent_names[i]); + memcpy(tan_data, layer_data, sizeof(*tan_data) * mr->loop_len); + tan_data += mr->loop_len; + } + if (use_orco_tan) { + void *layer_data = CustomData_get_layer_n(cd_ldata, CD_TANGENT, 0); + memcpy(tan_data, layer_data, sizeof(*tan_data) * mr->loop_len); + } + + CustomData_free_layers(cd_ldata, CD_TANGENT, mr->loop_len); + + return NULL; +} + +const MeshExtract extract_tan = {extract_tan_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract VCol + * \{ */ + +static void *extract_vcol_init(const MeshRenderData *mr, void *buf) +{ + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = &mr->me->ldata; + uint32_t vcol_layers = mr->cache->cd_used.vcol; + + for (int i = 0; i < 8; i++) { + if (vcol_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); + GPU_vertformat_safe_attrib_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); + + BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) { + GPU_vertformat_alias_add(&format, "c"); + } + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) { + GPU_vertformat_alias_add(&format, "ac"); + } + /* Gather number of auto layers. */ + /* We only do vcols that are not overridden by uvs */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + } + } + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + MLoopCol *vcol_data = (MLoopCol *)vbo->data; + for (int i = 0; i < 8; i++) { + if (vcol_layers & (1 << i)) { + void *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); + memcpy(vcol_data, layer_data, sizeof(*vcol_data) * mr->loop_len); + vcol_data += mr->loop_len; + } + } + return NULL; +} + +const MeshExtract extract_vcol = { + extract_vcol_init, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, false}; + +/** \} */ /** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Orco + * \{ */ + +typedef struct MeshExtract_Orco_Data { + float (*vbo_data)[4]; + float (*orco)[3]; +} MeshExtract_Orco_Data; + +static void *extract_orco_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex + * attribs. This is a substantial waste of Vram and should be done another way. + * Unfortunately, at the time of writing, I did not found any other "non disruptive" + * alternative. */ + GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + CustomData *cd_vdata = &mr->me->vdata; + + MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__); + data->vbo_data = (float(*)[4])vbo->data; + data->orco = CustomData_get_layer(cd_vdata, CD_ORCO); + /* Make sure orco layer was requested only if needed! */ + BLI_assert(data->orco); + return data; +} + +static void extract_orco_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *data) +{ + MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; + float *loop_orco = orco_data->vbo_data[l]; + copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]); + loop_orco[3] = 0.0; /* Tag as not a generic attrib */ +} + +static void extract_orco_loop_mesh(const MeshRenderData *UNUSED(mr), + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *UNUSED(mpoly), + void *data) +{ + MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; + float *loop_orco = orco_data->vbo_data[l]; + copy_v3_v3(loop_orco, orco_data->orco[mloop->v]); + loop_orco[3] = 0.0; /* Tag as not a generic attrib */ +} + +static void extract_orco_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_orco = {extract_orco_init, + NULL, + NULL, + extract_orco_loop_bmesh, + extract_orco_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_orco_finish, + 0, + true}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edge Factor + * Defines how much an edge is visible. + * \{ */ + +typedef struct MeshExtract_EdgeFac_Data { + uchar *vbo_data; + bool use_edge_render; + /* Number of loop per edge. */ + uchar edge_loop_count[0]; +} MeshExtract_EdgeFac_Data; + +static float loop_edge_factor_get(const float f_no[3], + const float v_co[3], + const float v_no[3], + const float v_next_co[3]) +{ + float enor[3], evec[3]; + sub_v3_v3v3(evec, v_next_co, v_co); + cross_v3_v3v3(enor, v_no, evec); + normalize_v3(enor); + float d = fabsf(dot_v3v3(enor, f_no)); + /* Rescale to the slider range. */ + d *= (1.0f / 0.065f); + CLAMP(d, 0.0f, 1.0f); + return d; +} + +static void *extract_edge_fac_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + MeshExtract_EdgeFac_Data *data; + + if (mr->extract_type == MR_EXTRACT_MESH) { + size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len; + data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__); + + /* HACK(fclem) Detecting the need for edge render. + * We could have a flag in the mesh instead or check the modifier stack. */ + const MEdge *medge = mr->medge; + for (int e = 0; e < mr->edge_len; e++, medge++) { + if ((medge->flag & ME_EDGERENDER) == 0) { + data->use_edge_render = true; + break; + } + } + } + else { + data = MEM_callocN(sizeof(*data), __func__); + /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */ + data->use_edge_render = true; + } + + data->vbo_data = vbo->data; + return data; +} + +static void extract_edge_fac_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *_data) +{ + MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; + if (BM_edge_is_manifold(loop->e)) { + float ratio = loop_edge_factor_get(loop->f->no, loop->v->co, loop->v->no, loop->next->v->co); + data->vbo_data[l] = ratio * 253 + 1; + } + else { + data->vbo_data[l] = 255; + } +} + +static void extract_edge_fac_loop_mesh( + const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data) +{ + MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; + if (data->use_edge_render) { + const MEdge *medge = &mr->medge[mloop->e]; + data->vbo_data[l] = (medge->flag & ME_EDGERENDER) ? 255 : 0; + } + else { + /* Count loop per edge to detect non-manifold. */ + if (data->edge_loop_count[mloop->e] < 3) { + data->edge_loop_count[mloop->e]++; + } + if (data->edge_loop_count[mloop->e] == 2) { + /* Manifold */ + int loopend = mpoly->totloop + mpoly->loopstart - 1; + int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1); + const MLoop *mloop_next = &mr->mloop[other_loop]; + const MVert *v1 = &mr->mvert[mloop->v]; + const MVert *v2 = &mr->mvert[mloop_next->v]; + float vnor_f[3]; + normal_short_to_float_v3(vnor_f, v1->no); + float ratio = loop_edge_factor_get(mr->poly_normals[p], v1->co, vnor_f, v2->co); + data->vbo_data[l] = ratio * 253 + 1; + } + else { + /* Non-manifold */ + data->vbo_data[l] = 255; + } + } +} + +static void extract_edge_fac_ledge_bmesh(const MeshRenderData *mr, + int e, + BMEdge *UNUSED(eed), + void *_data) +{ + MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; + data->vbo_data[mr->loop_len + e * 2 + 0] = 255; + data->vbo_data[mr->loop_len + e * 2 + 1] = 255; +} + +static void extract_edge_fac_ledge_mesh(const MeshRenderData *mr, + int e, + const MEdge *UNUSED(edge), + void *_data) +{ + MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; + data->vbo_data[mr->loop_len + e * 2 + 0] = 255; + data->vbo_data[mr->loop_len + e * 2 + 1] = 255; +} + +static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data) +{ + MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; + + if (GPU_crappy_amd_driver()) { + GPUVertBuf *vbo = (GPUVertBuf *)buf; + /* Some AMD drivers strangely crash with VBOs with a one byte format. + * To workaround we reinit the vbo with another format and convert + * all bytes to floats. */ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + /* We keep the data reference in data->vbo_data. */ + vbo->data = NULL; + GPU_vertbuf_clear(vbo); + + int buf_len = mr->loop_len + mr->loop_loose_len; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, buf_len); + + float *fdata = (float *)vbo->data; + for (int l = 0; l < buf_len; l++, fdata++) { + *fdata = data->vbo_data[l] / 255.0f; + } + /* Free old byte data. */ + MEM_freeN(data->vbo_data); + } + MEM_freeN(data); +} + +const MeshExtract extract_edge_fac = {extract_edge_fac_init, + NULL, + NULL, + extract_edge_fac_loop_bmesh, + extract_edge_fac_loop_mesh, + extract_edge_fac_ledge_bmesh, + extract_edge_fac_ledge_mesh, + NULL, + NULL, + extract_edge_fac_finish, + MR_DATA_POLY_NOR, + false}; + +/** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Extract Vertex Weight + * \{ */ + +typedef struct MeshExtract_Weight_Data { + float *vbo_data; + const DRW_MeshWeightState *wstate; + const MDeformVert *dvert; /* For Mesh. */ + int cd_ofs; /* For BMesh. */ +} MeshExtract_Weight_Data; + +static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate) +{ + /* Error state. */ + if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) { + return -2.0f; + } + else if (dvert == NULL) { + return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f; + } + + float input = 0.0f; + if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) { + /* Multi-Paint feature */ + input = BKE_defvert_multipaint_collective_weight( + dvert, + wstate->defgroup_len, + wstate->defgroup_sel, + wstate->defgroup_sel_count, + (wstate->flags & DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE) != 0); + /* make it black if the selected groups have no weight on a vertex */ + if (input == 0.0f) { + return -1.0f; + } + } + else { + /* default, non tricky behavior */ + input = defvert_find_weight(dvert, wstate->defgroup_active); + + if (input == 0.0f) { + switch (wstate->alert_mode) { + case OB_DRAW_GROUPUSER_ACTIVE: + return -1.0f; + break; + case OB_DRAW_GROUPUSER_ALL: + if (defvert_is_weight_zero(dvert, wstate->defgroup_len)) { + return -1.0f; + } + break; + } + } + } + CLAMP(input, 0.0f, 1.0f); + return input; +} + +static void *extract_weights_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (float *)vbo->data; + data->wstate = &mr->cache->weight_state; + + if (data->wstate->defgroup_active == -1) { + /* Nothing to show. */ + data->dvert = NULL; + data->cd_ofs = -1; + } + else if (mr->extract_type == MR_EXTRACT_BMESH) { + data->dvert = NULL; + data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT); + } + else { + data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT); + data->cd_ofs = -1; + } + return data; +} + +static void extract_weights_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *_data) +{ + MeshExtract_Weight_Data *data = (MeshExtract_Weight_Data *)_data; + const MDeformVert *dvert = (data->cd_ofs != -1) ? BM_ELEM_CD_GET_VOID_P(loop->v, data->cd_ofs) : + NULL; + data->vbo_data[l] = evaluate_vertex_weight(dvert, data->wstate); +} + +static void extract_weights_loop_mesh(const MeshRenderData *UNUSED(mr), + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *UNUSED(mpoly), + void *_data) +{ + MeshExtract_Weight_Data *data = (MeshExtract_Weight_Data *)_data; + const MDeformVert *dvert = data->dvert ? &data->dvert[mloop->v] : NULL; + data->vbo_data[l] = evaluate_vertex_weight(dvert, data->wstate); +} + +static void extract_weights_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_weights = {extract_weights_init, + NULL, + NULL, + extract_weights_loop_bmesh, + extract_weights_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_weights_finish, + 0, + true}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit Mode Data / Flags + * \{ */ + +typedef struct EditLoopData { + uchar v_flag; + uchar e_flag; + uchar crease; + uchar bweight; +} EditLoopData; + +static void mesh_render_data_face_flag(const MeshRenderData *mr, + BMFace *efa, + const int cd_ofs, + EditLoopData *eattr) +{ + if (efa == mr->efa_act) { + eattr->v_flag |= VFLAG_FACE_ACTIVE; + } + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + eattr->v_flag |= VFLAG_FACE_SELECTED; + } + + if (efa == mr->efa_act_uv) { + eattr->v_flag |= VFLAG_FACE_UV_ACTIVE; + } + if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) { + eattr->v_flag |= VFLAG_FACE_UV_SELECT; + } + +#ifdef WITH_FREESTYLE + if (mr->freestyle_face_ofs != -1) { + const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs); + if (ffa->flag & FREESTYLE_FACE_MARK) { + eattr->v_flag |= VFLAG_FACE_FREESTYLE; + } + } +#endif +} + +static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr) +{ + const ToolSettings *ts = mr->toolsettings; + const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0; + const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE); + + if (eed == mr->eed_act) { + eattr->e_flag |= VFLAG_EDGE_ACTIVE; + } + if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_EDGE_SELECTED; + } + if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_EDGE_SELECTED; + eattr->e_flag |= VFLAG_VERT_SELECTED; + } + if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) { + eattr->e_flag |= VFLAG_EDGE_SEAM; + } + if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) { + eattr->e_flag |= VFLAG_EDGE_SHARP; + } + + /* Use active edge color for active face edges because + * specular highlights make it hard to see T55456#510873. + * + * This isn't ideal since it can't be used when mixing edge/face modes + * but it's still better then not being able to see the active face. */ + if (is_face_only_select_mode) { + if (mr->efa_act != NULL) { + if (BM_edge_in_face(eed, mr->efa_act)) { + eattr->e_flag |= VFLAG_EDGE_ACTIVE; + } + } + } + + /* Use a byte for value range */ + if (mr->crease_ofs != -1) { + float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs); + if (crease > 0) { + eattr->crease = (uchar)(crease * 255.0f); + } + } + /* Use a byte for value range */ + if (mr->bweight_ofs != -1) { + float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs); + if (bweight > 0) { + eattr->bweight = (uchar)(bweight * 255.0f); + } + } +#ifdef WITH_FREESTYLE + if (mr->freestyle_edge_ofs != -1) { + const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs); + if (fed->flag & FREESTYLE_EDGE_MARK) { + eattr->e_flag |= VFLAG_EDGE_FREESTYLE; + } + } +#endif +} + +static void mesh_render_data_loop_flag(const MeshRenderData *mr, + BMLoop *loop, + const int cd_ofs, + EditLoopData *eattr) +{ + if (cd_ofs == -1) { + return; + } + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs); + if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) { + eattr->v_flag |= VFLAG_VERT_UV_PINNED; + } + if (uvedit_uv_select_test_ex(mr->toolsettings, loop, cd_ofs)) { + eattr->v_flag |= VFLAG_VERT_UV_SELECT; + } +} + +static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr, + BMLoop *loop, + const int cd_ofs, + EditLoopData *eattr) +{ + if (cd_ofs == -1) { + return; + } + if (uvedit_edge_select_test_ex(mr->toolsettings, loop, cd_ofs)) { + eattr->v_flag |= VFLAG_EDGE_UV_SELECT; + eattr->v_flag |= VFLAG_VERT_UV_SELECT; + } +} + +static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr) +{ + if (eve == mr->eve_act) { + eattr->e_flag |= VFLAG_VERT_ACTIVE; + } + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_VERT_SELECTED; + } +} + +static void *extract_edit_data_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING Adjust EditLoopData struct accordingly. */ + GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); + GPU_vertformat_alias_add(&format, "flag"); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + return vbo->data; +} + +static void extract_edit_data_loop_bmesh(const MeshRenderData *mr, + int l, + BMLoop *loop, + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + l; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_face_flag(mr, loop->f, -1, data); + mesh_render_data_edge_flag(mr, loop->e, data); + mesh_render_data_vert_flag(mr, loop->v, data); +} + +static void extract_edit_data_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *mloop, + int p, + const MPoly *UNUSED(mpoly), + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + l; + memset(data, 0x0, sizeof(*data)); + BMFace *efa = bm_original_face_get(mr, p); + BMEdge *eed = bm_original_edge_get(mr, mloop->e); + BMVert *eve = bm_original_vert_get(mr, mloop->v); + if (efa) { + mesh_render_data_face_flag(mr, efa, -1, data); + } + if (eed) { + mesh_render_data_edge_flag(mr, eed, data); + } + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); + } +} + +static void extract_edit_data_ledge_bmesh(const MeshRenderData *mr, + int e, + BMEdge *eed, + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + e * 2; + memset(data, 0x0, sizeof(*data) * 2); + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + mesh_render_data_vert_flag(mr, eed->v1, &data[0]); + mesh_render_data_vert_flag(mr, eed->v2, &data[1]); +} + +static void extract_edit_data_ledge_mesh(const MeshRenderData *mr, + int e, + const MEdge *edge, + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + e * 2; + memset(data, 0x0, sizeof(*data) * 2); + int e_idx = mr->ledges[e]; + BMEdge *eed = bm_original_edge_get(mr, e_idx); + BMVert *eve1 = bm_original_vert_get(mr, edge->v1); + BMVert *eve2 = bm_original_vert_get(mr, edge->v2); + if (eed) { + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + } + if (eve1) { + mesh_render_data_vert_flag(mr, eve1, &data[0]); + } + if (eve2) { + mesh_render_data_vert_flag(mr, eve2, &data[1]); + } +} + +static void extract_edit_data_lvert_bmesh(const MeshRenderData *mr, + int v, + BMVert *eve, + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + mr->edge_loose_len * 2 + v; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_vert_flag(mr, eve, data); +} + +static void extract_edit_data_lvert_mesh(const MeshRenderData *mr, + int v, + const MVert *UNUSED(mvert), + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + mr->edge_loose_len * 2 + v; + memset(data, 0x0, sizeof(*data)); + int v_idx = mr->lverts[v]; + BMVert *eve = bm_original_vert_get(mr, v_idx); + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); + } +} + +const MeshExtract extract_edit_data = {extract_edit_data_init, + NULL, + NULL, + extract_edit_data_loop_bmesh, + extract_edit_data_loop_mesh, + extract_edit_data_ledge_bmesh, + extract_edit_data_ledge_mesh, + extract_edit_data_lvert_bmesh, + extract_edit_data_lvert_mesh, + NULL, + 0, + true}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Data / Flags + * \{ */ + +typedef struct MeshExtract_EditUVData_Data { + EditLoopData *vbo_data; + int cd_ofs; +} MeshExtract_EditUVData_Data; + +static void *extract_edituv_data_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING Adjust EditLoopData struct accordingly. */ + GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); + GPU_vertformat_alias_add(&format, "flag"); + } + + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + CustomData *cd_ldata = &mr->me->ldata; + + MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (EditLoopData *)vbo->data; + data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); + return data; +} + +static void extract_edituv_data_loop_bmesh(const MeshRenderData *mr, + int l, + BMLoop *loop, + void *_data) +{ + MeshExtract_EditUVData_Data *data = (MeshExtract_EditUVData_Data *)_data; + EditLoopData *eldata = data->vbo_data + l; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_loop_flag(mr, loop, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata); +} + +static void extract_edituv_data_loop_mesh( + const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data) +{ + MeshExtract_EditUVData_Data *data = (MeshExtract_EditUVData_Data *)_data; + EditLoopData *eldata = data->vbo_data + l; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, p); + if (efa) { + BMEdge *eed = bm_original_edge_get(mr, mloop->e); + BMVert *eve = bm_original_vert_get(mr, mloop->v); + if (eed && eve) { + /* Loop on an edge endpoint. */ + BMLoop *loop = BM_face_edge_share_loop(efa, eed); + mesh_render_data_loop_flag(mr, loop, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata); + } + else { + if (eed == NULL) { + /* Find if the loop's vert is not part of an edit edge. + * For this, we check if the previous loop was on an edge. */ + int loopend = mpoly->loopstart + mpoly->totloop - 1; + int l_prev = (l == mpoly->loopstart) ? loopend : (l - 1); + const MLoop *mloop_prev = &mr->mloop[l_prev]; + eed = bm_original_edge_get(mr, mloop_prev->e); + } + if (eed) { + /* Mapped points on an edge between two edit verts. */ + BMLoop *loop = BM_face_edge_share_loop(efa, eed); + mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata); + } + } + } +} + +static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_edituv_data = {extract_edituv_data_init, + NULL, + NULL, + extract_edituv_data_loop_bmesh, + extract_edituv_data_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_edituv_data_finish, + 0, + true}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV area stretch + * \{ */ + +static void *extract_stretch_area_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "stretch", GPU_COMP_U16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return NULL; +} + +BLI_INLINE float area_ratio_get(float area, float uvarea) +{ + if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) { + /* Tag inversion by using the sign. */ + return (area > uvarea) ? (uvarea / area) : -(area / uvarea); + } + return 0.0f; +} + +BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio) +{ + ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio; + return (ratio > 1.0f) ? (1.0f / ratio) : ratio; +} + +static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +{ + float totarea = 0, totuvarea = 0; + float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + CustomData *cd_ldata = &mr->bm->ldata; + int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); + + BMFace *efa; + BMIter f_iter; + int f; + BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { + float area = BM_face_calc_area(efa); + float uvarea = BM_face_calc_area_uv(efa, uv_ofs); + totarea += area; + totuvarea += uvarea; + area_ratio[f] = area_ratio_get(area, uvarea); + } + } + else if (mr->extract_type == MR_EXTRACT_MAPPED) { + const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + const MPoly *mpoly = mr->mpoly; + for (int p = 0; p < mr->poly_len; p++, mpoly++) { + float area = BKE_mesh_calc_poly_area(mpoly, &mr->mloop[mpoly->loopstart], mr->mvert); + float uvarea = BKE_mesh_calc_poly_uv_area(mpoly, uv_data); + totarea += area; + totuvarea += uvarea; + area_ratio[p] = area_ratio_get(area, uvarea); + } + } + else { + /* Should not happen. */ + BLI_assert(0); + } + + float tot_ratio, inv_tot_ratio; + if (totarea < FLT_EPSILON || totuvarea < FLT_EPSILON) { + tot_ratio = 0.0f; + inv_tot_ratio = 0.0f; + } + else { + tot_ratio = totarea / totuvarea; + inv_tot_ratio = totuvarea / totarea; + } + + /* Convert in place to avoid an extra allocation */ + uint16_t *poly_stretch = (uint16_t *)area_ratio; + for (int p = 0; p < mr->poly_len; p++) { + float stretch = area_ratio_to_stretch(area_ratio[p], tot_ratio, inv_tot_ratio); + poly_stretch[p] = (1.0f - stretch) * 65534.0f; + } + + /* Copy face data for each loop. */ + GPUVertBuf *vbo = buf; + uint16_t *loop_stretch = (uint16_t *)vbo->data; + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMFace *efa; + BMIter f_iter; + int f, l = 0; + BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { + for (int i = 0; i < efa->len; i++, l++) { + loop_stretch[l] = poly_stretch[f]; + } + } + } + else if (mr->extract_type == MR_EXTRACT_MAPPED) { + const MPoly *mpoly = mr->mpoly; + for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) { + for (int i = 0; i < mpoly->totloop; i++, l++) { + loop_stretch[l] = poly_stretch[p]; + } + } + } + else { + /* Should not happen. */ + BLI_assert(0); + } + + MEM_freeN(area_ratio); +} + +const MeshExtract extract_stretch_area = {extract_stretch_area_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + mesh_stretch_area_finish, + 0, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV angle stretch + * \{ */ + +typedef struct UVStretchAngle { + int16_t angle; + int16_t uv_angles[2]; +} UVStretchAngle; + +typedef struct MeshExtract_StretchAngle_Data { + UVStretchAngle *vbo_data; + MLoopUV *luv; + float auv[2][2], last_auv[2]; + float av[2][3], last_av[3]; + int cd_ofs; +} MeshExtract_StretchAngle_Data; + +static void compute_normalize_edge_vectors(float auv[2][2], + float av[2][3], + const float uv[2], + const float uv_prev[2], + const float co[2], + const float co_prev[2]) +{ + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* 2d edge */ + sub_v2_v2v2(auv[1], uv_prev, uv); + normalize_v2(auv[1]); + /* 3d edge */ + sub_v3_v3v3(av[1], co_prev, co); + normalize_v3(av[1]); +} + +static short v2_to_short_angle(float v[2]) +{ + return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX; +} + +static void edituv_get_stretch_angle(float auv[2][2], float av[2][3], UVStretchAngle *r_stretch) +{ + /* Send uvs to the shader and let it compute the aspect corrected angle. */ + r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]); + r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]); + /* Compute 3D angle here. */ + r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX; + +#if 0 /* here for reference, this is done in shader now. */ + float uvang = angle_normalized_v2v2(auv0, auv1); + float ang = angle_normalized_v3v3(av0, av1); + float stretch = fabsf(uvang - ang) / (float)M_PI; + return 1.0f - pow2f(1.0f - stretch); +#endif +} + +static void *extract_stretch_angle_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING Adjust UVStretchAngle struct accordingly. */ + GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (UVStretchAngle *)vbo->data; + + /* Special iter nneded to save about half of the computing cost. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); + } + else if (mr->extract_type == MR_EXTRACT_MAPPED) { + data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + } + else { + BLI_assert(0); + } + return data; +} + +static void extract_stretch_angle_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *_data) +{ + MeshExtract_StretchAngle_Data *data = (MeshExtract_StretchAngle_Data *)_data; + float(*auv)[2] = data->auv, *last_auv = data->last_auv; + float(*av)[3] = data->av, *last_av = data->last_av; + const MLoopUV *luv, *luv_next; + BMLoop *l_next = loop->next; + BMFace *efa = loop->f; + if (loop == efa->l_first) { + /* First loop in face. */ + BMLoop *l_tmp = loop->prev; + BMLoop *l_next_tmp = loop; + luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); + luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); + compute_normalize_edge_vectors( + auv, av, luv->uv, luv_next->uv, l_tmp->v->co, l_next_tmp->v->co); + /* Save last edge. */ + copy_v2_v2(last_auv, auv[1]); + copy_v3_v3(last_av, av[1]); + } + if (l_next == efa->l_first) { + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* Copy already calculated last edge. */ + copy_v2_v2(auv[1], last_auv); + copy_v3_v3(av[1], last_av); + } + else { + luv = BM_ELEM_CD_GET_VOID_P(loop, data->cd_ofs); + luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); + compute_normalize_edge_vectors(auv, av, luv->uv, luv_next->uv, loop->v->co, l_next->v->co); + } + edituv_get_stretch_angle(auv, av, data->vbo_data + l); +} + +static void extract_stretch_angle_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *UNUSED(mloop), + int UNUSED(p), + const MPoly *mpoly, + void *_data) +{ + MeshExtract_StretchAngle_Data *data = (MeshExtract_StretchAngle_Data *)_data; + float(*auv)[2] = data->auv, *last_auv = data->last_auv; + float(*av)[3] = data->av, *last_av = data->last_av; + int l_next = l + 1, loopend = mpoly->loopstart + mpoly->totloop; + const MVert *v, *v_next; + if (l == mpoly->loopstart) { + /* First loop in face. */ + int l_tmp = loopend - 1; + int l_next_tmp = mpoly->loopstart; + v = &mr->mvert[mr->mloop[l_tmp].v]; + v_next = &mr->mvert[mr->mloop[l_next_tmp].v]; + compute_normalize_edge_vectors( + auv, av, data->luv[l_tmp].uv, data->luv[l_next_tmp].uv, v->co, v_next->co); + /* Save last edge. */ + copy_v2_v2(last_auv, auv[1]); + copy_v3_v3(last_av, av[1]); + } + if (l_next == loopend) { + l_next = mpoly->loopstart; + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* Copy already calculated last edge. */ + copy_v2_v2(auv[1], last_auv); + copy_v3_v3(av[1], last_av); + } + else { + v = &mr->mvert[mr->mloop[l].v]; + v_next = &mr->mvert[mr->mloop[l_next].v]; + compute_normalize_edge_vectors( + auv, av, data->luv[l].uv, data->luv[l_next].uv, v->co, v_next->co); + } + edituv_get_stretch_angle(auv, av, data->vbo_data + l); +} + +static void extract_stretch_angle_finish(const MeshRenderData *UNUSED(mr), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_stretch_angle = {extract_stretch_angle_init, + NULL, + NULL, + extract_stretch_angle_loop_bmesh, + extract_stretch_angle_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_stretch_angle_finish, + 0, + true}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV angle stretch + * \{ */ + +static void *extract_mesh_analysis_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return NULL; +} + +static void axis_from_enum_v3(float v[3], const char axis) +{ + zero_v3(v); + if (axis < 3) { + v[axis] = 1.0f; + } + else { + v[axis - 3] = -1.0f; + } +} + +BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange) +{ + if (fac < min) { + fac = 1.0f; + } + else if (fac > max) { + fac = -1.0f; + } + else { + fac = (fac - min) * minmax_irange; + fac = 1.0f - fac; + CLAMP(fac, 0.0f, 1.0f); + } + return fac; +} + +static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang) +{ + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->overhang_min / (float)M_PI; + const float max = statvis->overhang_max / (float)M_PI; + const char axis = statvis->overhang_axis; + BMEditMesh *em = mr->edit_bmesh; + BMIter iter; + BMesh *bm = em->bm; + BMFace *f; + float dir[3]; + const float minmax_irange = 1.0f / (max - min); + + BLI_assert(min <= max); + + axis_from_enum_v3(dir, axis); + + if (em && LIKELY(em->ob)) { + /* now convert into global space */ + mul_transposed_mat3_m4_v3(em->ob->obmat, dir); + normalize_v3(dir); + } + + if (mr->extract_type == MR_EXTRACT_BMESH) { + int l = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + float fac = angle_normalized_v3v3(f->no, dir) / (float)M_PI; + fac = overhang_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l++) { + r_overhang[l] = fac; + } + } + } + else { + const MPoly *mpoly = mr->mpoly; + for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) { + float fac = angle_normalized_v3v3(mr->poly_normals[p], dir) / (float)M_PI; + fac = overhang_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mpoly->totloop; i++, l++) { + r_overhang[l] = fac; + } + } + } +} + +/* so we can use jitter values for face interpolation */ +static void uv_from_jitter_v2(float uv[2]) +{ + uv[0] += 0.5f; + uv[1] += 0.5f; + if (uv[0] + uv[1] > 1.0f) { + uv[0] = 1.0f - uv[0]; + uv[1] = 1.0f - uv[1]; + } + + CLAMP(uv[0], 0.0f, 1.0f); + CLAMP(uv[1], 0.0f, 1.0f); +} + +BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange) +{ + /* important not '<=' */ + if (fac < max) { + fac = (fac - min) * minmax_irange; + fac = 1.0f - fac; + CLAMP(fac, 0.0f, 1.0f); + } + else { + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness) +{ + const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */ + /* cheating to avoid another allocation */ + float *face_dists = r_thickness + (mr->loop_len - mr->poly_len); + BMEditMesh *em = mr->edit_bmesh; + const float scale = 1.0f / mat4_to_scale(em->ob->obmat); + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->thickness_min * scale; + const float max = statvis->thickness_max * scale; + const float minmax_irange = 1.0f / (max - min); + const int samples = statvis->thickness_samples; + float jit_ofs[32][2]; + BLI_assert(samples <= 32); + BLI_assert(min <= max); + + copy_vn_fl(face_dists, mr->poly_len, max); + + BLI_jitter_init(jit_ofs, samples); + for (int j = 0; j < samples; j++) { + uv_from_jitter_v2(jit_ofs[j]); + } + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMesh *bm = em->bm; + BM_mesh_elem_index_ensure(bm, BM_FACE); + + struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); + struct BMLoop *(*looptris)[3] = em->looptris; + for (int i = 0; i < mr->tri_len; i++) { + BMLoop **ltri = looptris[i]; + const int index = BM_elem_index_get(ltri[0]->f); + const float *cos[3] = {ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co}; + float ray_co[3]; + float ray_no[3]; + + normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); + + for (int j = 0; j < samples; j++) { + float dist = face_dists[index]; + interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); + madd_v3_v3fl(ray_co, ray_no, eps_offset); + + BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL); + if (f_hit && dist < face_dists[index]) { + float angle_fac = fabsf(dot_v3v3(ltri[0]->f->no, f_hit->no)); + angle_fac = 1.0f - angle_fac; + angle_fac = angle_fac * angle_fac * angle_fac; + angle_fac = 1.0f - angle_fac; + dist /= angle_fac; + if (dist < face_dists[index]) { + face_dists[index] = dist; + } + } + } + } + BKE_bmbvh_free(bmtree); + + BMIter iter; + BMFace *f; + int l = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + float fac = face_dists[BM_elem_index_get(f)]; + fac = thickness_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l++) { + r_thickness[l] = fac; + } + } + } + else { + BVHTreeFromMesh treeData = {NULL}; + + BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); + const MLoopTri *mlooptri = mr->mlooptri; + for (int i = 0; i < mr->tri_len; i++, mlooptri++) { + const int index = mlooptri->poly; + const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co, + mr->mvert[mr->mloop[mlooptri->tri[1]].v].co, + mr->mvert[mr->mloop[mlooptri->tri[2]].v].co}; + float ray_co[3]; + float ray_no[3]; + + normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); + + for (int j = 0; j < samples; j++) { + interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); + madd_v3_v3fl(ray_co, ray_no, eps_offset); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = face_dists[index]; + if ((BLI_bvhtree_ray_cast( + tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) && + hit.dist < face_dists[index]) { + float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no)); + angle_fac = 1.0f - angle_fac; + angle_fac = angle_fac * angle_fac * angle_fac; + angle_fac = 1.0f - angle_fac; + hit.dist /= angle_fac; + if (hit.dist < face_dists[index]) { + face_dists[index] = hit.dist; + } + } + } + } + + const MPoly *mpoly = mr->mpoly; + for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) { + float fac = face_dists[p]; + fac = thickness_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mpoly->totloop; i++, l++) { + r_thickness[l] = fac; + } + } + } +} + +struct BVHTree_OverlapData { + const Mesh *me; + const MLoopTri *mlooptri; + float epsilon; +}; + +static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) +{ + struct BVHTree_OverlapData *data = userdata; + const Mesh *me = data->me; + + const MLoopTri *tri_a = &data->mlooptri[index_a]; + const MLoopTri *tri_b = &data->mlooptri[index_b]; + + if (UNLIKELY(tri_a->poly == tri_b->poly)) { + return false; + } + + const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co, + me->mvert[me->mloop[tri_a->tri[1]].v].co, + me->mvert[me->mloop[tri_a->tri[2]].v].co}; + const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co, + me->mvert[me->mloop[tri_b->tri[1]].v].co, + me->mvert[me->mloop[tri_b->tri[2]].v].co}; + float ix_pair[2][3]; + int verts_shared = 0; + + verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) + + ELEM(tri_a_co[2], UNPACK3(tri_b_co))); + + /* if 2 points are shared, bail out */ + if (verts_shared >= 2) { + return false; + } + + return (isect_tri_tri_epsilon_v3( + UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1], data->epsilon) && + /* if we share a vertex, check the intersection isn't a 'point' */ + ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon))); +} + +static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect) +{ + BMEditMesh *em = mr->edit_bmesh; + + for (int l = 0; l < mr->loop_len; l++) { + r_intersect[l] = -1.0f; + } + + if (mr->extract_type == MR_EXTRACT_BMESH) { + uint overlap_len; + BMesh *bm = em->bm; + + BM_mesh_elem_index_ensure(bm, BM_FACE); + + struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); + BVHTreeOverlap *overlap = BKE_bmbvh_overlap(bmtree, bmtree, &overlap_len); + + if (overlap) { + for (int i = 0; i < overlap_len; i++) { + BMFace *f_hit_pair[2] = { + em->looptris[overlap[i].indexA][0]->f, + em->looptris[overlap[i].indexB][0]->f, + }; + for (int j = 0; j < 2; j++) { + BMFace *f_hit = f_hit_pair[j]; + BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit); + int l = BM_elem_index_get(l_first); + for (int k = 0; k < f_hit->len; k++, l++) { + r_intersect[l] = 1.0f; + } + } + } + MEM_freeN(overlap); + } + + BKE_bmbvh_free(bmtree); + } + else { + uint overlap_len; + BVHTreeFromMesh treeData = {NULL}; + + BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); + + struct BVHTree_OverlapData data = { + .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)}; + + BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data); + if (overlap) { + for (int i = 0; i < overlap_len; i++) { + const MPoly *f_hit_pair[2] = { + &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly], + &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly], + }; + for (int j = 0; j < 2; j++) { + const MPoly *f_hit = f_hit_pair[j]; + int l = f_hit->loopstart; + for (int k = 0; k < f_hit->totloop; k++, l++) { + r_intersect[l] = 1.0f; + } + } + } + MEM_freeN(overlap); + } + } +} + +BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange) +{ + if (fac >= min) { + fac = (fac - min) * minmax_irange; + CLAMP(fac, 0.0f, 1.0f); + } + else { + /* fallback */ + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort) +{ + BMEditMesh *em = mr->edit_bmesh; + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->distort_min; + const float max = statvis->distort_max; + const float minmax_irange = 1.0f / (max - min); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMesh *bm = em->bm; + BMFace *f; + + int l = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + float fac = -1.0f; + + if (f->len > 3) { + BMLoop *l_iter, *l_first; + + fac = 0.0f; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float no_corner[3]; + BM_loop_calc_face_normal_safe(l_iter, no_corner); + /* simple way to detect (what is most likely) concave */ + if (dot_v3v3(f->no, no_corner) < 0.0f) { + negate_v3(no_corner); + } + fac = max_ff(fac, angle_normalized_v3v3(f->no, no_corner)); + } while ((l_iter = l_iter->next) != l_first); + fac *= 2.0f; + } + + fac = distort_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l++) { + r_distort[l] = fac; + } + } + } + else { + const MPoly *mpoly = mr->mpoly; + for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) { + float fac = -1.0f; + + if (mpoly->totloop > 3) { + float *f_no = mr->poly_normals[p]; + fac = 0.0f; + + for (int i = 1; i <= mpoly->totloop; i++) { + const MLoop *l_prev = &mr->mloop[mpoly->loopstart + (i - 1) % mpoly->totloop]; + const MLoop *l_curr = &mr->mloop[mpoly->loopstart + (i + 0) % mpoly->totloop]; + const MLoop *l_next = &mr->mloop[mpoly->loopstart + (i + 1) % mpoly->totloop]; + float no_corner[3]; + normal_tri_v3(no_corner, + mr->mvert[l_prev->v].co, + mr->mvert[l_curr->v].co, + mr->mvert[l_next->v].co); + /* simple way to detect (what is most likely) concave */ + if (dot_v3v3(f_no, no_corner) < 0.0f) { + negate_v3(no_corner); + } + fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner)); + } + fac *= 2.0f; + } + + fac = distort_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mpoly->totloop; i++, l++) { + r_distort[l] = fac; + } + } + } +} + +BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange) +{ + /* important not '>=' */ + if (fac > min) { + fac = (fac - min) * minmax_irange; + CLAMP(fac, 0.0f, 1.0f); + } + else { + /* fallback */ + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) +{ + BMEditMesh *em = mr->edit_bmesh; + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->sharp_min; + const float max = statvis->sharp_max; + const float minmax_irange = 1.0f / (max - min); + + /* Can we avoid this extra allocation? */ + float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__); + copy_vn_fl(vert_angles, mr->vert_len, -M_PI); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter, l_iter; + BMesh *bm = em->bm; + BMFace *efa; + BMEdge *e; + BMLoop *loop; + /* first assign float values to verts */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + float angle = BM_edge_calc_face_angle_signed(e); + float *col1 = &vert_angles[BM_elem_index_get(e->v1)]; + float *col2 = &vert_angles[BM_elem_index_get(e->v2)]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + /* Copy vert value to loops. */ + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) { + int l = BM_elem_index_get(loop); + int v = BM_elem_index_get(loop->v); + r_sharp[l] = sharp_remap(vert_angles[v], min, max, minmax_irange); + } + } + } + else { + /* first assign float values to verts */ + const MPoly *mpoly = mr->mpoly; + + EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len); + + for (int p = 0; p < mr->poly_len; p++, mpoly++) { + for (int i = 0; i < mpoly->totloop; i++) { + const MLoop *l_curr = &mr->mloop[mpoly->loopstart + (i + 0) % mpoly->totloop]; + const MLoop *l_next = &mr->mloop[mpoly->loopstart + (i + 1) % mpoly->totloop]; + const MVert *v_curr = &mr->mvert[l_curr->v]; + const MVert *v_next = &mr->mvert[l_next->v]; + float angle; + void **pval; + bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval); + if (!value_is_init) { + *pval = mr->poly_normals[p]; + /* non-manifold edge, yet... */ + continue; + } + else if (*pval != NULL) { + const float *f1_no = mr->poly_normals[p]; + const float *f2_no = *pval; + angle = angle_normalized_v3v3(f1_no, f2_no); + angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle; + /* Tag as manifold. */ + *pval = NULL; + } + else { + /* non-manifold edge */ + angle = DEG2RADF(90.0f); + } + float *col1 = &vert_angles[l_curr->v]; + float *col2 = &vert_angles[l_next->v]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + } + /* Remaining non manifold edges. */ + EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh); + for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + if (BLI_edgehashIterator_getValue(ehi) != NULL) { + uint v1, v2; + const float angle = DEG2RADF(90.0f); + BLI_edgehashIterator_getKey(ehi, &v1, &v2); + float *col1 = &vert_angles[v1]; + float *col2 = &vert_angles[v2]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + } + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(eh, NULL); + + const MLoop *mloop = mr->mloop; + for (int l = 0; l < mr->loop_len; l++, mloop++) { + r_sharp[l] = sharp_remap(vert_angles[mloop->v], min, max, minmax_irange); + } + } + + MEM_freeN(vert_angles); +} + +static void extract_mesh_analysis_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +{ + BLI_assert(mr->edit_bmesh); + + GPUVertBuf *vbo = buf; + float *l_weight = (float *)vbo->data; + + switch (mr->toolsettings->statvis.type) { + case SCE_STATVIS_OVERHANG: + statvis_calc_overhang(mr, l_weight); + break; + case SCE_STATVIS_THICKNESS: + statvis_calc_thickness(mr, l_weight); + break; + case SCE_STATVIS_INTERSECT: + statvis_calc_intersect(mr, l_weight); + break; + case SCE_STATVIS_DISTORT: + statvis_calc_distort(mr, l_weight); + break; + case SCE_STATVIS_SHARP: + statvis_calc_sharp(mr, l_weight); + break; + } +} + +const MeshExtract extract_mesh_analysis = {extract_mesh_analysis_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + extract_mesh_analysis_finish, + /* This is not needed for all vis type. + * Maybe split into different extract. */ + MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots positions + * \{ */ + +static void *extract_fdots_pos_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + if (!mr->use_subsurf_fdots) { + /* Clear so we can accumulate on it. */ + memset(vbo->data, 0x0, mr->poly_len * vbo->format.stride); + } + return vbo->data; +} + +static void extract_fdots_pos_loop_bmesh(const MeshRenderData *UNUSED(mr), + int UNUSED(l), + BMLoop *loop, + void *data) +{ + float(*center)[3] = (float(*)[3])data; + float w = 1.0f / (float)loop->f->len; + madd_v3_v3fl(center[BM_elem_index_get(loop->f)], loop->v->co, w); +} + +static void extract_fdots_pos_loop_mesh(const MeshRenderData *mr, + int UNUSED(l), + const MLoop *mloop, + int p, + const MPoly *mpoly, + void *data) +{ + float(*center)[3] = (float(*)[3])data; + const MVert *mvert = &mr->mvert[mloop->v]; + if (mr->use_subsurf_fdots) { + if (mvert->flag & ME_VERT_FACEDOT) { + copy_v3_v3(center[p], mvert->co); + } + } + else { + float w = 1.0f / (float)mpoly->totloop; + madd_v3_v3fl(center[p], mvert->co, w); + } +} + +const MeshExtract extract_fdots_pos = {extract_fdots_pos_init, + NULL, + NULL, + extract_fdots_pos_loop_bmesh, + extract_fdots_pos_loop_mesh, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, + true}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Normal and edit flag + * \{ */ + +static void *extract_fdots_nor_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + return NULL; +} + +static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + GPUPackedNormal *nor = (GPUPackedNormal *)vbo->data; + BMFace *efa; + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + for (int f = 0; f < mr->poly_len; f++) { + efa = BM_face_at_index(mr->bm, f); + nor[f] = GPU_normal_convert_i10_v3(efa->no); + /* Select / Active Flag. */ + nor[f].w = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == mr->efa_act) ? -1 : 1) : 0; + } + } + else { + for (int f = 0; f < mr->poly_len; f++) { + nor[f] = GPU_normal_convert_i10_v3(mr->poly_normals[f]); + if ((efa = bm_original_face_get(mr, f))) { + /* Select / Active Flag. */ + nor[f].w = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == mr->efa_act) ? -1 : 1) : 0; + } + } + } +} + +const MeshExtract extract_fdots_nor = {extract_fdots_nor_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + extract_fdots_nor_finish, + MR_DATA_POLY_NOR, + false}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Normal and edit flag + * \{ */ + +typedef struct MeshExtract_FdotUV_Data { + float (*vbo_data)[2]; + MLoopUV *uv_data; +} MeshExtract_FdotUV_Data; + +static void *extract_fdots_uv_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPU_vertformat_alias_add(&format, "au"); + GPU_vertformat_alias_add(&format, "pos"); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + if (!mr->use_subsurf_fdots) { + /* Clear so we can accumulate on it. */ + memset(vbo->data, 0x0, mr->poly_len * vbo->format.stride); + } + + CustomData *cd_ldata = &mr->me->ldata; + + MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (float(*)[2])vbo->data; + data->uv_data = CustomData_get_layer(cd_ldata, CD_MLOOPUV); + return data; +} + +static void extract_fdots_uv_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *_data) +{ + MeshExtract_FdotUV_Data *data = (MeshExtract_FdotUV_Data *)_data; + float w = 1.0f / (float)loop->f->len; + madd_v2_v2fl(data->vbo_data[BM_elem_index_get(loop->f)], data->uv_data[l].uv, w); +} + +static void extract_fdots_uv_loop_mesh( + const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data) +{ + MeshExtract_FdotUV_Data *data = (MeshExtract_FdotUV_Data *)_data; + if (mr->use_subsurf_fdots) { + const MVert *mvert = &mr->mvert[mloop->v]; + if (mvert->flag & ME_VERT_FACEDOT) { + copy_v2_v2(data->vbo_data[p], data->uv_data[l].uv); + } + } + else { + float w = 1.0f / (float)mpoly->totloop; + madd_v2_v2fl(data->vbo_data[p], data->uv_data[l].uv, w); + } +} + +static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_fdots_uv = {extract_fdots_uv_init, + NULL, + NULL, + extract_fdots_uv_loop_bmesh, + extract_fdots_uv_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_fdots_uv_finish, + 0, + true}; +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Edit UV flag + * \{ */ + +typedef struct MeshExtract_EditUVFdotData_Data { + EditLoopData *vbo_data; + int cd_ofs; +} MeshExtract_EditUVFdotData_Data; + +static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (EditLoopData *)vbo->data; + data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); + return data; +} + +static void extract_fdots_edituv_data_loop_bmesh(const MeshRenderData *mr, + int UNUSED(l), + BMLoop *loop, + void *_data) +{ + MeshExtract_EditUVFdotData_Data *data = (MeshExtract_EditUVFdotData_Data *)_data; + EditLoopData *eldata = data->vbo_data + BM_elem_index_get(loop->f); + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_face_flag(mr, loop->f, data->cd_ofs, eldata); +} + +static void extract_fdots_edituv_data_loop_mesh(const MeshRenderData *mr, + int UNUSED(l), + const MLoop *UNUSED(mloop), + int p, + const MPoly *UNUSED(mpoly), + void *_data) +{ + MeshExtract_EditUVFdotData_Data *data = (MeshExtract_EditUVFdotData_Data *)_data; + EditLoopData *eldata = data->vbo_data + p; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, p); + if (efa) { + mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); + } +} + +static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_fdots_edituv_data = {extract_fdots_edituv_data_init, + NULL, + NULL, + extract_fdots_edituv_data_loop_bmesh, + extract_fdots_edituv_data_loop_mesh, + NULL, + NULL, + NULL, + NULL, + extract_fdots_edituv_data_finish, + 0, + true}; +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Selection Index + * \{ */ + +static void *extract_select_idx_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* TODO rename "color" to something more descriptive. */ + GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + return vbo->data; +} + +/* TODO Use glVertexID to get loop index and use the data structure on the CPU to retrieve the + * select element associated with this loop ID. This would remove the need for this separate index + * VBOs. We could upload the p/e/v_origindex as a buffer texture and sample it inside the shader to + * output original index. */ + +static void extract_poly_idx_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *data) +{ + ((uint32_t *)data)[l] = BM_elem_index_get(loop->f); +} + +static void extract_edge_idx_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *data) +{ + ((uint32_t *)data)[l] = BM_elem_index_get(loop->e); +} + +static void extract_vert_idx_loop_bmesh(const MeshRenderData *UNUSED(mr), + int l, + BMLoop *loop, + void *data) +{ + ((uint32_t *)data)[l] = BM_elem_index_get(loop->v); +} + +static void extract_edge_idx_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *data) +{ + ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = BM_elem_index_get(eed); + ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = BM_elem_index_get(eed); +} + +static void extract_vert_idx_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *data) +{ + ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = BM_elem_index_get(eed->v1); + ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = BM_elem_index_get(eed->v2); +} + +static void extract_vert_idx_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *data) +{ + ((uint32_t *)data)[mr->loop_len + mr->edge_loose_len * 2 + v] = BM_elem_index_get(eve); +} + +static void extract_poly_idx_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *UNUSED(mloop), + int p, + const MPoly *UNUSED(mpoly), + void *data) +{ + ((uint32_t *)data)[l] = (mr->p_origindex) ? mr->p_origindex[p] : p; +} + +static void extract_edge_idx_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *UNUSED(mpoly), + void *data) +{ + ((uint32_t *)data)[l] = (mr->e_origindex) ? mr->e_origindex[mloop->e] : mloop->e; +} + +static void extract_vert_idx_loop_mesh(const MeshRenderData *mr, + int l, + const MLoop *mloop, + int UNUSED(p), + const MPoly *UNUSED(mpoly), + void *data) +{ + ((uint32_t *)data)[l] = (mr->v_origindex) ? mr->v_origindex[mloop->v] : mloop->v; +} + +static void extract_edge_idx_ledge_mesh(const MeshRenderData *mr, + int e, + const MEdge *UNUSED(medge), + void *data) +{ + int e_idx = mr->ledges[e]; + int e_orig = (mr->e_origindex) ? mr->e_origindex[e_idx] : e_idx; + ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = e_orig; + ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = e_orig; +} + +static void extract_vert_idx_ledge_mesh(const MeshRenderData *mr, + int e, + const MEdge *medge, + void *data) +{ + int v1_orig = (mr->v_origindex) ? mr->v_origindex[medge->v1] : medge->v1; + int v2_orig = (mr->v_origindex) ? mr->v_origindex[medge->v2] : medge->v2; + ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = v1_orig; + ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = v2_orig; +} + +static void extract_vert_idx_lvert_mesh(const MeshRenderData *mr, + int v, + const MVert *UNUSED(mvert), + void *data) +{ + int v_idx = mr->lverts[v]; + int v_orig = (mr->v_origindex) ? mr->v_origindex[v_idx] : v_idx; + ((uint32_t *)data)[mr->loop_len + mr->edge_loose_len * 2 + v] = v_orig; +} + +const MeshExtract extract_poly_idx = {extract_select_idx_init, + NULL, + NULL, + extract_poly_idx_loop_bmesh, + extract_poly_idx_loop_mesh, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, + true}; + +const MeshExtract extract_edge_idx = {extract_select_idx_init, + NULL, + NULL, + extract_edge_idx_loop_bmesh, + extract_edge_idx_loop_mesh, + extract_edge_idx_ledge_bmesh, + extract_edge_idx_ledge_mesh, + NULL, + NULL, + NULL, + 0, + true}; + +const MeshExtract extract_vert_idx = {extract_select_idx_init, + NULL, + NULL, + extract_vert_idx_loop_bmesh, + extract_vert_idx_loop_mesh, + extract_vert_idx_ledge_bmesh, + extract_vert_idx_ledge_mesh, + extract_vert_idx_lvert_bmesh, + extract_vert_idx_lvert_mesh, + NULL, + 0, + true}; + +static void *extract_select_fdot_idx_init(const MeshRenderData *mr, void *buf) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* TODO rename "color" to something more descriptive. */ + GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + return vbo->data; +} + +static void extract_fdot_idx_loop_bmesh(const MeshRenderData *UNUSED(mr), + int UNUSED(l), + BMLoop *loop, + void *data) +{ + ((uint32_t *)data)[BM_elem_index_get(loop->f)] = BM_elem_index_get(loop->f); +} + +static void extract_fdot_idx_loop_mesh(const MeshRenderData *mr, + int UNUSED(l), + const MLoop *UNUSED(mloop), + int p, + const MPoly *UNUSED(mpoly), + void *data) +{ + ((uint32_t *)data)[p] = (mr->p_origindex) ? mr->p_origindex[p] : p; +} + +const MeshExtract extract_fdot_idx = {extract_select_fdot_idx_init, + NULL, + NULL, + extract_fdot_idx_loop_bmesh, + extract_fdot_idx_loop_mesh, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, + true}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loop + * \{ */ + +typedef struct ExtractTaskData { + const MeshRenderData *mr; + const MeshExtract *extract; + eMRIterType iter_type; + int start, end; + /** Decremented each time a task is finished. */ + int32_t *task_counter; + void *buf; + void *user_data; +} ExtractTaskData; + +BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, + const eMRIterType iter_type, + int start, + int end, + const MeshExtract *extract, + void *user_data) +{ + switch (mr->extract_type) { + case MR_EXTRACT_BMESH: + if (iter_type & MR_ITER_LOOPTRI) { + int t_end = min_ii(mr->tri_len, end); + for (int t = start; t < t_end; t++) { + BMLoop **elt = &mr->edit_bmesh->looptris[t][0]; + extract->iter_looptri_bm(mr, t, elt, user_data); + } + } + if (iter_type & MR_ITER_LOOP) { + int l_end = min_ii(mr->poly_len, end); + for (int f = start; f < l_end; f++) { + BMFace *efa = BM_face_at_index(mr->bm, f); + BMLoop *loop; + BMIter l_iter; + BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) { + extract->iter_loop_bm(mr, BM_elem_index_get(loop), loop, user_data); + } + } + } + if (iter_type & MR_ITER_LEDGE) { + int le_end = min_ii(mr->edge_loose_len, end); + for (int e = start; e < le_end; e++) { + BMEdge *eed = BM_edge_at_index(mr->bm, mr->ledges[e]); + extract->iter_ledge_bm(mr, e, eed, user_data); + } + } + if (iter_type & MR_ITER_LVERT) { + int lv_end = min_ii(mr->vert_loose_len, end); + for (int v = start; v < lv_end; v++) { + BMVert *eve = BM_vert_at_index(mr->bm, mr->lverts[v]); + extract->iter_lvert_bm(mr, v, eve, user_data); + } + } + break; + case MR_EXTRACT_MAPPED: + case MR_EXTRACT_MESH: + if (iter_type & MR_ITER_LOOPTRI) { + int t_end = min_ii(mr->tri_len, end); + for (int t = start; t < t_end; t++) { + extract->iter_looptri(mr, t, &mr->mlooptri[t], user_data); + } + } + if (iter_type & MR_ITER_LOOP) { + int l_end = min_ii(mr->poly_len, end); + for (int p = start; p < l_end; p++) { + const MPoly *mpoly = &mr->mpoly[p]; + int l = mpoly->loopstart; + for (int i = 0; i < mpoly->totloop; i++, l++) { + extract->iter_loop(mr, l, &mr->mloop[l], p, mpoly, user_data); + } + } + } + if (iter_type & MR_ITER_LEDGE) { + int le_end = min_ii(mr->edge_loose_len, end); + for (int e = start; e < le_end; e++) { + extract->iter_ledge(mr, e, &mr->medge[mr->ledges[e]], user_data); + } + } + if (iter_type & MR_ITER_LVERT) { + int lv_end = min_ii(mr->vert_loose_len, end); + for (int v = start; v < lv_end; v++) { + extract->iter_lvert(mr, v, &mr->mvert[mr->lverts[v]], user_data); + } + } + break; + } +} + +static void extract_run(TaskPool *__restrict UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + ExtractTaskData *data = taskdata; + mesh_extract_iter( + data->mr, data->iter_type, data->start, data->end, data->extract, data->user_data); + + /* If this is the last task, we do the finish function. */ + int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); + if (remainin_tasks == 0 && data->extract->finish != NULL) { + data->extract->finish(data->mr, data->buf, data->user_data); + } +} + +static void extract_range_task_create( + TaskPool *task_pool, ExtractTaskData *taskdata, const eMRIterType type, int start, int length) +{ + taskdata = MEM_dupallocN(taskdata); + atomic_add_and_fetch_int32(taskdata->task_counter, 1); + taskdata->iter_type = type; + taskdata->start = start; + taskdata->end = start + length; + BLI_task_pool_push(task_pool, extract_run, taskdata, true, TASK_PRIORITY_HIGH); +} + +static void extract_task_create(TaskPool *task_pool, + const MeshRenderData *mr, + const MeshExtract *extract, + void *buf, + int32_t *task_counter) +{ + /* Divide extraction of the VBO/IBO into sensible chunks of works. */ + ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), "ExtractTaskData"); + taskdata->mr = mr; + taskdata->extract = extract; + taskdata->buf = buf; + taskdata->user_data = extract->init(mr, buf); + taskdata->iter_type = mesh_extract_iter_type(extract); + taskdata->task_counter = task_counter; + taskdata->start = 0; + taskdata->end = INT_MAX; + + /* Simple heuristic. */ + const bool use_thread = (mr->loop_len + mr->loop_loose_len) > 8192; + if (use_thread && extract->use_threading) { + /* Divide task into sensible chunks. */ + const int chunk_size = 8192; + if (taskdata->iter_type & MR_ITER_LOOPTRI) { + for (int i = 0; i < mr->tri_len; i += chunk_size) { + extract_range_task_create(task_pool, taskdata, MR_ITER_LOOPTRI, i, chunk_size); + } + } + if (taskdata->iter_type & MR_ITER_LOOP) { + for (int i = 0; i < mr->poly_len; i += chunk_size) { + extract_range_task_create(task_pool, taskdata, MR_ITER_LOOP, i, chunk_size); + } + } + if (taskdata->iter_type & MR_ITER_LEDGE) { + for (int i = 0; i < mr->edge_loose_len; i += chunk_size) { + extract_range_task_create(task_pool, taskdata, MR_ITER_LEDGE, i, chunk_size); + } + } + if (taskdata->iter_type & MR_ITER_LVERT) { + for (int i = 0; i < mr->vert_loose_len; i += chunk_size) { + extract_range_task_create(task_pool, taskdata, MR_ITER_LVERT, i, chunk_size); + } + } + MEM_freeN(taskdata); + } + else if (use_thread) { + /* One task for the whole VBO. */ + (*task_counter)++; + BLI_task_pool_push(task_pool, extract_run, taskdata, true, TASK_PRIORITY_HIGH); + } + else { + /* Single threaded extraction. */ + (*task_counter)++; + extract_run(NULL, taskdata, -1); + MEM_freeN(taskdata); + } +} + +void mesh_buffer_cache_create_requested(MeshBatchCache *cache, + MeshBufferCache mbc, + Mesh *me, + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const DRW_MeshCDMask *cd_layer_used, + const ToolSettings *ts, + const bool use_hide) +{ + eMRIterType iter_flag = 0; + eMRDataType data_flag = 0; + +#define TEST_ASSIGN(type, type_lowercase, name) \ + do { \ + if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ + iter_flag |= mesh_extract_iter_type(&extract_##name); \ + data_flag |= extract_##name.data_flag; \ + } \ + } while (0) + + TEST_ASSIGN(VBO, vbo, pos_nor); + TEST_ASSIGN(VBO, vbo, lnor); + TEST_ASSIGN(VBO, vbo, uv); + TEST_ASSIGN(VBO, vbo, tan); + TEST_ASSIGN(VBO, vbo, vcol); + TEST_ASSIGN(VBO, vbo, orco); + TEST_ASSIGN(VBO, vbo, edge_fac); + TEST_ASSIGN(VBO, vbo, weights); + TEST_ASSIGN(VBO, vbo, edit_data); + TEST_ASSIGN(VBO, vbo, edituv_data); + TEST_ASSIGN(VBO, vbo, stretch_area); + TEST_ASSIGN(VBO, vbo, stretch_angle); + TEST_ASSIGN(VBO, vbo, mesh_analysis); + TEST_ASSIGN(VBO, vbo, fdots_pos); + TEST_ASSIGN(VBO, vbo, fdots_nor); + TEST_ASSIGN(VBO, vbo, fdots_uv); + TEST_ASSIGN(VBO, vbo, fdots_edituv_data); + TEST_ASSIGN(VBO, vbo, poly_idx); + TEST_ASSIGN(VBO, vbo, edge_idx); + TEST_ASSIGN(VBO, vbo, vert_idx); + TEST_ASSIGN(VBO, vbo, fdot_idx); + + TEST_ASSIGN(IBO, ibo, tris); + TEST_ASSIGN(IBO, ibo, lines); + TEST_ASSIGN(IBO, ibo, points); + TEST_ASSIGN(IBO, ibo, fdots); + TEST_ASSIGN(IBO, ibo, lines_paint_mask); + TEST_ASSIGN(IBO, ibo, lines_adjacency); + TEST_ASSIGN(IBO, ibo, edituv_tris); + TEST_ASSIGN(IBO, ibo, edituv_lines); + TEST_ASSIGN(IBO, ibo, edituv_points); + TEST_ASSIGN(IBO, ibo, edituv_fdots); + +#undef TEST_ASSIGN + +#ifdef DEBUG_TIME + double rdata_start = PIL_check_seconds_timer(); +#endif + + MeshRenderData *mr = mesh_render_data_create( + me, do_final, do_uvedit, iter_flag, data_flag, cd_layer_used, ts); + mr->cache = cache; /* HACK */ + mr->use_hide = use_hide; + mr->use_subsurf_fdots = use_subsurf_fdots; + mr->use_final_mesh = do_final; + +#ifdef DEBUG_TIME + double rdata_end = PIL_check_seconds_timer(); +#endif + + TaskScheduler *task_scheduler; + TaskPool *task_pool; + + task_scheduler = BLI_task_scheduler_get(); + task_pool = BLI_task_pool_create(task_scheduler, NULL); + + size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t); + int32_t *task_counters = MEM_callocN(counters_size, __func__); + int counter_used = 0; + +#define EXTRACT(buf, name) \ + if (mbc.buf.name) { \ + extract_task_create( \ + task_pool, mr, &extract_##name, mbc.buf.name, &task_counters[counter_used++]); \ + } + + EXTRACT(vbo, pos_nor); + EXTRACT(vbo, lnor); + EXTRACT(vbo, uv); + EXTRACT(vbo, tan); + EXTRACT(vbo, vcol); + EXTRACT(vbo, orco); + EXTRACT(vbo, edge_fac); + EXTRACT(vbo, weights); + EXTRACT(vbo, edit_data); + EXTRACT(vbo, edituv_data); + EXTRACT(vbo, stretch_area); + EXTRACT(vbo, stretch_angle); + EXTRACT(vbo, mesh_analysis); + EXTRACT(vbo, fdots_pos); + EXTRACT(vbo, fdots_nor); + EXTRACT(vbo, fdots_uv); + EXTRACT(vbo, fdots_edituv_data); + EXTRACT(vbo, poly_idx); + EXTRACT(vbo, edge_idx); + EXTRACT(vbo, vert_idx); + EXTRACT(vbo, fdot_idx); + + EXTRACT(ibo, tris); + EXTRACT(ibo, lines); + EXTRACT(ibo, points); + EXTRACT(ibo, fdots); + EXTRACT(ibo, lines_paint_mask); + EXTRACT(ibo, lines_adjacency); + EXTRACT(ibo, edituv_tris); + EXTRACT(ibo, edituv_lines); + EXTRACT(ibo, edituv_points); + EXTRACT(ibo, edituv_fdots); + +#undef EXTRACT + + /* TODO(fclem) Ideally, we should have one global pool for all + * objects and wait for finish only before drawing when buffers + * need to be ready. */ + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + MEM_freeN(task_counters); + + mesh_render_data_free(mr); + +#ifdef DEBUG_TIME + double end = PIL_check_seconds_timer(); + + static double avg = 0; + static double avg_fps = 0; + static double avg_rdata = 0; + static double end_prev = 0; + + if (end_prev == 0) { + end_prev = end; + } + + avg = avg * 0.95 + (end - rdata_end) * 0.05; + avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; + avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; + + printf( + "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); + + end_prev = end; +#endif +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 4dc58972ce6..d392db63938 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -119,7 +119,7 @@ struct GPUBatch *DRW_lattice_batch_cache_get_edit_verts(struct Lattice *lt); /* Mesh */ void DRW_mesh_batch_cache_create_requested(struct Object *ob, struct Mesh *me, - const struct ToolSettings *ts, + const struct Scene *scene, const bool is_paint_mode, const bool use_hide); @@ -143,6 +143,7 @@ struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_edit_triangles(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_edit_vertices(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_edit_edges(struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_edit_vnors(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_edit_lnors(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_edit_facedots(struct Mesh *me); /* edit-mesh selection */ diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index ba58dc3d9de..12c6a715685 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -64,313 +64,12 @@ #include "ED_uvedit.h" #include "draw_cache_inline.h" +#include "draw_cache_extract.h" #include "draw_cache_impl.h" /* own include */ static void mesh_batch_cache_clear(Mesh *me); -/* Vertex Group Selection and display options */ -typedef struct DRW_MeshWeightState { - int defgroup_active; - int defgroup_len; - - short flags; - char alert_mode; - - /* Set of all selected bones for Multipaint. */ - bool *defgroup_sel; /* [defgroup_len] */ - int defgroup_sel_count; -} DRW_MeshWeightState; - -typedef struct DRW_MeshCDMask { - uint32_t uv : 8; - uint32_t tan : 8; - uint32_t vcol : 8; - uint32_t orco : 1; - uint32_t tan_orco : 1; -} DRW_MeshCDMask; - -/* DRW_MeshWeightState.flags */ -enum { - DRW_MESH_WEIGHT_STATE_MULTIPAINT = (1 << 0), - DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE = (1 << 1), -}; - -/* ---------------------------------------------------------------------- */ -/** \name BMesh Inline Wrappers - * \{ */ - -/** - * Wrapper for #BM_vert_find_first_loop_visible - * since most of the time this can be accessed directly without a function call. - */ -BLI_INLINE BMLoop *bm_vert_find_first_loop_visible_inline(BMVert *v) -{ - if (v->e) { - BMLoop *l = v->e->l; - if (l && !BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { - return l->v == v ? l : l->next; - } - return BM_vert_find_first_loop_visible(v); - } - return NULL; -} - -BLI_INLINE BMLoop *bm_edge_find_first_loop_visible_inline(BMEdge *e) -{ - if (e->l) { - BMLoop *l = e->l; - if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { - return l; - } - return BM_edge_find_first_loop_visible(e); - } - return NULL; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh/BMesh Interface (direct access to basic data). - * \{ */ - -static int mesh_render_verts_len_get(Mesh *me) -{ - return me->edit_mesh ? me->edit_mesh->bm->totvert : me->totvert; -} - -static int mesh_render_edges_len_get(Mesh *me) -{ - return me->edit_mesh ? me->edit_mesh->bm->totedge : me->totedge; -} - -static int mesh_render_looptri_len_get(Mesh *me) -{ - return me->edit_mesh ? me->edit_mesh->tottri : poly_to_tri_count(me->totpoly, me->totloop); -} - -static int mesh_render_polys_len_get(Mesh *me) -{ - return me->edit_mesh ? me->edit_mesh->bm->totface : me->totpoly; -} - -static int mesh_render_mat_len_get(Mesh *me) -{ - return MAX2(1, me->totcol); -} - -static int UNUSED_FUNCTION(mesh_render_loops_len_get)(Mesh *me) -{ - return me->edit_mesh ? me->edit_mesh->bm->totloop : me->totloop; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). - * \{ */ - -typedef struct EdgeAdjacentPolys { - int count; - int face_index[2]; -} EdgeAdjacentPolys; - -typedef struct EdgeAdjacentVerts { - int vert_index[2]; /* -1 if none */ -} EdgeAdjacentVerts; - -typedef struct EdgeDrawAttr { - uchar v_flag; - uchar e_flag; - uchar crease; - uchar bweight; -} EdgeDrawAttr; - -typedef struct MeshRenderData { - int types; - - int vert_len; - int edge_len; - int tri_len; - int loop_len; - int poly_len; - int mat_len; - int loose_vert_len; - int loose_edge_len; - - /* Support for mapped mesh data. */ - struct { - /* Must be set if we want to get mapped data. */ - bool use; - bool supported; - - Mesh *me_cage; - - int vert_len; - int edge_len; - int tri_len; - int loop_len; - int poly_len; - - int *loose_verts; - int loose_vert_len; - - int *loose_edges; - int loose_edge_len; - - /* origindex layers */ - int *v_origindex; - int *e_origindex; - int *l_origindex; - int *p_origindex; - } mapped; - - BMEditMesh *edit_bmesh; - struct EditMeshData *edit_data; - const ToolSettings *toolsettings; - - Mesh *me; - - MVert *mvert; - const MEdge *medge; - const MLoop *mloop; - const MPoly *mpoly; - float (*orco)[3]; /* vertex coordinates normalized to bounding box */ - bool is_orco_allocated; - MDeformVert *dvert; - MLoopUV *mloopuv; - MLoopCol *mloopcol; - float (*loop_normals)[3]; - - /* CustomData 'cd' cache for efficient access. */ - struct { - struct { - MLoopUV **uv; - MLoopCol **vcol; - float (**tangent)[4]; - - int uv_len; - int uv_active; - int uv_render; - int uv_mask_active; - - int vcol_len; - int vcol_active; - int vcol_render; - - int tangent_len; - int tangent_active; - int tangent_render; - - bool *auto_vcol; - } layers; - - /* Custom-data offsets (only needed for BMesh access) */ - struct { - int crease; - int bweight; - int *uv; - int *vcol; -#ifdef WITH_FREESTYLE - int freestyle_edge; - int freestyle_face; -#endif - } offset; - - struct { - char (*auto_mix)[32]; - char (*uv)[32]; - char (*vcol)[32]; - char (*tangent)[32]; - } uuid; - - /* for certain cases we need an output loop-data storage (bmesh tangents) */ - struct { - CustomData ldata; - /* grr, special case variable (use in place of 'dm->tangent_mask') */ - short tangent_mask; - } output; - } cd; - - BMVert *eve_act; - BMEdge *eed_act; - BMFace *efa_act; - BMFace *efa_act_uv; - - /* Data created on-demand (usually not for bmesh-based data). */ - EdgeAdjacentPolys *edges_adjacent_polys; - MLoopTri *mlooptri; - int *loose_edges; - int *loose_verts; - - float (*poly_normals)[3]; - float *vert_weight; - char (*vert_color)[3]; - GPUPackedNormal *poly_normals_pack; - GPUPackedNormal *vert_normals_pack; - bool *edge_select_bool; - bool *edge_visible_bool; -} MeshRenderData; - -typedef enum eMRDataType { - MR_DATATYPE_VERT = 1 << 0, - MR_DATATYPE_EDGE = 1 << 1, - MR_DATATYPE_LOOPTRI = 1 << 2, - MR_DATATYPE_LOOP = 1 << 3, - MR_DATATYPE_POLY = 1 << 4, - MR_DATATYPE_OVERLAY = 1 << 5, - MR_DATATYPE_SHADING = 1 << 6, - MR_DATATYPE_DVERT = 1 << 7, - MR_DATATYPE_LOOPCOL = 1 << 8, - MR_DATATYPE_LOOPUV = 1 << 9, - MR_DATATYPE_LOOSE_VERT = 1 << 10, - MR_DATATYPE_LOOSE_EDGE = 1 << 11, - MR_DATATYPE_LOOP_NORMALS = 1 << 12, -} eMRDataType; - -#define MR_DATATYPE_VERT_LOOP_POLY (MR_DATATYPE_VERT | MR_DATATYPE_POLY | MR_DATATYPE_LOOP) -#define MR_DATATYPE_VERT_LOOP_TRI_POLY (MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_LOOPTRI) -#define MR_DATATYPE_LOOSE_VERT_EGDE (MR_DATATYPE_LOOSE_VERT | MR_DATATYPE_LOOSE_EDGE) - -/** - * These functions look like they would be slow but they will typically return true on the first - * iteration. Only false when all attached elements are hidden. - */ -static bool bm_vert_has_visible_edge(const BMVert *v) -{ - const BMEdge *e_iter, *e_first; - - e_iter = e_first = v->e; - do { - if (!BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN)) { - return true; - } - } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); - return false; -} - -static bool bm_edge_has_visible_face(const BMEdge *e) -{ - const BMLoop *l_iter, *l_first; - l_iter = l_first = e->l; - do { - if (!BM_elem_flag_test(l_iter->f, BM_ELEM_HIDDEN)) { - return true; - } - } while ((l_iter = l_iter->radial_next) != l_first); - return false; -} - -BLI_INLINE bool bm_vert_is_loose_and_visible(const BMVert *v) -{ - return (!BM_elem_flag_test(v, BM_ELEM_HIDDEN) && (v->e == NULL || !bm_vert_has_visible_edge(v))); -} - -BLI_INLINE bool bm_edge_is_loose_and_visible(const BMEdge *e) -{ - return (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && (e->l == NULL || !bm_edge_has_visible_face(e))); -} - /* Return true is all layers in _b_ are inside _a_. */ BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b) { @@ -523,43 +222,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, return cd_used; } -static void mesh_render_calc_normals_loop_and_poly(const Mesh *me, - const float split_angle, - MeshRenderData *rdata) -{ - BLI_assert((me->flag & ME_AUTOSMOOTH) != 0); - - int totloop = me->totloop; - int totpoly = me->totpoly; - float(*loop_normals)[3] = MEM_mallocN(sizeof(*loop_normals) * totloop, __func__); - float(*poly_normals)[3] = MEM_mallocN(sizeof(*poly_normals) * totpoly, __func__); - short(*clnors)[2] = CustomData_get_layer(&me->ldata, CD_CUSTOMLOOPNORMAL); - - BKE_mesh_calc_normals_poly( - me->mvert, NULL, me->totvert, me->mloop, me->mpoly, totloop, totpoly, poly_normals, false); - - BKE_mesh_normals_loop_split(me->mvert, - me->totvert, - me->medge, - me->totedge, - me->mloop, - loop_normals, - totloop, - me->mpoly, - poly_normals, - totpoly, - true, - split_angle, - NULL, - clnors, - NULL); - - rdata->loop_len = totloop; - rdata->poly_len = totpoly; - rdata->loop_normals = loop_normals; - rdata->poly_normals = poly_normals; -} - static void mesh_cd_extract_auto_layers_names_and_srgb(Mesh *me, DRW_MeshCDMask cd_used, char **r_auto_layers_names, @@ -582,10 +244,12 @@ static void mesh_cd_extract_auto_layers_names_and_srgb(Mesh *me, for (int i = 0; i < uv_len; i++) { if ((cd_used.uv & (1 << i)) != 0) { const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); - uint hash = BLI_ghashutil_strhash_p(name); + char safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; + GPU_vertformat_safe_attrib_name(name, safe_name, GPU_MAX_SAFE_ATTRIB_NAME); + auto_ofs += BLI_snprintf_rlen( + auto_names + auto_ofs, auto_names_len - auto_ofs, "ba%s", safe_name); /* +1 to include '\0' terminator. */ - auto_ofs += 1 + BLI_snprintf_rlen( - auto_names + auto_ofs, auto_names_len - auto_ofs, "ba%u", hash); + auto_ofs += 1; } } @@ -595,10 +259,12 @@ static void mesh_cd_extract_auto_layers_names_and_srgb(Mesh *me, const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); /* We only do vcols that are not overridden by a uv layer with same name. */ if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, name) == -1) { - uint hash = BLI_ghashutil_strhash_p(name); + char safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; + GPU_vertformat_safe_attrib_name(name, safe_name, GPU_MAX_SAFE_ATTRIB_NAME); + auto_ofs += BLI_snprintf_rlen( + auto_names + auto_ofs, auto_names_len - auto_ofs, "ba%s", safe_name); /* +1 to include '\0' terminator. */ - auto_ofs += 1 + BLI_snprintf_rlen( - auto_names + auto_ofs, auto_names_len - auto_ofs, "ba%u", hash); + auto_ofs += 1; auto_is_srgb[auto_is_srgb_ofs] = true; auto_is_srgb_ofs++; } @@ -617,1265 +283,6 @@ static void mesh_cd_extract_auto_layers_names_and_srgb(Mesh *me, *r_auto_layers_len = auto_is_srgb_ofs; } -/** - * TODO(campbell): 'gpumat_array' may include materials linked to the object. - * While not default, object materials should be supported. - * Although this only impacts the data that's generated, not the materials that display. - */ -static MeshRenderData *mesh_render_data_create_ex(Mesh *me, - const int types, - const DRW_MeshCDMask *cd_used, - const ToolSettings *ts) -{ - MeshRenderData *rdata = MEM_callocN(sizeof(*rdata), __func__); - rdata->types = types; - rdata->toolsettings = ts; - rdata->mat_len = mesh_render_mat_len_get(me); - - CustomData_reset(&rdata->cd.output.ldata); - - const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; - const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; - - if (me->edit_mesh) { - BMEditMesh *embm = me->edit_mesh; - BMesh *bm = embm->bm; - - rdata->edit_bmesh = embm; - rdata->edit_data = me->runtime.edit_data; - - if (embm->mesh_eval_cage && (embm->mesh_eval_cage->runtime.is_original == false)) { - Mesh *me_cage = embm->mesh_eval_cage; - - rdata->mapped.me_cage = me_cage; - if (types & MR_DATATYPE_VERT) { - rdata->mapped.vert_len = me_cage->totvert; - } - if (types & MR_DATATYPE_EDGE) { - rdata->mapped.edge_len = me_cage->totedge; - } - if (types & MR_DATATYPE_LOOP) { - rdata->mapped.loop_len = me_cage->totloop; - } - if (types & MR_DATATYPE_POLY) { - rdata->mapped.poly_len = me_cage->totpoly; - } - if (types & MR_DATATYPE_LOOPTRI) { - rdata->mapped.tri_len = poly_to_tri_count(me_cage->totpoly, me_cage->totloop); - } - if (types & MR_DATATYPE_LOOPUV) { - rdata->mloopuv = CustomData_get_layer(&me_cage->ldata, CD_MLOOPUV); - } - - rdata->mapped.v_origindex = CustomData_get_layer(&me_cage->vdata, CD_ORIGINDEX); - rdata->mapped.e_origindex = CustomData_get_layer(&me_cage->edata, CD_ORIGINDEX); - rdata->mapped.l_origindex = CustomData_get_layer(&me_cage->ldata, CD_ORIGINDEX); - rdata->mapped.p_origindex = CustomData_get_layer(&me_cage->pdata, CD_ORIGINDEX); - rdata->mapped.supported = (rdata->mapped.v_origindex || rdata->mapped.e_origindex || - rdata->mapped.p_origindex); - } - - int bm_ensure_types = 0; - if (types & MR_DATATYPE_VERT) { - rdata->vert_len = bm->totvert; - bm_ensure_types |= BM_VERT; - } - if (types & MR_DATATYPE_EDGE) { - rdata->edge_len = bm->totedge; - bm_ensure_types |= BM_EDGE; - } - if (types & MR_DATATYPE_LOOPTRI) { - bm_ensure_types |= BM_LOOP; - } - if (types & MR_DATATYPE_LOOP) { - rdata->loop_len = bm->totloop; - bm_ensure_types |= BM_LOOP; - } - if (types & MR_DATATYPE_POLY) { - rdata->poly_len = bm->totface; - bm_ensure_types |= BM_FACE; - } - if (types & MR_DATATYPE_LOOP_NORMALS) { - BLI_assert(types & MR_DATATYPE_LOOP); - if (is_auto_smooth) { - rdata->loop_normals = MEM_mallocN(sizeof(*rdata->loop_normals) * bm->totloop, __func__); - int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); - BM_loops_calc_normal_vcos(bm, - NULL, - NULL, - NULL, - true, - split_angle, - rdata->loop_normals, - NULL, - NULL, - cd_loop_clnors_offset, - false); - } - } - if (types & MR_DATATYPE_OVERLAY) { - rdata->efa_act_uv = EDBM_uv_active_face_get(embm, false, false); - rdata->efa_act = BM_mesh_active_face_get(bm, false, true); - rdata->eed_act = BM_mesh_active_edge_get(bm); - rdata->eve_act = BM_mesh_active_vert_get(bm); - rdata->cd.offset.crease = CustomData_get_offset(&bm->edata, CD_CREASE); - rdata->cd.offset.bweight = CustomData_get_offset(&bm->edata, CD_BWEIGHT); - -#ifdef WITH_FREESTYLE - rdata->cd.offset.freestyle_edge = CustomData_get_offset(&bm->edata, CD_FREESTYLE_EDGE); - rdata->cd.offset.freestyle_face = CustomData_get_offset(&bm->pdata, CD_FREESTYLE_FACE); -#endif - } - if (types & (MR_DATATYPE_DVERT)) { - bm_ensure_types |= BM_VERT; - } - if (rdata->edit_data != NULL) { - bm_ensure_types |= BM_VERT; - } - - BM_mesh_elem_index_ensure(bm, bm_ensure_types); - BM_mesh_elem_table_ensure(bm, bm_ensure_types & ~BM_LOOP); - - if (types & MR_DATATYPE_LOOPTRI) { - /* Edit mode ensures this is valid, no need to calculate. */ - BLI_assert((bm->totloop == 0) || (embm->looptris != NULL)); - int tottri = embm->tottri; - MLoopTri *mlooptri = MEM_mallocN(sizeof(*rdata->mlooptri) * embm->tottri, __func__); - for (int index = 0; index < tottri; index++) { - BMLoop **bmtri = embm->looptris[index]; - MLoopTri *mtri = &mlooptri[index]; - mtri->tri[0] = BM_elem_index_get(bmtri[0]); - mtri->tri[1] = BM_elem_index_get(bmtri[1]); - mtri->tri[2] = BM_elem_index_get(bmtri[2]); - } - rdata->mlooptri = mlooptri; - rdata->tri_len = tottri; - } - - if (types & MR_DATATYPE_LOOSE_VERT) { - BLI_assert(types & MR_DATATYPE_VERT); - rdata->loose_vert_len = 0; - - { - int *lverts = MEM_mallocN(rdata->vert_len * sizeof(int), __func__); - BLI_assert((bm->elem_table_dirty & BM_VERT) == 0); - for (int i = 0; i < bm->totvert; i++) { - const BMVert *eve = BM_vert_at_index(bm, i); - if (bm_vert_is_loose_and_visible(eve)) { - lverts[rdata->loose_vert_len++] = i; - } - } - rdata->loose_verts = MEM_reallocN(lverts, rdata->loose_vert_len * sizeof(int)); - } - - if (rdata->mapped.supported) { - Mesh *me_cage = embm->mesh_eval_cage; - rdata->mapped.loose_vert_len = 0; - - if (rdata->loose_vert_len) { - int *lverts = MEM_mallocN(me_cage->totvert * sizeof(int), __func__); - const int *v_origindex = rdata->mapped.v_origindex; - for (int i = 0; i < me_cage->totvert; i++) { - const int v_orig = v_origindex[i]; - if (v_orig != ORIGINDEX_NONE) { - BMVert *eve = BM_vert_at_index(bm, v_orig); - if (bm_vert_is_loose_and_visible(eve)) { - lverts[rdata->mapped.loose_vert_len++] = i; - } - } - } - rdata->mapped.loose_verts = MEM_reallocN(lverts, - rdata->mapped.loose_vert_len * sizeof(int)); - } - } - } - - if (types & MR_DATATYPE_LOOSE_EDGE) { - BLI_assert(types & MR_DATATYPE_EDGE); - rdata->loose_edge_len = 0; - - { - int *ledges = MEM_mallocN(rdata->edge_len * sizeof(int), __func__); - BLI_assert((bm->elem_table_dirty & BM_EDGE) == 0); - for (int i = 0; i < bm->totedge; i++) { - const BMEdge *eed = BM_edge_at_index(bm, i); - if (bm_edge_is_loose_and_visible(eed)) { - ledges[rdata->loose_edge_len++] = i; - } - } - rdata->loose_edges = MEM_reallocN(ledges, rdata->loose_edge_len * sizeof(int)); - } - - if (rdata->mapped.supported) { - Mesh *me_cage = embm->mesh_eval_cage; - rdata->mapped.loose_edge_len = 0; - - if (rdata->loose_edge_len) { - int *ledges = MEM_mallocN(me_cage->totedge * sizeof(int), __func__); - const int *e_origindex = rdata->mapped.e_origindex; - for (int i = 0; i < me_cage->totedge; i++) { - const int e_orig = e_origindex[i]; - if (e_orig != ORIGINDEX_NONE) { - BMEdge *eed = BM_edge_at_index(bm, e_orig); - if (bm_edge_is_loose_and_visible(eed)) { - ledges[rdata->mapped.loose_edge_len++] = i; - } - } - } - rdata->mapped.loose_edges = MEM_reallocN(ledges, - rdata->mapped.loose_edge_len * sizeof(int)); - } - } - } - } - else { - rdata->me = me; - - if (types & (MR_DATATYPE_VERT)) { - rdata->vert_len = me->totvert; - rdata->mvert = CustomData_get_layer(&me->vdata, CD_MVERT); - } - if (types & (MR_DATATYPE_EDGE)) { - rdata->edge_len = me->totedge; - rdata->medge = CustomData_get_layer(&me->edata, CD_MEDGE); - } - if (types & MR_DATATYPE_LOOPTRI) { - const int tri_len = rdata->tri_len = poly_to_tri_count(me->totpoly, me->totloop); - MLoopTri *mlooptri = MEM_mallocN(sizeof(*mlooptri) * tri_len, __func__); - BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mlooptri); - rdata->mlooptri = mlooptri; - } - if (types & MR_DATATYPE_LOOP) { - rdata->loop_len = me->totloop; - rdata->mloop = CustomData_get_layer(&me->ldata, CD_MLOOP); - } - if (types & MR_DATATYPE_LOOP_NORMALS) { - BLI_assert(types & MR_DATATYPE_LOOP); - if (is_auto_smooth) { - mesh_render_calc_normals_loop_and_poly(me, split_angle, rdata); - } - } - if (types & MR_DATATYPE_POLY) { - rdata->poly_len = me->totpoly; - rdata->mpoly = CustomData_get_layer(&me->pdata, CD_MPOLY); - } - if (types & MR_DATATYPE_DVERT) { - rdata->vert_len = me->totvert; - rdata->dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT); - } - if (types & MR_DATATYPE_LOOPCOL) { - rdata->loop_len = me->totloop; - rdata->mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); - } - if (types & MR_DATATYPE_LOOPUV) { - rdata->loop_len = me->totloop; - rdata->mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV); - } - } - - if (types & MR_DATATYPE_SHADING) { - CustomData *cd_vdata, *cd_ldata; - - BLI_assert(cd_used != NULL); - - if (me->edit_mesh) { - BMesh *bm = me->edit_mesh->bm; - cd_vdata = &bm->vdata; - cd_ldata = &bm->ldata; - } - else { - cd_vdata = &me->vdata; - cd_ldata = &me->ldata; - } - - rdata->cd.layers.uv_active = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV); - rdata->cd.layers.uv_render = CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); - rdata->cd.layers.uv_mask_active = CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV); - rdata->cd.layers.vcol_active = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL); - rdata->cd.layers.vcol_render = CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL); - rdata->cd.layers.tangent_active = rdata->cd.layers.uv_active; - rdata->cd.layers.tangent_render = rdata->cd.layers.uv_render; - -#define CD_VALIDATE_ACTIVE_LAYER(active_index, used) \ - if ((active_index != -1) && (used & (1 << active_index)) == 0) { \ - active_index = -1; \ - } \ - ((void)0) - - CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.uv_active, cd_used->uv); - CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.uv_render, cd_used->uv); - CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.uv_mask_active, cd_used->uv); - CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.tangent_active, cd_used->tan); - CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.tangent_render, cd_used->tan); - CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.vcol_active, cd_used->vcol); - CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.vcol_render, cd_used->vcol); - -#undef CD_VALIDATE_ACTIVE_LAYER - - rdata->is_orco_allocated = false; - if (cd_used->orco != 0) { - rdata->orco = CustomData_get_layer(cd_vdata, CD_ORCO); - /* If orco is not available compute it ourselves */ - if (!rdata->orco) { - rdata->is_orco_allocated = true; - if (me->edit_mesh) { - BMesh *bm = me->edit_mesh->bm; - rdata->orco = MEM_mallocN(sizeof(*rdata->orco) * rdata->vert_len, "orco mesh"); - BLI_assert((bm->elem_table_dirty & BM_VERT) == 0); - for (int i = 0; i < bm->totvert; i++) { - copy_v3_v3(rdata->orco[i], BM_vert_at_index(bm, i)->co); - } - BKE_mesh_orco_verts_transform(me, rdata->orco, rdata->vert_len, 0); - } - else { - rdata->orco = MEM_mallocN(sizeof(*rdata->orco) * rdata->vert_len, "orco mesh"); - MVert *mvert = rdata->mvert; - for (int a = 0; a < rdata->vert_len; a++, mvert++) { - copy_v3_v3(rdata->orco[a], mvert->co); - } - BKE_mesh_orco_verts_transform(me, rdata->orco, rdata->vert_len, 0); - } - } - } - else { - rdata->orco = NULL; - } - - /* don't access mesh directly, instead use vars taken from BMesh or Mesh */ -#define me DONT_USE_THIS -#ifdef me /* quiet warning */ -#endif - struct { - uint uv_len; - uint vcol_len; - } cd_layers_src = { - .uv_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPUV), - .vcol_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPCOL), - }; - - rdata->cd.layers.uv_len = min_ii(cd_layers_src.uv_len, count_bits_i(cd_used->uv)); - rdata->cd.layers.tangent_len = count_bits_i(cd_used->tan) + cd_used->tan_orco; - rdata->cd.layers.vcol_len = min_ii(cd_layers_src.vcol_len, count_bits_i(cd_used->vcol)); - - rdata->cd.layers.uv = MEM_mallocN(sizeof(*rdata->cd.layers.uv) * rdata->cd.layers.uv_len, - __func__); - rdata->cd.layers.vcol = MEM_mallocN(sizeof(*rdata->cd.layers.vcol) * rdata->cd.layers.vcol_len, - __func__); - rdata->cd.layers.tangent = MEM_mallocN( - sizeof(*rdata->cd.layers.tangent) * rdata->cd.layers.tangent_len, __func__); - - rdata->cd.uuid.uv = MEM_mallocN(sizeof(*rdata->cd.uuid.uv) * rdata->cd.layers.uv_len, - __func__); - rdata->cd.uuid.vcol = MEM_mallocN(sizeof(*rdata->cd.uuid.vcol) * rdata->cd.layers.vcol_len, - __func__); - rdata->cd.uuid.tangent = MEM_mallocN( - sizeof(*rdata->cd.uuid.tangent) * rdata->cd.layers.tangent_len, __func__); - - rdata->cd.offset.uv = MEM_mallocN(sizeof(*rdata->cd.offset.uv) * rdata->cd.layers.uv_len, - __func__); - rdata->cd.offset.vcol = MEM_mallocN(sizeof(*rdata->cd.offset.vcol) * rdata->cd.layers.vcol_len, - __func__); - - /* Allocate max */ - rdata->cd.layers.auto_vcol = MEM_callocN( - sizeof(*rdata->cd.layers.auto_vcol) * rdata->cd.layers.vcol_len, __func__); - rdata->cd.uuid.auto_mix = MEM_mallocN( - sizeof(*rdata->cd.uuid.auto_mix) * (rdata->cd.layers.vcol_len + rdata->cd.layers.uv_len), - __func__); - - /* XXX FIXME XXX */ - /* We use a hash to identify each data layer based on its name. - * Gawain then search for this name in the current shader and bind if it exists. - * NOTE : This is prone to hash collision. - * One solution to hash collision would be to format the cd layer name - * to a safe glsl var name, but without name clash. - * NOTE 2 : Replicate changes to code_generate_vertex_new() in gpu_codegen.c */ - if (rdata->cd.layers.vcol_len != 0) { - int act_vcol = rdata->cd.layers.vcol_active; - int ren_vcol = rdata->cd.layers.vcol_render; - for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.vcol_len; i_src++, i_dst++) { - if ((cd_used->vcol & (1 << i_src)) == 0) { - /* This is a non-used VCol slot. Skip. */ - i_dst--; - if (rdata->cd.layers.vcol_active >= i_src) { - act_vcol--; - } - if (rdata->cd.layers.vcol_render >= i_src) { - ren_vcol--; - } - } - else { - const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i_src); - uint hash = BLI_ghashutil_strhash_p(name); - BLI_snprintf(rdata->cd.uuid.vcol[i_dst], sizeof(*rdata->cd.uuid.vcol), "c%u", hash); - rdata->cd.layers.vcol[i_dst] = CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i_src); - if (rdata->edit_bmesh) { - rdata->cd.offset.vcol[i_dst] = CustomData_get_n_offset( - &rdata->edit_bmesh->bm->ldata, CD_MLOOPCOL, i_src); - } - - /* Gather number of auto layers. */ - /* We only do vcols that are not overridden by uvs */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, name) == -1) { - BLI_snprintf(rdata->cd.uuid.auto_mix[rdata->cd.layers.uv_len + i_dst], - sizeof(*rdata->cd.uuid.auto_mix), - "a%u", - hash); - rdata->cd.layers.auto_vcol[i_dst] = true; - } - } - } - /* Actual active Vcol slot inside vcol layers used for shading. */ - if (rdata->cd.layers.vcol_active != -1) { - rdata->cd.layers.vcol_active = act_vcol; - } - if (rdata->cd.layers.vcol_render != -1) { - rdata->cd.layers.vcol_render = ren_vcol; - } - } - - /* Start Fresh */ - CustomData_free_layers(cd_ldata, CD_TANGENT, rdata->loop_len); - CustomData_free_layers(cd_ldata, CD_MLOOPTANGENT, rdata->loop_len); - - if (rdata->cd.layers.uv_len != 0) { - int ren_uv = rdata->cd.layers.uv_render; - int act_uv = rdata->cd.layers.uv_active; - for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) { - if ((cd_used->uv & (1 << i_src)) == 0) { - /* This is a non-used UV slot. Skip. */ - i_dst--; - if (rdata->cd.layers.uv_render >= i_src) { - ren_uv--; - } - if (rdata->cd.layers.uv_active >= i_src) { - act_uv--; - } - } - else { - const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src); - uint hash = BLI_ghashutil_strhash_p(name); - - BLI_snprintf(rdata->cd.uuid.uv[i_dst], sizeof(*rdata->cd.uuid.uv), "u%u", hash); - rdata->cd.layers.uv[i_dst] = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i_src); - if (rdata->edit_bmesh) { - rdata->cd.offset.uv[i_dst] = CustomData_get_n_offset( - &rdata->edit_bmesh->bm->ldata, CD_MLOOPUV, i_src); - } - BLI_snprintf( - rdata->cd.uuid.auto_mix[i_dst], sizeof(*rdata->cd.uuid.auto_mix), "a%u", hash); - } - } - /* Actual active / Render UV slot inside uv layers used for shading. */ - if (rdata->cd.layers.uv_render != -1) { - rdata->cd.layers.uv_render = ren_uv; - } - if (rdata->cd.layers.uv_active != -1) { - rdata->cd.layers.uv_active = act_uv; - } - } - - if (rdata->cd.layers.tangent_len != 0) { - - /* -------------------------------------------------------------------- */ - /* Pre-calculate tangents into 'rdata->cd.output.ldata' */ - - BLI_assert(!CustomData_has_layer(&rdata->cd.output.ldata, CD_TANGENT)); - - /* Tangent Names */ - char tangent_names[MAX_MTFACE][MAX_NAME]; - for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) { - if ((cd_used->tan & (1 << i_src)) == 0) { - i_dst--; - } - else { - BLI_strncpy(tangent_names[i_dst], - CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src), - MAX_NAME); - } - } - - /* If tangent from orco is requested, decrement tangent_len */ - int actual_tangent_len = (cd_used->tan_orco != 0) ? rdata->cd.layers.tangent_len - 1 : - rdata->cd.layers.tangent_len; - if (rdata->edit_bmesh) { - BMEditMesh *em = rdata->edit_bmesh; - BMesh *bm = em->bm; - - if (is_auto_smooth && rdata->loop_normals == NULL) { - /* Should we store the previous array of `loop_normals` in somewhere? */ - rdata->loop_len = bm->totloop; - rdata->loop_normals = MEM_mallocN(sizeof(*rdata->loop_normals) * rdata->loop_len, - __func__); - BM_loops_calc_normal_vcos( - bm, NULL, NULL, NULL, true, split_angle, rdata->loop_normals, NULL, NULL, -1, false); - } - - bool calc_active_tangent = false; - - BKE_editmesh_loop_tangent_calc(em, - calc_active_tangent, - tangent_names, - actual_tangent_len, - rdata->poly_normals, - rdata->loop_normals, - rdata->orco, - &rdata->cd.output.ldata, - bm->totloop, - &rdata->cd.output.tangent_mask); - } - else { -#undef me - - if (is_auto_smooth && rdata->loop_normals == NULL) { - /* Should we store the previous array of `loop_normals` in CustomData? */ - mesh_render_calc_normals_loop_and_poly(me, split_angle, rdata); - } - - bool calc_active_tangent = false; - - BKE_mesh_calc_loop_tangent_ex(me->mvert, - me->mpoly, - me->totpoly, - me->mloop, - rdata->mlooptri, - rdata->tri_len, - cd_ldata, - calc_active_tangent, - tangent_names, - actual_tangent_len, - rdata->poly_normals, - rdata->loop_normals, - rdata->orco, - &rdata->cd.output.ldata, - me->totloop, - &rdata->cd.output.tangent_mask); - - /* If we store tangents in the mesh, set temporary. */ -#if 0 - CustomData_set_layer_flag(cd_ldata, CD_TANGENT, CD_FLAG_TEMPORARY); -#endif - -#define me DONT_USE_THIS -#ifdef me /* quiet warning */ -#endif - } - - /* End tangent calculation */ - /* -------------------------------------------------------------------- */ - - BLI_assert(CustomData_number_of_layers(&rdata->cd.output.ldata, CD_TANGENT) == - rdata->cd.layers.tangent_len); - - int i_dst = 0; - int act_tan = rdata->cd.layers.tangent_active; - int ren_tan = rdata->cd.layers.tangent_render; - for (int i_src = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) { - if ((cd_used->tan & (1 << i_src)) == 0) { - i_dst--; - if (rdata->cd.layers.tangent_render >= i_src) { - ren_tan--; - } - if (rdata->cd.layers.tangent_active >= i_src) { - act_tan--; - } - } - else { - const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src); - uint hash = BLI_ghashutil_strhash_p(name); - - BLI_snprintf( - rdata->cd.uuid.tangent[i_dst], sizeof(*rdata->cd.uuid.tangent), "t%u", hash); - - /* Done adding tangents. */ - - /* note: BKE_editmesh_loop_tangent_calc calculates 'CD_TANGENT', - * not 'CD_MLOOPTANGENT' (as done below). It's OK, they're compatible. */ - - /* note: normally we'd use 'i_src' here, but 'i_dst' is in sync with 'rdata->cd.output' - */ - rdata->cd.layers.tangent[i_dst] = CustomData_get_layer_n( - &rdata->cd.output.ldata, CD_TANGENT, i_dst); - if (rdata->tri_len != 0) { - BLI_assert(rdata->cd.layers.tangent[i_dst] != NULL); - } - } - } - /* Actual active rangent slot inside uv layers used for shading. */ - if (rdata->cd.layers.tangent_active != -1) { - rdata->cd.layers.tangent_active = act_tan; - } - if (rdata->cd.layers.tangent_render != -1) { - rdata->cd.layers.tangent_render = ren_tan; - } - - if (cd_used->tan_orco != 0) { - const char *name = CustomData_get_layer_name(&rdata->cd.output.ldata, CD_TANGENT, i_dst); - uint hash = BLI_ghashutil_strhash_p(name); - BLI_snprintf(rdata->cd.uuid.tangent[i_dst], sizeof(*rdata->cd.uuid.tangent), "t%u", hash); - - rdata->cd.layers.tangent[i_dst] = CustomData_get_layer_n( - &rdata->cd.output.ldata, CD_TANGENT, i_dst); - } - } - -#undef me - } - - return rdata; -} - -/* Warning replace mesh pointer. */ -#define MBC_GET_FINAL_MESH(me) \ - /* Hack to show the final result. */ \ - const bool _use_em_final = ((me)->edit_mesh && (me)->edit_mesh->mesh_eval_final && \ - ((me)->edit_mesh->mesh_eval_final->runtime.is_original == false)); \ - Mesh _me_fake; \ - if (_use_em_final) { \ - _me_fake = *(me)->edit_mesh->mesh_eval_final; \ - _me_fake.mat = (me)->mat; \ - _me_fake.totcol = (me)->totcol; \ - (me) = &_me_fake; \ - } \ - ((void)0) - -static void mesh_render_data_free(MeshRenderData *rdata) -{ - if (rdata->is_orco_allocated) { - MEM_SAFE_FREE(rdata->orco); - } - MEM_SAFE_FREE(rdata->cd.offset.uv); - MEM_SAFE_FREE(rdata->cd.offset.vcol); - MEM_SAFE_FREE(rdata->cd.uuid.auto_mix); - MEM_SAFE_FREE(rdata->cd.uuid.uv); - MEM_SAFE_FREE(rdata->cd.uuid.vcol); - MEM_SAFE_FREE(rdata->cd.uuid.tangent); - MEM_SAFE_FREE(rdata->cd.layers.uv); - MEM_SAFE_FREE(rdata->cd.layers.vcol); - MEM_SAFE_FREE(rdata->cd.layers.tangent); - MEM_SAFE_FREE(rdata->cd.layers.auto_vcol); - MEM_SAFE_FREE(rdata->loose_verts); - MEM_SAFE_FREE(rdata->loose_edges); - MEM_SAFE_FREE(rdata->edges_adjacent_polys); - MEM_SAFE_FREE(rdata->mlooptri); - MEM_SAFE_FREE(rdata->loop_normals); - MEM_SAFE_FREE(rdata->poly_normals); - MEM_SAFE_FREE(rdata->poly_normals_pack); - MEM_SAFE_FREE(rdata->vert_normals_pack); - MEM_SAFE_FREE(rdata->vert_weight); - MEM_SAFE_FREE(rdata->edge_select_bool); - MEM_SAFE_FREE(rdata->edge_visible_bool); - MEM_SAFE_FREE(rdata->vert_color); - - MEM_SAFE_FREE(rdata->mapped.loose_verts); - MEM_SAFE_FREE(rdata->mapped.loose_edges); - - CustomData_free(&rdata->cd.output.ldata, rdata->loop_len); - - MEM_freeN(rdata); -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Accessor Functions - * \{ */ - -static const char *mesh_render_data_uv_auto_layer_uuid_get(const MeshRenderData *rdata, int layer) -{ - BLI_assert(rdata->types & MR_DATATYPE_SHADING); - return rdata->cd.uuid.auto_mix[layer]; -} - -static const char *mesh_render_data_vcol_auto_layer_uuid_get(const MeshRenderData *rdata, - int layer) -{ - BLI_assert(rdata->types & MR_DATATYPE_SHADING); - return rdata->cd.uuid.auto_mix[rdata->cd.layers.uv_len + layer]; -} - -static const char *mesh_render_data_uv_layer_uuid_get(const MeshRenderData *rdata, int layer) -{ - BLI_assert(rdata->types & MR_DATATYPE_SHADING); - return rdata->cd.uuid.uv[layer]; -} - -static const char *mesh_render_data_vcol_layer_uuid_get(const MeshRenderData *rdata, int layer) -{ - BLI_assert(rdata->types & MR_DATATYPE_SHADING); - return rdata->cd.uuid.vcol[layer]; -} - -static const char *mesh_render_data_tangent_layer_uuid_get(const MeshRenderData *rdata, int layer) -{ - BLI_assert(rdata->types & MR_DATATYPE_SHADING); - return rdata->cd.uuid.tangent[layer]; -} - -static int UNUSED_FUNCTION(mesh_render_data_verts_len_get)(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_VERT); - return rdata->vert_len; -} -static int mesh_render_data_verts_len_get_maybe_mapped(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_VERT); - return ((rdata->mapped.use == false) ? rdata->vert_len : rdata->mapped.vert_len); -} - -static int UNUSED_FUNCTION(mesh_render_data_loose_verts_len_get)(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_LOOSE_VERT); - return rdata->loose_vert_len; -} -static int mesh_render_data_loose_verts_len_get_maybe_mapped(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_LOOSE_VERT); - return ((rdata->mapped.use == false) ? rdata->loose_vert_len : rdata->mapped.loose_vert_len); -} - -static int mesh_render_data_edges_len_get(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_EDGE); - return rdata->edge_len; -} -static int mesh_render_data_edges_len_get_maybe_mapped(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_EDGE); - return ((rdata->mapped.use == false) ? rdata->edge_len : rdata->mapped.edge_len); -} - -static int UNUSED_FUNCTION(mesh_render_data_loose_edges_len_get)(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_LOOSE_EDGE); - return rdata->loose_edge_len; -} -static int mesh_render_data_loose_edges_len_get_maybe_mapped(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_LOOSE_EDGE); - return ((rdata->mapped.use == false) ? rdata->loose_edge_len : rdata->mapped.loose_edge_len); -} - -static int mesh_render_data_looptri_len_get(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_LOOPTRI); - return rdata->tri_len; -} -static int mesh_render_data_looptri_len_get_maybe_mapped(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_LOOPTRI); - return ((rdata->mapped.use == false) ? rdata->tri_len : rdata->mapped.tri_len); -} - -static int UNUSED_FUNCTION(mesh_render_data_mat_len_get)(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_POLY); - return rdata->mat_len; -} - -static int mesh_render_data_loops_len_get(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_LOOP); - return rdata->loop_len; -} - -static int mesh_render_data_loops_len_get_maybe_mapped(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_LOOP); - return ((rdata->mapped.use == false) ? rdata->loop_len : rdata->mapped.loop_len); -} - -static int mesh_render_data_polys_len_get(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_POLY); - return rdata->poly_len; -} -static int mesh_render_data_polys_len_get_maybe_mapped(const MeshRenderData *rdata) -{ - BLI_assert(rdata->types & MR_DATATYPE_POLY); - return ((rdata->mapped.use == false) ? rdata->poly_len : rdata->mapped.poly_len); -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ - -/** \name Internal Cache (Lazy Initialization) - * \{ */ - -/** Ensure #MeshRenderData.poly_normals_pack */ -static void mesh_render_data_ensure_poly_normals_pack(MeshRenderData *rdata) -{ - GPUPackedNormal *pnors_pack = rdata->poly_normals_pack; - if (pnors_pack == NULL) { - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter fiter; - BMFace *efa; - int i; - - pnors_pack = rdata->poly_normals_pack = MEM_mallocN(sizeof(*pnors_pack) * rdata->poly_len, - __func__); - if (rdata->edit_data && rdata->edit_data->vertexCos != NULL) { - BKE_editmesh_cache_ensure_poly_normals(rdata->edit_bmesh, rdata->edit_data); - const float(*pnors)[3] = rdata->edit_data->polyNos; - for (i = 0; i < bm->totface; i++) { - pnors_pack[i] = GPU_normal_convert_i10_v3(pnors[i]); - } - } - else { - BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) { - pnors_pack[i] = GPU_normal_convert_i10_v3(efa->no); - } - } - } - else { - float(*pnors)[3] = rdata->poly_normals; - - if (!pnors) { - pnors = rdata->poly_normals = MEM_mallocN(sizeof(*pnors) * rdata->poly_len, __func__); - BKE_mesh_calc_normals_poly(rdata->mvert, - NULL, - rdata->vert_len, - rdata->mloop, - rdata->mpoly, - rdata->loop_len, - rdata->poly_len, - pnors, - true); - } - - pnors_pack = rdata->poly_normals_pack = MEM_mallocN(sizeof(*pnors_pack) * rdata->poly_len, - __func__); - for (int i = 0; i < rdata->poly_len; i++) { - pnors_pack[i] = GPU_normal_convert_i10_v3(pnors[i]); - } - } - } -} - -/** Ensure #MeshRenderData.vert_normals_pack */ -static void mesh_render_data_ensure_vert_normals_pack(MeshRenderData *rdata) -{ - GPUPackedNormal *vnors_pack = rdata->vert_normals_pack; - if (vnors_pack == NULL) { - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter viter; - BMVert *eve; - int i; - - vnors_pack = rdata->vert_normals_pack = MEM_mallocN(sizeof(*vnors_pack) * rdata->vert_len, - __func__); - BM_ITER_MESH_INDEX (eve, &viter, bm, BM_VERT, i) { - vnors_pack[i] = GPU_normal_convert_i10_v3(eve->no); - } - } - else { - /* data from mesh used directly */ - BLI_assert(0); - } - } -} - -/** Ensure #MeshRenderData.vert_color */ -static void UNUSED_FUNCTION(mesh_render_data_ensure_vert_color)(MeshRenderData *rdata) -{ - char(*vcol)[3] = rdata->vert_color; - if (vcol == NULL) { - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - const int cd_loop_color_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPCOL); - if (cd_loop_color_offset == -1) { - goto fallback; - } - - vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__); - - BMIter fiter; - BMFace *efa; - int i = 0; - - BM_ITER_MESH (efa, &fiter, bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MLoopCol *lcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_color_offset); - vcol[i][0] = lcol->r; - vcol[i][1] = lcol->g; - vcol[i][2] = lcol->b; - i += 1; - } while ((l_iter = l_iter->next) != l_first); - } - BLI_assert(i == rdata->loop_len); - } - else { - if (rdata->mloopcol == NULL) { - goto fallback; - } - - vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__); - - for (int i = 0; i < rdata->loop_len; i++) { - vcol[i][0] = rdata->mloopcol[i].r; - vcol[i][1] = rdata->mloopcol[i].g; - vcol[i][2] = rdata->mloopcol[i].b; - } - } - } - return; - -fallback: - vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__); - - for (int i = 0; i < rdata->loop_len; i++) { - vcol[i][0] = 255; - vcol[i][1] = 255; - vcol[i][2] = 255; - } -} - -static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate) -{ - float input = 0.0f; - bool show_alert_color = false; - - if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) { - /* Multi-Paint feature */ - input = BKE_defvert_multipaint_collective_weight( - dvert, - wstate->defgroup_len, - wstate->defgroup_sel, - wstate->defgroup_sel_count, - (wstate->flags & DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE) != 0); - - /* make it black if the selected groups have no weight on a vertex */ - if (input == 0.0f) { - show_alert_color = true; - } - } - else { - /* default, non tricky behavior */ - input = defvert_find_weight(dvert, wstate->defgroup_active); - - if (input == 0.0f) { - switch (wstate->alert_mode) { - case OB_DRAW_GROUPUSER_ACTIVE: - show_alert_color = true; - break; - - case OB_DRAW_GROUPUSER_ALL: - show_alert_color = defvert_is_weight_zero(dvert, wstate->defgroup_len); - break; - } - } - } - - if (show_alert_color) { - return -1.0f; - } - else { - CLAMP(input, 0.0f, 1.0f); - return input; - } -} - -/** Ensure #MeshRenderData.vert_weight */ -static void mesh_render_data_ensure_vert_weight(MeshRenderData *rdata, - const struct DRW_MeshWeightState *wstate) -{ - float *vweight = rdata->vert_weight; - if (vweight == NULL) { - if (wstate->defgroup_active == -1) { - goto fallback; - } - - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); - if (cd_dvert_offset == -1) { - goto fallback; - } - - BMIter viter; - BMVert *eve; - int i; - - vweight = rdata->vert_weight = MEM_mallocN(sizeof(*vweight) * rdata->vert_len, __func__); - BM_ITER_MESH_INDEX (eve, &viter, bm, BM_VERT, i) { - const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); - vweight[i] = evaluate_vertex_weight(dvert, wstate); - } - } - else { - if (rdata->dvert == NULL) { - goto fallback; - } - - vweight = rdata->vert_weight = MEM_mallocN(sizeof(*vweight) * rdata->vert_len, __func__); - for (int i = 0; i < rdata->vert_len; i++) { - vweight[i] = evaluate_vertex_weight(&rdata->dvert[i], wstate); - } - } - } - return; - -fallback: - vweight = rdata->vert_weight = MEM_callocN(sizeof(*vweight) * rdata->vert_len, __func__); - - if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) { - copy_vn_fl(vweight, rdata->vert_len, -2.0f); - } - else if (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) { - copy_vn_fl(vweight, rdata->vert_len, -1.0f); - } -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Internal Cache Generation - * \{ */ - -static uchar mesh_render_data_face_flag(MeshRenderData *rdata, const BMFace *efa, const int cd_ofs) -{ - uchar fflag = 0; - - if (efa == rdata->efa_act) { - fflag |= VFLAG_FACE_ACTIVE; - } - if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - fflag |= VFLAG_FACE_SELECTED; - } - - if (efa == rdata->efa_act_uv) { - fflag |= VFLAG_FACE_UV_ACTIVE; - } - if ((cd_ofs != -1) && uvedit_face_select_test_ex(rdata->toolsettings, (BMFace *)efa, cd_ofs)) { - fflag |= VFLAG_FACE_UV_SELECT; - } - -#ifdef WITH_FREESTYLE - if (rdata->cd.offset.freestyle_face != -1) { - const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, rdata->cd.offset.freestyle_face); - if (ffa->flag & FREESTYLE_FACE_MARK) { - fflag |= VFLAG_FACE_FREESTYLE; - } - } -#endif - - return fflag; -} - -static void mesh_render_data_edge_flag(const MeshRenderData *rdata, - const BMEdge *eed, - EdgeDrawAttr *eattr) -{ - const ToolSettings *ts = rdata->toolsettings; - const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0; - const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE); - - if (eed == rdata->eed_act) { - eattr->e_flag |= VFLAG_EDGE_ACTIVE; - } - if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_EDGE_SELECTED; - } - if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) && - BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_EDGE_SELECTED; - eattr->e_flag |= VFLAG_VERT_SELECTED; - } - if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) { - eattr->e_flag |= VFLAG_EDGE_SEAM; - } - if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) { - eattr->e_flag |= VFLAG_EDGE_SHARP; - } - - /* Use active edge color for active face edges because - * specular highlights make it hard to see T55456#510873. - * - * This isn't ideal since it can't be used when mixing edge/face modes - * but it's still better then not being able to see the active face. */ - if (is_face_only_select_mode) { - if (rdata->efa_act != NULL) { - if (BM_edge_in_face(eed, rdata->efa_act)) { - eattr->e_flag |= VFLAG_EDGE_ACTIVE; - } - } - } - - /* Use a byte for value range */ - if (rdata->cd.offset.crease != -1) { - float crease = BM_ELEM_CD_GET_FLOAT(eed, rdata->cd.offset.crease); - if (crease > 0) { - eattr->crease = (uchar)(crease * 255.0f); - } - } - /* Use a byte for value range */ - if (rdata->cd.offset.bweight != -1) { - float bweight = BM_ELEM_CD_GET_FLOAT(eed, rdata->cd.offset.bweight); - if (bweight > 0) { - eattr->bweight = (uchar)(bweight * 255.0f); - } - } -#ifdef WITH_FREESTYLE - if (rdata->cd.offset.freestyle_edge != -1) { - const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, rdata->cd.offset.freestyle_edge); - if (fed->flag & FREESTYLE_EDGE_MARK) { - eattr->e_flag |= VFLAG_EDGE_FREESTYLE; - } - } -#endif -} - -static void mesh_render_data_loop_flag(MeshRenderData *rdata, - BMLoop *loop, - const int cd_ofs, - EdgeDrawAttr *eattr) -{ - if (cd_ofs == -1) { - return; - } - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs); - if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) { - eattr->v_flag |= VFLAG_VERT_UV_PINNED; - } - if (uvedit_uv_select_test_ex(rdata->toolsettings, loop, cd_ofs)) { - eattr->v_flag |= VFLAG_VERT_UV_SELECT; - } - if (uvedit_edge_select_test_ex(rdata->toolsettings, loop, cd_ofs)) { - eattr->v_flag |= VFLAG_EDGE_UV_SELECT; - } -} - -static void mesh_render_data_vert_flag(MeshRenderData *rdata, - const BMVert *eve, - EdgeDrawAttr *eattr) -{ - if (eve == rdata->eve_act) { - eattr->e_flag |= VFLAG_VERT_ACTIVE; - } - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_VERT_SELECTED; - } -} - -static bool add_edit_facedot(MeshRenderData *rdata, - GPUVertBuf *vbo, - const uint fdot_pos_id, - const uint fdot_nor_flag_id, - const int poly, - const int base_vert_idx) -{ - BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY)); - float pnor[3], center[3]; - int facedot_flag; - if (rdata->edit_bmesh) { - BMEditMesh *em = rdata->edit_bmesh; - const BMFace *efa = BM_face_at_index(em->bm, poly); - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - return false; - } - if (rdata->edit_data && rdata->edit_data->vertexCos) { - copy_v3_v3(center, rdata->edit_data->polyCos[poly]); - copy_v3_v3(pnor, rdata->edit_data->polyNos[poly]); - } - else { - BM_face_calc_center_median(efa, center); - copy_v3_v3(pnor, efa->no); - } - facedot_flag = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == em->bm->act_face) ? -1 : 1) : - 0; - } - else { - MVert *mvert = rdata->mvert; - const MPoly *mpoly = rdata->mpoly + poly; - const MLoop *mloop = rdata->mloop + mpoly->loopstart; - - BKE_mesh_calc_poly_center(mpoly, mloop, mvert, center); - BKE_mesh_calc_poly_normal(mpoly, mloop, mvert, pnor); - /* No selection if not in edit mode. */ - facedot_flag = 0; - } - - GPUPackedNormal nor = GPU_normal_convert_i10_v3(pnor); - nor.w = facedot_flag; - GPU_vertbuf_attr_set(vbo, fdot_nor_flag_id, base_vert_idx, &nor); - GPU_vertbuf_attr_set(vbo, fdot_pos_id, base_vert_idx, center); - - return true; -} -static bool add_edit_facedot_mapped(MeshRenderData *rdata, - GPUVertBuf *vbo, - const uint fdot_pos_id, - const uint fdot_nor_flag_id, - const int poly, - const int base_vert_idx) -{ - BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY)); - float pnor[3], center[3]; - const int *p_origindex = rdata->mapped.p_origindex; - const int p_orig = p_origindex[poly]; - if (p_orig == ORIGINDEX_NONE) { - return false; - } - BMEditMesh *em = rdata->edit_bmesh; - const BMFace *efa = BM_face_at_index(em->bm, p_orig); - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - return false; - } - - Mesh *me_cage = em->mesh_eval_cage; - const MVert *mvert = me_cage->mvert; - const MLoop *mloop = me_cage->mloop; - const MPoly *mpoly = me_cage->mpoly; - - const MPoly *mp = mpoly + poly; - const MLoop *ml = mloop + mp->loopstart; - - BKE_mesh_calc_poly_center(mp, ml, mvert, center); - BKE_mesh_calc_poly_normal(mp, ml, mvert, pnor); - - GPUPackedNormal nor = GPU_normal_convert_i10_v3(pnor); - nor.w = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == em->bm->act_face) ? -1 : 1) : 0; - GPU_vertbuf_attr_set(vbo, fdot_nor_flag_id, base_vert_idx, &nor); - GPU_vertbuf_attr_set(vbo, fdot_pos_id, base_vert_idx, center); - - return true; -} -static bool add_edit_facedot_subdiv(MeshRenderData *rdata, - GPUVertBuf *vbo, - const uint fdot_pos_id, - const uint fdot_nor_flag_id, - const int vert, - const int poly, - const int base_vert_idx) -{ - BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY)); - const int *p_origindex = rdata->mapped.p_origindex; - const int p_orig = p_origindex[poly]; - if (p_orig == ORIGINDEX_NONE) { - return false; - } - BMEditMesh *em = rdata->edit_bmesh; - const BMFace *efa = BM_face_at_index(em->bm, p_orig); - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - return false; - } - - Mesh *me_cage = em->mesh_eval_cage; - const MVert *mvert = &me_cage->mvert[vert]; - - GPUPackedNormal nor = GPU_normal_convert_i10_s3(mvert->no); - nor.w = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == em->bm->act_face) ? -1 : 1) : 0; - GPU_vertbuf_attr_set(vbo, fdot_nor_flag_id, base_vert_idx, &nor); - GPU_vertbuf_attr_set(vbo, fdot_pos_id, base_vert_idx, mvert->co); - - return true; -} - /** \} */ /* ---------------------------------------------------------------------- */ @@ -1963,170 +370,6 @@ static void drw_mesh_weight_state_extract(Object *ob, /** \name Mesh GPUBatch Cache * \{ */ -typedef enum DRWBatchFlag { - MBC_SURFACE = (1 << 0), - MBC_SURFACE_WEIGHTS = (1 << 1), - MBC_EDIT_TRIANGLES = (1 << 2), - MBC_EDIT_VERTICES = (1 << 3), - MBC_EDIT_EDGES = (1 << 4), - MBC_EDIT_LNOR = (1 << 5), - MBC_EDIT_FACEDOTS = (1 << 6), - MBC_EDIT_MESH_ANALYSIS = (1 << 7), - MBC_EDITUV_FACES_STRECH_AREA = (1 << 8), - MBC_EDITUV_FACES_STRECH_ANGLE = (1 << 9), - MBC_EDITUV_FACES = (1 << 10), - MBC_EDITUV_EDGES = (1 << 11), - MBC_EDITUV_VERTS = (1 << 12), - MBC_EDITUV_FACEDOTS = (1 << 13), - MBC_EDIT_SELECTION_VERTS = (1 << 14), - MBC_EDIT_SELECTION_EDGES = (1 << 15), - MBC_EDIT_SELECTION_FACES = (1 << 16), - MBC_EDIT_SELECTION_FACEDOTS = (1 << 17), - MBC_ALL_VERTS = (1 << 18), - MBC_ALL_EDGES = (1 << 19), - MBC_LOOSE_EDGES = (1 << 20), - MBC_EDGE_DETECTION = (1 << 21), - MBC_WIRE_EDGES = (1 << 22), - MBC_WIRE_LOOPS = (1 << 23), - MBC_WIRE_LOOPS_UVS = (1 << 24), - MBC_SURF_PER_MAT = (1 << 25), -} DRWBatchFlag; - -#define MBC_EDITUV \ - (MBC_EDITUV_FACES_STRECH_AREA | MBC_EDITUV_FACES_STRECH_ANGLE | MBC_EDITUV_FACES | \ - MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS) - -typedef struct MeshBatchCache { - /* In order buffers: All verts only specified once - * or once per loop. To be used with a GPUIndexBuf. */ - struct { - /* Vertex data. */ - GPUVertBuf *pos_nor; - GPUVertBuf *weights; - /* Loop data. */ - GPUVertBuf *loop_pos_nor; - GPUVertBuf *loop_uv_tan; - GPUVertBuf *loop_vcol; - GPUVertBuf *loop_edge_fac; - GPUVertBuf *loop_orco; - } ordered; - - /* Edit Mesh Data: - * Edit cage can be different from final mesh so vertex count - * might differ. */ - struct { - /* TODO(fclem): Reuse ordered.loop_pos_nor and maybe even - * ordered.loop_uv_tan when cage match final mesh. */ - GPUVertBuf *loop_pos_nor; - GPUVertBuf *loop_data; - GPUVertBuf *loop_lnor; - GPUVertBuf *facedots_pos_nor_data; - GPUVertBuf *loop_mesh_analysis; - /* UV data without modifier applied. - * Vertex count is always the one of the cage. */ - GPUVertBuf *loop_uv; - GPUVertBuf *loop_uv_data; - GPUVertBuf *loop_stretch_angle; - GPUVertBuf *loop_stretch_area; - GPUVertBuf *facedots_uv; - GPUVertBuf *facedots_uv_data; - /* Selection */ - GPUVertBuf *loop_vert_idx; - GPUVertBuf *loop_edge_idx; - GPUVertBuf *loop_face_idx; - GPUVertBuf *facedots_idx; - } edit; - - /* Index Buffers: - * Only need to be updated when topology changes. */ - struct { - /* Indices to verts. */ - GPUIndexBuf *surf_tris; - GPUIndexBuf *edges_lines; - GPUIndexBuf *edges_adj_lines; - GPUIndexBuf *loose_edges_lines; - /* Indices to vloops. */ - GPUIndexBuf *loops_tris; - GPUIndexBuf *loops_lines; - GPUIndexBuf *loops_lines_paint_mask; - GPUIndexBuf *loops_line_strips; - /* Edit mode. */ - GPUIndexBuf *edit_loops_points; /* verts */ - GPUIndexBuf *edit_loops_lines; /* edges */ - GPUIndexBuf *edit_loops_tris; /* faces */ - /* Edit UVs */ - GPUIndexBuf *edituv_loops_points; /* verts */ - GPUIndexBuf *edituv_loops_line_strips; /* edges */ - GPUIndexBuf *edituv_loops_tri_fans; /* faces */ - } ibo; - - struct { - /* Surfaces / Render */ - GPUBatch *surface; - GPUBatch *surface_weights; - /* Edit mode */ - GPUBatch *edit_triangles; - GPUBatch *edit_vertices; - GPUBatch *edit_edges; - GPUBatch *edit_lnor; - GPUBatch *edit_facedots; - GPUBatch *edit_mesh_analysis; - /* Edit UVs */ - GPUBatch *edituv_faces_strech_area; - GPUBatch *edituv_faces_strech_angle; - GPUBatch *edituv_faces; - GPUBatch *edituv_edges; - GPUBatch *edituv_verts; - GPUBatch *edituv_facedots; - /* Edit selection */ - GPUBatch *edit_selection_verts; - GPUBatch *edit_selection_edges; - GPUBatch *edit_selection_faces; - GPUBatch *edit_selection_facedots; - /* Common display / Other */ - GPUBatch *all_verts; - GPUBatch *all_edges; - GPUBatch *loose_edges; - GPUBatch *edge_detection; - GPUBatch *wire_edges; /* Individual edges with face normals. */ - GPUBatch *wire_loops; /* Loops around faces. no edges between selected faces */ - GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */ - } batch; - - GPUIndexBuf **surf_per_mat_tris; - GPUBatch **surf_per_mat; - - /* arrays of bool uniform names (and value) that will be use to - * set srgb conversion for auto attributes.*/ - char *auto_layer_names; - int *auto_layer_is_srgb; - int auto_layer_len; - - DRWBatchFlag batch_requested; - DRWBatchFlag batch_ready; - - /* settings to determine if cache is invalid */ - int edge_len; - int tri_len; - int poly_len; - int vert_len; - int mat_len; - bool is_dirty; /* Instantly invalidates cache, skipping mesh check */ - bool is_editmode; - bool is_uvsyncsel; - - struct DRW_MeshWeightState weight_state; - - DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; - - int lastmatch; - - /* Valid only if edge_detection is up to date. */ - bool is_manifold; - - bool no_loose_wire; -} MeshBatchCache; - BLI_INLINE void mesh_batch_cache_add_request(MeshBatchCache *cache, DRWBatchFlag new_flag) { atomic_fetch_and_or_uint32((uint32_t *)(&cache->batch_requested), *(uint32_t *)&new_flag); @@ -2171,16 +414,14 @@ static void mesh_batch_cache_init(Mesh *me) cache->is_editmode = me->edit_mesh != NULL; if (cache->is_editmode == false) { - cache->edge_len = mesh_render_edges_len_get(me); - cache->tri_len = mesh_render_looptri_len_get(me); - cache->poly_len = mesh_render_polys_len_get(me); - cache->vert_len = mesh_render_verts_len_get(me); + // cache->edge_len = mesh_render_edges_len_get(me); + // cache->tri_len = mesh_render_looptri_len_get(me); + // cache->poly_len = mesh_render_polys_len_get(me); + // cache->vert_len = mesh_render_verts_len_get(me); } cache->mat_len = mesh_render_mat_len_get(me); - cache->surf_per_mat_tris = MEM_callocN(sizeof(*cache->surf_per_mat_tris) * cache->mat_len, - __func__); - cache->surf_per_mat = MEM_callocN(sizeof(*cache->surf_per_mat) * cache->mat_len, __func__); + cache->surface_per_mat = MEM_callocN(sizeof(*cache->surface_per_mat) * cache->mat_len, __func__); cache->is_dirty = false; cache->batch_ready = 0; @@ -2206,8 +447,11 @@ static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache, const struct DRW_MeshWeightState *wstate) { if (!drw_mesh_weight_state_compare(&cache->weight_state, wstate)) { + FOREACH_MESH_BUFFER_CACHE(cache, mbufcache) + { + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.weights); + } GPU_BATCH_CLEAR_SAFE(cache->batch.surface_weights); - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.weights); cache->batch_ready &= ~MBC_SURFACE_WEIGHTS; @@ -2217,23 +461,21 @@ static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache, static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache) { - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_pos_nor); - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_uv_tan); - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_vcol); - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_orco); - - if (cache->surf_per_mat_tris) { - for (int i = 0; i < cache->mat_len; i++) { - GPU_INDEXBUF_DISCARD_SAFE(cache->surf_per_mat_tris[i]); - } + FOREACH_MESH_BUFFER_CACHE(cache, mbufcache) + { + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.tan); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.orco); } - MEM_SAFE_FREE(cache->surf_per_mat_tris); - if (cache->surf_per_mat) { + + if (cache->surface_per_mat) { for (int i = 0; i < cache->mat_len; i++) { - GPU_BATCH_DISCARD_SAFE(cache->surf_per_mat[i]); + GPU_BATCH_DISCARD_SAFE(cache->surface_per_mat[i]); } } - MEM_SAFE_FREE(cache->surf_per_mat); + MEM_SAFE_FREE(cache->surface_per_mat); cache->batch_ready &= ~MBC_SURF_PER_MAT; @@ -2247,21 +489,26 @@ static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache) static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache) { - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_stretch_angle); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_stretch_area); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_uv); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_uv_data); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_uv); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_uv_data); - GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_tri_fans); - GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_line_strips); - GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_points); + FOREACH_MESH_BUFFER_CACHE(cache, mbufcache) + { + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.stretch_angle); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.stretch_area); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_uv); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data); + GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_tris); + GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_lines); + GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_points); + GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_fdots); + } GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_area); GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_angle); GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces); GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges); GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_facedots); + GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_fdots); + GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops_uvs); cache->batch_ready &= ~MBC_EDITUV; } @@ -2274,31 +521,41 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, int mode) } switch (mode) { case BKE_MESH_BATCH_DIRTY_SELECT: - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_data); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_pos_nor_data); + FOREACH_MESH_BUFFER_CACHE(cache, mbufcache) + { + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edit_data); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_nor); + } GPU_BATCH_DISCARD_SAFE(cache->batch.edit_triangles); GPU_BATCH_DISCARD_SAFE(cache->batch.edit_vertices); GPU_BATCH_DISCARD_SAFE(cache->batch.edit_edges); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_facedots); - GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_facedots); + GPU_BATCH_DISCARD_SAFE(cache->batch.edit_fdots); + GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_verts); + GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_edges); + GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_faces); + GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_fdots); GPU_BATCH_DISCARD_SAFE(cache->batch.edit_mesh_analysis); cache->batch_ready &= ~(MBC_EDIT_TRIANGLES | MBC_EDIT_VERTICES | MBC_EDIT_EDGES | MBC_EDIT_FACEDOTS | MBC_EDIT_SELECTION_FACEDOTS | - MBC_EDIT_MESH_ANALYSIS); + MBC_EDIT_SELECTION_FACES | MBC_EDIT_SELECTION_EDGES | + MBC_EDIT_SELECTION_VERTS | MBC_EDIT_MESH_ANALYSIS); /* Because visible UVs depends on edit mode selection, discard everything. */ mesh_batch_cache_discard_uvedit(cache); break; case BKE_MESH_BATCH_DIRTY_SELECT_PAINT: /* Paint mode selection flag is packed inside the nor attrib. * Note that it can be slow if auto smooth is enabled. (see T63946) */ - GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.loops_lines_paint_mask); - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_pos_nor); + FOREACH_MESH_BUFFER_CACHE(cache, mbufcache) + { + GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.lines_paint_mask); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor); + } GPU_BATCH_DISCARD_SAFE(cache->batch.surface); GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops); GPU_BATCH_DISCARD_SAFE(cache->batch.wire_edges); - if (cache->surf_per_mat) { + if (cache->surface_per_mat) { for (int i = 0; i < cache->mat_len; i++) { - GPU_BATCH_DISCARD_SAFE(cache->surf_per_mat[i]); + GPU_BATCH_DISCARD_SAFE(cache->surface_per_mat[i]); } } cache->batch_ready &= ~(MBC_SURFACE | MBC_WIRE_EDGES | MBC_WIRE_LOOPS | MBC_SURF_PER_MAT); @@ -2314,14 +571,17 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, int mode) mesh_batch_cache_discard_uvedit(cache); break; case BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT: - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_uv_data); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_uv_data); + FOREACH_MESH_BUFFER_CACHE(cache, mbufcache) + { + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data); + } GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_area); GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_angle); GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces); GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges); GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts); - GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_facedots); + GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_fdots); cache->batch_ready &= ~MBC_EDITUV; break; default: @@ -2335,18 +595,16 @@ static void mesh_batch_cache_clear(Mesh *me) if (!cache) { return; } - - for (int i = 0; i < sizeof(cache->ordered) / sizeof(void *); ++i) { - GPUVertBuf **vbo = (GPUVertBuf **)&cache->ordered; - GPU_VERTBUF_DISCARD_SAFE(vbo[i]); - } - for (int i = 0; i < sizeof(cache->edit) / sizeof(void *); ++i) { - GPUVertBuf **vbo = (GPUVertBuf **)&cache->edit; - GPU_VERTBUF_DISCARD_SAFE(vbo[i]); - } - for (int i = 0; i < sizeof(cache->ibo) / sizeof(void *); ++i) { - GPUIndexBuf **ibo = (GPUIndexBuf **)&cache->ibo; - GPU_INDEXBUF_DISCARD_SAFE(ibo[i]); + FOREACH_MESH_BUFFER_CACHE(cache, mbufcache) + { + GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; + GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; + for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); ++i) { + GPU_VERTBUF_DISCARD_SAFE(vbos[i]); + } + for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); ++i) { + GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); + } } for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); ++i) { GPUBatch **batch = (GPUBatch **)&cache->batch; @@ -2368,2074 +626,6 @@ void DRW_mesh_batch_cache_free(Mesh *me) MEM_SAFE_FREE(me->runtime.batch_cache); } -/* GPUBatch cache usage. */ - -static void mesh_create_edit_vertex_loops(MeshRenderData *rdata, - GPUVertBuf *vbo_pos_nor, - GPUVertBuf *vbo_lnor, - GPUVertBuf *vbo_uv, - GPUVertBuf *vbo_data, - GPUVertBuf *vbo_verts, - GPUVertBuf *vbo_edges, - GPUVertBuf *vbo_faces) -{ -#if 0 - const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata); - const int edge_len = mesh_render_data_edges_len_get_maybe_mapped(rdata); -#endif - const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata); - const int lvert_len = mesh_render_data_loose_verts_len_get_maybe_mapped(rdata); - const int ledge_len = mesh_render_data_loose_edges_len_get_maybe_mapped(rdata); - const int loop_len = mesh_render_data_loops_len_get_maybe_mapped(rdata); - const int tot_loop_len = loop_len + ledge_len * 2 + lvert_len; - float(*lnors)[3] = rdata->loop_normals; - uchar fflag; - - /* Static formats */ - static struct { - GPUVertFormat sel_id, pos_nor, lnor, flag, uv; - } format = {{0}}; - static struct { - uint sel_id, pos, nor, lnor, data, uvs; - } attr_id; - if (format.sel_id.attr_len == 0) { - attr_id.sel_id = GPU_vertformat_attr_add( - &format.sel_id, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); - attr_id.pos = GPU_vertformat_attr_add( - &format.pos_nor, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - attr_id.nor = GPU_vertformat_attr_add( - &format.pos_nor, "vnor", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); - attr_id.lnor = GPU_vertformat_attr_add( - &format.lnor, "lnor", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); - attr_id.data = GPU_vertformat_attr_add(&format.flag, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); - attr_id.uvs = GPU_vertformat_attr_add(&format.uv, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_vertformat_alias_add(&format.uv, "pos"); - GPU_vertformat_alias_add(&format.flag, "flag"); - } - - GPUVertBufRaw raw_verts, raw_edges, raw_faces, raw_pos, raw_nor, raw_lnor, raw_uv, raw_data; - if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) { - GPU_vertbuf_init_with_format(vbo_pos_nor, &format.pos_nor); - GPU_vertbuf_data_alloc(vbo_pos_nor, tot_loop_len); - GPU_vertbuf_attr_get_raw_data(vbo_pos_nor, attr_id.pos, &raw_pos); - GPU_vertbuf_attr_get_raw_data(vbo_pos_nor, attr_id.nor, &raw_nor); - } - if (DRW_TEST_ASSIGN_VBO(vbo_lnor)) { - GPU_vertbuf_init_with_format(vbo_lnor, &format.lnor); - GPU_vertbuf_data_alloc(vbo_lnor, tot_loop_len); - GPU_vertbuf_attr_get_raw_data(vbo_lnor, attr_id.lnor, &raw_lnor); - } - if (DRW_TEST_ASSIGN_VBO(vbo_data)) { - GPU_vertbuf_init_with_format(vbo_data, &format.flag); - GPU_vertbuf_data_alloc(vbo_data, tot_loop_len); - GPU_vertbuf_attr_get_raw_data(vbo_data, attr_id.data, &raw_data); - } - if (DRW_TEST_ASSIGN_VBO(vbo_uv)) { - GPU_vertbuf_init_with_format(vbo_uv, &format.uv); - GPU_vertbuf_data_alloc(vbo_uv, tot_loop_len); - GPU_vertbuf_attr_get_raw_data(vbo_uv, attr_id.uvs, &raw_uv); - } - /* Select Idx */ - if (DRW_TEST_ASSIGN_VBO(vbo_verts)) { - GPU_vertbuf_init_with_format(vbo_verts, &format.sel_id); - GPU_vertbuf_data_alloc(vbo_verts, tot_loop_len); - GPU_vertbuf_attr_get_raw_data(vbo_verts, attr_id.sel_id, &raw_verts); - } - if (DRW_TEST_ASSIGN_VBO(vbo_edges)) { - GPU_vertbuf_init_with_format(vbo_edges, &format.sel_id); - GPU_vertbuf_data_alloc(vbo_edges, tot_loop_len); - GPU_vertbuf_attr_get_raw_data(vbo_edges, attr_id.sel_id, &raw_edges); - } - if (DRW_TEST_ASSIGN_VBO(vbo_faces)) { - GPU_vertbuf_init_with_format(vbo_faces, &format.sel_id); - GPU_vertbuf_data_alloc(vbo_faces, tot_loop_len); - GPU_vertbuf_attr_get_raw_data(vbo_faces, attr_id.sel_id, &raw_faces); - } - - if (rdata->edit_bmesh && rdata->mapped.use == false) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter_efa, iter_loop, iter_vert; - BMFace *efa; - BMEdge *eed; - BMVert *eve; - BMLoop *loop; - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - - /* Face Loops */ - BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) { - int fidx = BM_elem_index_get(efa); - if (vbo_data) { - fflag = mesh_render_data_face_flag(rdata, efa, cd_loop_uv_offset); - } - BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) { - if (vbo_pos_nor) { - GPUPackedNormal *vnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor); - *vnor = GPU_normal_convert_i10_v3(loop->v->no); - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), loop->v->co); - } - if (vbo_lnor) { - const float *nor = (lnors) ? lnors[BM_elem_index_get(loop)] : efa->no; - GPUPackedNormal *lnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_lnor); - *lnor = GPU_normal_convert_i10_v3(nor); - } - if (vbo_data) { - EdgeDrawAttr eattr = {.v_flag = fflag}; - mesh_render_data_edge_flag(rdata, loop->e, &eattr); - mesh_render_data_vert_flag(rdata, loop->v, &eattr); - mesh_render_data_loop_flag(rdata, loop, cd_loop_uv_offset, &eattr); - memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr)); - } - if (vbo_uv) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_loop_uv_offset); - copy_v2_v2(GPU_vertbuf_raw_step(&raw_uv), luv->uv); - } - /* Select Idx */ - if (vbo_verts) { - int vidx = BM_elem_index_get(loop->v); - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - if (vbo_edges) { - int eidx = BM_elem_index_get(loop->e); - *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx; - } - if (vbo_faces) { - *((uint *)GPU_vertbuf_raw_step(&raw_faces)) = fidx; - } - } - } - /* Loose edges */ - for (int e = 0; e < ledge_len; e++) { - eed = BM_edge_at_index(bm, rdata->loose_edges[e]); - BM_ITER_ELEM (eve, &iter_vert, eed, BM_VERTS_OF_EDGE) { - if (vbo_pos_nor) { - GPUPackedNormal *vnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor); - *vnor = GPU_normal_convert_i10_v3(eve->no); - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), eve->co); - } - if (vbo_data) { - EdgeDrawAttr eattr = {0}; - mesh_render_data_edge_flag(rdata, eed, &eattr); - mesh_render_data_vert_flag(rdata, eve, &eattr); - memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr)); - } - if (vbo_lnor) { - memset(GPU_vertbuf_raw_step(&raw_lnor), 0, sizeof(GPUPackedNormal)); - } - /* Select Idx */ - if (vbo_verts) { - int vidx = BM_elem_index_get(eve); - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - if (vbo_edges) { - int eidx = BM_elem_index_get(eed); - *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx; - } - } - } - /* Loose verts */ - for (int e = 0; e < lvert_len; e++) { - eve = BM_vert_at_index(bm, rdata->loose_verts[e]); - if (vbo_pos_nor) { - GPUPackedNormal *vnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor); - *vnor = GPU_normal_convert_i10_v3(eve->no); - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), eve->co); - } - if (vbo_lnor) { - memset(GPU_vertbuf_raw_step(&raw_lnor), 0, sizeof(GPUPackedNormal)); - } - if (vbo_data) { - EdgeDrawAttr eattr = {0}; - mesh_render_data_vert_flag(rdata, eve, &eattr); - memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr)); - } - /* Select Idx */ - if (vbo_verts) { - int vidx = BM_elem_index_get(eve); - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - } - } - else if (rdata->mapped.use == true) { - BMesh *bm = rdata->edit_bmesh->bm; - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - - const MPoly *mpoly = rdata->mapped.me_cage->mpoly; - const MEdge *medge = rdata->mapped.me_cage->medge; - const MVert *mvert = rdata->mapped.me_cage->mvert; - const MLoop *mloop = rdata->mapped.me_cage->mloop; - - const int *v_origindex = rdata->mapped.v_origindex; - const int *e_origindex = rdata->mapped.e_origindex; - const int *p_origindex = rdata->mapped.p_origindex; - - /* Face Loops */ - for (int poly = 0; poly < poly_len; poly++, mpoly++) { - const MLoop *l = &mloop[mpoly->loopstart]; - int fidx = p_origindex[poly]; - BMFace *efa = NULL; - if (vbo_data) { - fflag = 0; - if (fidx != ORIGINDEX_NONE) { - efa = BM_face_at_index(bm, fidx); - fflag = mesh_render_data_face_flag(rdata, efa, cd_loop_uv_offset); - } - } - for (int i = 0; i < mpoly->totloop; i++, l++) { - if (vbo_pos_nor) { - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[l->v].co); - } - if (vbo_lnor || vbo_pos_nor) { - GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[l->v].no); - if (vbo_pos_nor) { - *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor) = vnor; - } - if (vbo_lnor) { - /* Mapped does not support lnors yet. */ - *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_lnor) = vnor; - } - } - if (vbo_data) { - EdgeDrawAttr eattr = {.v_flag = fflag}; - int vidx = v_origindex[l->v]; - int eidx = e_origindex[l->e]; - if (vidx != ORIGINDEX_NONE) { - BMVert *eve = BM_vert_at_index(bm, vidx); - mesh_render_data_vert_flag(rdata, eve, &eattr); - } - if (eidx != ORIGINDEX_NONE) { - BMEdge *eed = BM_edge_at_index(bm, eidx); - mesh_render_data_edge_flag(rdata, eed, &eattr); - if (efa) { - BMLoop *loop = BM_face_edge_share_loop(efa, eed); - if (loop) { - mesh_render_data_loop_flag(rdata, loop, cd_loop_uv_offset, &eattr); - } - } - } - memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr)); - } - if (vbo_uv) { - MLoopUV *luv = &rdata->mloopuv[mpoly->loopstart + i]; - copy_v2_v2(GPU_vertbuf_raw_step(&raw_uv), luv->uv); - } - /* Select Idx */ - if (vbo_verts) { - int vidx = v_origindex[l->v]; - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - if (vbo_edges) { - int eidx = e_origindex[l->e]; - *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx; - } - if (vbo_faces) { - *((uint *)GPU_vertbuf_raw_step(&raw_faces)) = fidx; - } - } - } - /* Loose edges */ - for (int j = 0; j < ledge_len; j++) { - const int e = rdata->mapped.loose_edges[j]; - for (int i = 0; i < 2; ++i) { - int v = (i == 0) ? medge[e].v1 : medge[e].v2; - if (vbo_pos_nor) { - GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[v].no); - *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor) = vnor; - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[v].co); - } - if (vbo_lnor) { - memset(GPU_vertbuf_raw_step(&raw_lnor), 0, sizeof(GPUPackedNormal)); - } - if (vbo_data) { - EdgeDrawAttr eattr = {0}; - int vidx = v_origindex[v]; - int eidx = e_origindex[e]; - if (vidx != ORIGINDEX_NONE) { - BMVert *eve = BM_vert_at_index(bm, vidx); - mesh_render_data_vert_flag(rdata, eve, &eattr); - } - if (eidx != ORIGINDEX_NONE) { - BMEdge *eed = BM_edge_at_index(bm, eidx); - mesh_render_data_edge_flag(rdata, eed, &eattr); - } - memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr)); - } - /* Select Idx */ - if (vbo_verts) { - int vidx = v_origindex[v]; - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - if (vbo_edges) { - int eidx = e_origindex[e]; - *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx; - } - } - } - /* Loose verts */ - for (int i = 0; i < lvert_len; i++) { - const int v = rdata->mapped.loose_verts[i]; - if (vbo_pos_nor) { - GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[v].no); - *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor) = vnor; - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[v].co); - } - if (vbo_lnor) { - memset(GPU_vertbuf_raw_step(&raw_lnor), 0, sizeof(GPUPackedNormal)); - } - if (vbo_data) { - EdgeDrawAttr eattr = {0}; - int vidx = v_origindex[v]; - if (vidx != ORIGINDEX_NONE) { - BMVert *eve = BM_vert_at_index(bm, vidx); - mesh_render_data_vert_flag(rdata, eve, &eattr); - } - memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr)); - } - /* Select Idx */ - if (vbo_verts) { - int vidx = v_origindex[v]; - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - } - } - else { - const MPoly *mpoly = rdata->mpoly; - const MVert *mvert = rdata->mvert; - const MLoop *mloop = rdata->mloop; - - const int *v_origindex = CustomData_get_layer(&rdata->me->vdata, CD_ORIGINDEX); - const int *e_origindex = CustomData_get_layer(&rdata->me->edata, CD_ORIGINDEX); - const int *p_origindex = CustomData_get_layer(&rdata->me->pdata, CD_ORIGINDEX); - - /* Face Loops */ - for (int poly = 0; poly < poly_len; poly++, mpoly++) { - const MLoop *l = &mloop[mpoly->loopstart]; - int fidx = p_origindex ? p_origindex[poly] : poly; - for (int i = 0; i < mpoly->totloop; i++, l++) { - if (vbo_pos_nor) { - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[l->v].co); - } - if (vbo_lnor || vbo_pos_nor) { - GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[l->v].no); - if (vbo_pos_nor) { - *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor) = vnor; - } - if (vbo_lnor) { - /* Mapped does not support lnors yet. */ - *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_lnor) = vnor; - } - } - if (vbo_uv) { - MLoopUV *luv = &rdata->mloopuv[mpoly->loopstart + i]; - copy_v2_v2(GPU_vertbuf_raw_step(&raw_uv), luv->uv); - } - /* Select Idx */ - if (vbo_verts) { - int vidx = v_origindex ? v_origindex[l->v] : l->v; - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - if (vbo_edges) { - int eidx = e_origindex ? e_origindex[l->e] : l->e; - *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx; - } - if (vbo_faces) { - *((uint *)GPU_vertbuf_raw_step(&raw_faces)) = fidx; - } - } - } - /* TODO(fclem): Until we find a way to detect - * loose verts easily outside of edit mode, this - * will remain disabled. */ -#if 0 - /* Loose edges */ - for (int e = 0; e < edge_len; e++, medge++) { - int eidx = e_origindex[e]; - if (eidx != ORIGINDEX_NONE && (medge->flag & ME_LOOSEEDGE)) { - for (int i = 0; i < 2; ++i) { - int vidx = (i == 0) ? medge->v1 : medge->v2; - if (vbo_pos) { - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[vidx].co); - } - if (vbo_verts) { - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - if (vbo_edges) { - *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx; - } - } - } - } - /* Loose verts */ - for (int v = 0; v < vert_len; v++, mvert++) { - int vidx = v_origindex[v]; - if (vidx != ORIGINDEX_NONE) { - MVert *eve = BM_vert_at_index(bm, vidx); - if (eve->e == NULL) { - if (vbo_pos) { - copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert->co); - } - if (vbo_verts) { - *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx; - } - } - } - } -#endif - } - /* Don't resize */ -} - -/* TODO: We could use gl_PrimitiveID as index instead of using another VBO. */ -static void mesh_create_edit_facedots_select_id(MeshRenderData *rdata, - GPUVertBuf *vbo, - Scene *scene, - Object *ob) -{ - const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata); - - static GPUVertFormat format = {0}; - static struct { - uint idx; - } attr_id; - if (format.attr_len == 0) { - attr_id.idx = GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); - } - - GPUVertBufRaw idx_step; - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, poly_len); - GPU_vertbuf_attr_get_raw_data(vbo, attr_id.idx, &idx_step); - - /* Keep in sync with mesh_create_edit_facedots(). */ - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - for (int poly = 0; poly < poly_len; poly++) { - const BMFace *efa = BM_face_at_index(rdata->edit_bmesh->bm, poly); - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - *((uint *)GPU_vertbuf_raw_step(&idx_step)) = poly; - } - } - } - else { - for (int poly = 0; poly < poly_len; poly++) { - *((uint *)GPU_vertbuf_raw_step(&idx_step)) = poly; - } - } - } - else { - const int *p_origindex = rdata->mapped.p_origindex; - if (modifiers_usesSubsurfFacedots(scene, ob)) { - Mesh *me_cage = rdata->mapped.me_cage; - const MPoly *mpoly = me_cage->mpoly; - for (int p = 0; p < poly_len; p++, mpoly++) { - const int p_orig = p_origindex[p]; - if (p_orig != ORIGINDEX_NONE) { - const MLoop *mloop = me_cage->mloop + mpoly->loopstart; - for (int l = 0; l < mpoly->totloop; l++, mloop++) { - if (me_cage->mvert[mloop->v].flag & ME_VERT_FACEDOT) { - const BMFace *efa = BM_face_at_index(rdata->edit_bmesh->bm, p_orig); - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - *((uint *)GPU_vertbuf_raw_step(&idx_step)) = p_orig; - } - } - } - } - } - } - else { - for (int poly = 0; poly < poly_len; poly++) { - const int p_orig = p_origindex[poly]; - if (p_orig != ORIGINDEX_NONE) { - const BMFace *efa = BM_face_at_index(rdata->edit_bmesh->bm, p_orig); - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - *((uint *)GPU_vertbuf_raw_step(&idx_step)) = p_orig; - } - } - } - } - } - - /* Resize & Finish */ - int facedot_len_used = GPU_vertbuf_raw_used(&idx_step); - if (facedot_len_used != poly_len) { - GPU_vertbuf_data_resize(vbo, facedot_len_used); - } -} - -static void mesh_create_pos_and_nor(MeshRenderData *rdata, GPUVertBuf *vbo) -{ - static GPUVertFormat format = {0}; - static struct { - uint pos, nor; - } attr_id; - if (format.attr_len == 0) { - attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - attr_id.nor = GPU_vertformat_attr_add( - &format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - const int vbo_len_capacity = mesh_render_data_verts_len_get_maybe_mapped(rdata); - GPU_vertbuf_data_alloc(vbo, vbo_len_capacity); - - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter; - BMVert *eve; - uint i; - - mesh_render_data_ensure_vert_normals_pack(rdata); - GPUPackedNormal *vnor = rdata->vert_normals_pack; - - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - GPU_vertbuf_attr_set(vbo, attr_id.pos, i, eve->co); - GPU_vertbuf_attr_set(vbo, attr_id.nor, i, &vnor[i]); - } - BLI_assert(i == vbo_len_capacity); - } - else { - for (int i = 0; i < vbo_len_capacity; i++) { - const MVert *mv = &rdata->mvert[i]; - GPUPackedNormal vnor_pack = GPU_normal_convert_i10_s3(mv->no); - vnor_pack.w = (mv->flag & ME_HIDE) ? -1 : ((mv->flag & SELECT) ? 1 : 0); - GPU_vertbuf_attr_set(vbo, attr_id.pos, i, rdata->mvert[i].co); - GPU_vertbuf_attr_set(vbo, attr_id.nor, i, &vnor_pack); - } - } - } - else { - const MVert *mvert = rdata->mapped.me_cage->mvert; - const int *v_origindex = rdata->mapped.v_origindex; - for (int i = 0; i < vbo_len_capacity; i++) { - const int v_orig = v_origindex[i]; - if (v_orig != ORIGINDEX_NONE) { - const MVert *mv = &mvert[i]; - GPUPackedNormal vnor_pack = GPU_normal_convert_i10_s3(mv->no); - vnor_pack.w = (mv->flag & ME_HIDE) ? -1 : ((mv->flag & SELECT) ? 1 : 0); - GPU_vertbuf_attr_set(vbo, attr_id.pos, i, mv->co); - GPU_vertbuf_attr_set(vbo, attr_id.nor, i, &vnor_pack); - } - } - } -} - -static void mesh_create_weights(MeshRenderData *rdata, - GPUVertBuf *vbo, - DRW_MeshWeightState *wstate) -{ - static GPUVertFormat format = {0}; - static struct { - uint weight; - } attr_id; - if (format.attr_len == 0) { - attr_id.weight = GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - - const int vbo_len_capacity = mesh_render_data_verts_len_get_maybe_mapped(rdata); - - mesh_render_data_ensure_vert_weight(rdata, wstate); - const float *vert_weight = rdata->vert_weight; - - GPU_vertbuf_init_with_format(vbo, &format); - /* Meh, another allocation / copy for no benefit. - * Needed because rdata->vert_weight is freed afterwards and - * GPU module don't have a GPU_vertbuf_data_from_memory or similar. */ - /* TODO get rid of the extra allocation/copy. */ - GPU_vertbuf_data_alloc(vbo, vbo_len_capacity); - GPU_vertbuf_attr_fill(vbo, attr_id.weight, vert_weight); -} - -static float mesh_loop_edge_factor_get(const float f_no[3], - const float v_co[3], - const float v_no[3], - const float v_next_co[3]) -{ - float enor[3], evec[3]; - sub_v3_v3v3(evec, v_next_co, v_co); - cross_v3_v3v3(enor, v_no, evec); - normalize_v3(enor); - float d = fabsf(dot_v3v3(enor, f_no)); - /* Rescale to the slider range. */ - d *= (1.0f / 0.065f); - CLAMP(d, 0.0f, 1.0f); - return d; -} - -static void vertbuf_raw_step_u8(GPUVertBufRaw *wd_step, const uchar wiredata) -{ - *((uchar *)GPU_vertbuf_raw_step(wd_step)) = wiredata; -} - -static void vertbuf_raw_step_u8_to_f32(GPUVertBufRaw *wd_step, const uchar wiredata) -{ - *((float *)GPU_vertbuf_raw_step(wd_step)) = wiredata / 255.0f; -} - -static void mesh_create_loop_edge_fac(MeshRenderData *rdata, GPUVertBuf *vbo) -{ - static GPUVertFormat format = {0}; - static struct { - uint wd; - } attr_id; - static union { - float f; - uchar u; - } data; - static void (*vertbuf_raw_step)(GPUVertBufRaw *, const uchar); - if (format.attr_len == 0) { - if (!GPU_crappy_amd_driver()) { - /* Some AMD drivers strangely crash with a vbo with this format. */ - attr_id.wd = GPU_vertformat_attr_add( - &format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - vertbuf_raw_step = vertbuf_raw_step_u8; - data.u = UCHAR_MAX; - } - else { - attr_id.wd = GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - vertbuf_raw_step = vertbuf_raw_step_u8_to_f32; - data.f = 1.0f; - } - } - const int poly_len = mesh_render_data_polys_len_get(rdata); - const int loop_len = mesh_render_data_loops_len_get(rdata); - const int edge_len = mesh_render_data_edges_len_get(rdata); - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, loop_len); - - GPUVertBufRaw wd_step; - GPU_vertbuf_attr_get_raw_data(vbo, attr_id.wd, &wd_step); - - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter_efa, iter_loop; - BMFace *efa; - BMLoop *loop; - uint f; - - BM_ITER_MESH_INDEX (efa, &iter_efa, bm, BM_FACES_OF_MESH, f) { - BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) { - float ratio = mesh_loop_edge_factor_get( - efa->no, loop->v->co, loop->v->no, loop->next->v->co); - vertbuf_raw_step(&wd_step, ratio * 253 + 1); - } - } - BLI_assert(GPU_vertbuf_raw_used(&wd_step) == loop_len); - } - else { - const MVert *mvert = rdata->mvert; - const MPoly *mpoly = rdata->mpoly; - const MLoop *mloop = rdata->mloop; - MEdge *medge = (MEdge *)rdata->medge; - bool use_edge_render = false; - - /* TODO(fclem) We don't need them to be packed. But we need rdata->poly_normals */ - mesh_render_data_ensure_poly_normals_pack(rdata); - - /* Reset flag */ - for (int edge = 0; edge < edge_len; ++edge) { - /* NOTE: not thread safe. */ - medge[edge].flag &= ~ME_EDGE_TMP_TAG; - - /* HACK(fclem) Feels like a hack. Detecting the need for edge render. */ - if ((medge[edge].flag & ME_EDGERENDER) == 0) { - use_edge_render = true; - } - } - - for (int a = 0; a < poly_len; a++, mpoly++) { - const float *fnor = rdata->poly_normals[a]; - for (int b = 0; b < mpoly->totloop; b++) { - const MLoop *ml1 = &mloop[mpoly->loopstart + b]; - const MLoop *ml2 = &mloop[mpoly->loopstart + (b + 1) % mpoly->totloop]; - - /* Will only work for edges that have an odd number of faces connected. */ - MEdge *ed = (MEdge *)rdata->medge + ml1->e; - ed->flag ^= ME_EDGE_TMP_TAG; - - if (use_edge_render) { - vertbuf_raw_step(&wd_step, (ed->flag & ME_EDGERENDER) ? 255 : 0); - } - else { - float vnor_f[3]; - normal_short_to_float_v3(vnor_f, mvert[ml1->v].no); - float ratio = mesh_loop_edge_factor_get( - fnor, mvert[ml1->v].co, vnor_f, mvert[ml2->v].co); - vertbuf_raw_step(&wd_step, ratio * 253 + 1); - } - } - } - /* Gather non-manifold edges. */ - for (int l = 0; l < loop_len; l++, mloop++) { - MEdge *ed = (MEdge *)rdata->medge + mloop->e; - if (ed->flag & ME_EDGE_TMP_TAG) { - GPU_vertbuf_attr_set(vbo, attr_id.wd, l, &data); - } - } - - BLI_assert(loop_len == GPU_vertbuf_raw_used(&wd_step)); - } - } - else { - BLI_assert(0); - } -} - -static void mesh_create_loop_pos_and_nor(MeshRenderData *rdata, GPUVertBuf *vbo) -{ - /* TODO deduplicate format creation*/ - static GPUVertFormat format = {0}; - static struct { - uint pos, nor; - } attr_id; - if (format.attr_len == 0) { - attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - attr_id.nor = GPU_vertformat_attr_add( - &format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - const int poly_len = mesh_render_data_polys_len_get(rdata); - const int loop_len = mesh_render_data_loops_len_get(rdata); - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, loop_len); - - GPUVertBufRaw pos_step, nor_step; - GPU_vertbuf_attr_get_raw_data(vbo, attr_id.pos, &pos_step); - GPU_vertbuf_attr_get_raw_data(vbo, attr_id.nor, &nor_step); - - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - const GPUPackedNormal *vnor, *pnor; - const float(*lnors)[3] = rdata->loop_normals; - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter_efa, iter_loop; - BMFace *efa; - BMLoop *loop; - uint f; - - if (rdata->loop_normals == NULL) { - mesh_render_data_ensure_poly_normals_pack(rdata); - mesh_render_data_ensure_vert_normals_pack(rdata); - vnor = rdata->vert_normals_pack; - pnor = rdata->poly_normals_pack; - } - - BM_ITER_MESH_INDEX (efa, &iter_efa, bm, BM_FACES_OF_MESH, f) { - const bool face_smooth = BM_elem_flag_test(efa, BM_ELEM_SMOOTH); - - BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) { - BLI_assert(GPU_vertbuf_raw_used(&pos_step) == BM_elem_index_get(loop)); - copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), loop->v->co); - - if (lnors) { - GPUPackedNormal plnor = GPU_normal_convert_i10_v3(lnors[BM_elem_index_get(loop)]); - *((GPUPackedNormal *)GPU_vertbuf_raw_step(&nor_step)) = plnor; - } - else if (!face_smooth) { - *((GPUPackedNormal *)GPU_vertbuf_raw_step(&nor_step)) = pnor[f]; - } - else { - *((GPUPackedNormal *)GPU_vertbuf_raw_step( - &nor_step)) = vnor[BM_elem_index_get(loop->v)]; - } - } - } - BLI_assert(GPU_vertbuf_raw_used(&pos_step) == loop_len); - } - else { - const MVert *mvert = rdata->mvert; - const MPoly *mpoly = rdata->mpoly; - - if (rdata->loop_normals == NULL) { - mesh_render_data_ensure_poly_normals_pack(rdata); - } - - for (int a = 0; a < poly_len; a++, mpoly++) { - const MLoop *mloop = rdata->mloop + mpoly->loopstart; - const float(*lnors)[3] = (rdata->loop_normals) ? &rdata->loop_normals[mpoly->loopstart] : - NULL; - const GPUPackedNormal *fnor = (mpoly->flag & ME_SMOOTH) ? NULL : - &rdata->poly_normals_pack[a]; - const int hide_select_flag = (mpoly->flag & ME_HIDE) ? - -1 : - ((mpoly->flag & ME_FACE_SEL) ? 1 : 0); - for (int b = 0; b < mpoly->totloop; b++, mloop++) { - copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[mloop->v].co); - GPUPackedNormal *pnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&nor_step); - if (lnors) { - *pnor = GPU_normal_convert_i10_v3(lnors[b]); - } - else if (fnor) { - *pnor = *fnor; - } - else { - *pnor = GPU_normal_convert_i10_s3(mvert[mloop->v].no); - } - pnor->w = hide_select_flag; - } - } - - BLI_assert(loop_len == GPU_vertbuf_raw_used(&pos_step)); - } - } - else { - const int *p_origindex = rdata->mapped.p_origindex; - const MVert *mvert = rdata->mvert; - const MPoly *mpoly = rdata->mpoly; - - if (rdata->loop_normals == NULL) { - mesh_render_data_ensure_poly_normals_pack(rdata); - } - - for (int a = 0; a < poly_len; a++, mpoly++) { - const MLoop *mloop = rdata->mloop + mpoly->loopstart; - const float(*lnors)[3] = (rdata->loop_normals) ? &rdata->loop_normals[mpoly->loopstart] : - NULL; - const GPUPackedNormal *fnor = (mpoly->flag & ME_SMOOTH) ? NULL : - &rdata->poly_normals_pack[a]; - if (p_origindex[a] == ORIGINDEX_NONE) { - continue; - } - for (int b = 0; b < mpoly->totloop; b++, mloop++) { - copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[mloop->v].co); - GPUPackedNormal *pnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&nor_step); - if (lnors) { - *pnor = GPU_normal_convert_i10_v3(lnors[b]); - } - else if (fnor) { - *pnor = *fnor; - } - else { - *pnor = GPU_normal_convert_i10_s3(mvert[mloop->v].no); - } - } - } - } - - int vbo_len_used = GPU_vertbuf_raw_used(&pos_step); - if (vbo_len_used < loop_len) { - GPU_vertbuf_data_resize(vbo, vbo_len_used); - } -} - -static void mesh_create_loop_orco(MeshRenderData *rdata, GPUVertBuf *vbo) -{ - const uint loops_len = mesh_render_data_loops_len_get(rdata); - - /* initialize vertex format */ - GPUVertFormat format = {0}; - GPUVertBufRaw vbo_step; - - /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex attribs. - * This is a substential waste of Vram and should be done another way. Unfortunately, - * at the time of writing, I did not found any other "non disruptive" alternative. */ - uint attr_id = GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, loops_len); - GPU_vertbuf_attr_get_raw_data(vbo, attr_id, &vbo_step); - - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter_efa, iter_loop; - BMFace *efa; - BMLoop *loop; - - BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) { - float *data = (float *)GPU_vertbuf_raw_step(&vbo_step); - copy_v3_v3(data, rdata->orco[BM_elem_index_get(loop->v)]); - data[3] = 0.0; /* Tag as not a generic attrib */ - } - } - } - else { - for (uint l = 0; l < loops_len; l++) { - float *data = (float *)GPU_vertbuf_raw_step(&vbo_step); - copy_v3_v3(data, rdata->orco[rdata->mloop[l].v]); - data[3] = 0.0; /* Tag as not a generic attrib */ - } - } -} - -static void mesh_create_loop_uv_and_tan(MeshRenderData *rdata, GPUVertBuf *vbo) -{ - const uint loops_len = mesh_render_data_loops_len_get(rdata); - const uint uv_len = rdata->cd.layers.uv_len; - const uint tangent_len = rdata->cd.layers.tangent_len; - const uint layers_combined_len = uv_len + tangent_len; - - GPUVertBufRaw *layers_combined_step = BLI_array_alloca(layers_combined_step, - layers_combined_len); - GPUVertBufRaw *uv_step = layers_combined_step; - GPUVertBufRaw *tangent_step = uv_step + uv_len; - - uint *layers_combined_id = BLI_array_alloca(layers_combined_id, layers_combined_len); - uint *uv_id = layers_combined_id; - uint *tangent_id = uv_id + uv_len; - - /* initialize vertex format */ - GPUVertFormat format = {0}; - - for (uint i = 0; i < uv_len; i++) { - const char *attr_name = mesh_render_data_uv_layer_uuid_get(rdata, i); -#if 0 /* these are clamped. Maybe use them as an option in the future */ - uv_id[i] = GPU_vertformat_attr_add( - &format, attr_name, GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); -#else - uv_id[i] = GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); -#endif - /* Auto Name */ - attr_name = mesh_render_data_uv_auto_layer_uuid_get(rdata, i); - GPU_vertformat_alias_add(&format, attr_name); - - if (i == rdata->cd.layers.uv_render) { - GPU_vertformat_alias_add(&format, "u"); - } - if (i == rdata->cd.layers.uv_active) { - GPU_vertformat_alias_add(&format, "au"); - } - if (i == rdata->cd.layers.uv_mask_active) { - GPU_vertformat_alias_add(&format, "mu"); - } - } - - for (uint i = 0; i < tangent_len; i++) { - const char *attr_name = mesh_render_data_tangent_layer_uuid_get(rdata, i); -#ifdef USE_COMP_MESH_DATA - tangent_id[i] = GPU_vertformat_attr_add( - &format, attr_name, GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); -#else - tangent_id[i] = GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT); -#endif - if (i == rdata->cd.layers.tangent_render) { - GPU_vertformat_alias_add(&format, "t"); - } - if (i == rdata->cd.layers.tangent_active) { - GPU_vertformat_alias_add(&format, "at"); - } - } - - /* HACK: Create a dummy attribute in case there is no valid UV/tangent layer. */ - if (layers_combined_len == 0) { - GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, loops_len); - - for (uint i = 0; i < uv_len; i++) { - GPU_vertbuf_attr_get_raw_data(vbo, uv_id[i], &uv_step[i]); - } - for (uint i = 0; i < tangent_len; i++) { - GPU_vertbuf_attr_get_raw_data(vbo, tangent_id[i], &tangent_step[i]); - } - - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter_efa, iter_loop; - BMFace *efa; - BMLoop *loop; - - BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) { - /* UVs */ - for (uint j = 0; j < uv_len; j++) { - const uint layer_offset = rdata->cd.offset.uv[j]; - const float *elem = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(loop, layer_offset))->uv; - copy_v2_v2(GPU_vertbuf_raw_step(&uv_step[j]), elem); - } - /* TANGENTs */ - for (uint j = 0; j < tangent_len; j++) { - float(*layer_data)[4] = rdata->cd.layers.tangent[j]; - const float *elem = layer_data[BM_elem_index_get(loop)]; -#ifdef USE_COMP_MESH_DATA - normal_float_to_short_v4(GPU_vertbuf_raw_step(&tangent_step[j]), elem); -#else - copy_v4_v4(GPU_vertbuf_raw_step(&tangent_step[j]), elem); -#endif - } - } - } - } - else { - for (uint loop = 0; loop < loops_len; loop++) { - /* UVs */ - for (uint j = 0; j < uv_len; j++) { - const MLoopUV *layer_data = rdata->cd.layers.uv[j]; - const float *elem = layer_data[loop].uv; - copy_v2_v2(GPU_vertbuf_raw_step(&uv_step[j]), elem); - } - /* TANGENTs */ - for (uint j = 0; j < tangent_len; j++) { - float(*layer_data)[4] = rdata->cd.layers.tangent[j]; - const float *elem = layer_data[loop]; -#ifdef USE_COMP_MESH_DATA - normal_float_to_short_v4(GPU_vertbuf_raw_step(&tangent_step[j]), elem); -#else - copy_v4_v4(GPU_vertbuf_raw_step(&tangent_step[j]), elem); -#endif - } - } - } - -#ifndef NDEBUG - /* Check all layers are write aligned. */ - if (layers_combined_len > 0) { - int vbo_len_used = GPU_vertbuf_raw_used(&layers_combined_step[0]); - for (uint i = 0; i < layers_combined_len; i++) { - BLI_assert(vbo_len_used == GPU_vertbuf_raw_used(&layers_combined_step[i])); - } - } -#endif - -#undef USE_COMP_MESH_DATA -} - -static void mesh_create_loop_vcol(MeshRenderData *rdata, GPUVertBuf *vbo) -{ - const uint loops_len = mesh_render_data_loops_len_get(rdata); - const uint vcol_len = rdata->cd.layers.vcol_len; - - GPUVertBufRaw *vcol_step = BLI_array_alloca(vcol_step, vcol_len); - uint *vcol_id = BLI_array_alloca(vcol_id, vcol_len); - - /* initialize vertex format */ - GPUVertFormat format = {0}; - - for (uint i = 0; i < vcol_len; i++) { - const char *attr_name = mesh_render_data_vcol_layer_uuid_get(rdata, i); - vcol_id[i] = GPU_vertformat_attr_add( - &format, attr_name, GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); - /* Auto layer */ - if (rdata->cd.layers.auto_vcol[i]) { - attr_name = mesh_render_data_vcol_auto_layer_uuid_get(rdata, i); - GPU_vertformat_alias_add(&format, attr_name); - } - if (i == rdata->cd.layers.vcol_render) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == rdata->cd.layers.vcol_active) { - GPU_vertformat_alias_add(&format, "ac"); - } - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, loops_len); - - for (uint i = 0; i < vcol_len; i++) { - GPU_vertbuf_attr_get_raw_data(vbo, vcol_id[i], &vcol_step[i]); - } - - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter_efa, iter_loop; - BMFace *efa; - BMLoop *loop; - - BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) { - for (uint j = 0; j < vcol_len; j++) { - const uint layer_offset = rdata->cd.offset.vcol[j]; - const uchar *elem = &((MLoopCol *)BM_ELEM_CD_GET_VOID_P(loop, layer_offset))->r; - copy_v3_v3_uchar(GPU_vertbuf_raw_step(&vcol_step[j]), elem); - } - } - } - } - else { - for (uint loop = 0; loop < loops_len; loop++) { - for (uint j = 0; j < vcol_len; j++) { - const MLoopCol *layer_data = rdata->cd.layers.vcol[j]; - const uchar *elem = &layer_data[loop].r; - copy_v3_v3_uchar(GPU_vertbuf_raw_step(&vcol_step[j]), elem); - } - } - } - -#ifndef NDEBUG - /* Check all layers are write aligned. */ - if (vcol_len > 0) { - int vbo_len_used = GPU_vertbuf_raw_used(&vcol_step[0]); - for (uint i = 0; i < vcol_len; i++) { - BLI_assert(vbo_len_used == GPU_vertbuf_raw_used(&vcol_step[i])); - } - } -#endif - -#undef USE_COMP_MESH_DATA -} - -static void mesh_create_edit_facedots(MeshRenderData *rdata, - GPUVertBuf *vbo_facedots_pos_nor_data, - Scene *scene, - Object *ob) -{ - const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata); - const int verts_facedot_len = poly_len; - int facedot_len_used = 0; - - static struct { - uint fdot_pos, fdot_nor_flag; - } attr_id; - static GPUVertFormat facedot_format = {0}; - if (facedot_format.attr_len == 0) { - attr_id.fdot_pos = GPU_vertformat_attr_add( - &facedot_format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - attr_id.fdot_nor_flag = GPU_vertformat_attr_add( - &facedot_format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - if (DRW_TEST_ASSIGN_VBO(vbo_facedots_pos_nor_data)) { - GPU_vertbuf_init_with_format(vbo_facedots_pos_nor_data, &facedot_format); - GPU_vertbuf_data_alloc(vbo_facedots_pos_nor_data, verts_facedot_len); - /* TODO(fclem): Maybe move data generation to mesh_render_data_create() */ - if (rdata->edit_bmesh) { - if (rdata->edit_data && rdata->edit_data->vertexCos != NULL) { - BKE_editmesh_cache_ensure_poly_normals(rdata->edit_bmesh, rdata->edit_data); - BKE_editmesh_cache_ensure_poly_centers(rdata->edit_bmesh, rdata->edit_data); - } - } - } - - if (rdata->mapped.use == false) { - for (int i = 0; i < poly_len; i++) { - if (add_edit_facedot(rdata, - vbo_facedots_pos_nor_data, - attr_id.fdot_pos, - attr_id.fdot_nor_flag, - i, - facedot_len_used)) { - facedot_len_used += 1; - } - } - } - else { - if (modifiers_usesSubsurfFacedots(scene, ob)) { - /* Facedots that follow surbsurf face center. */ - Mesh *me_cage = rdata->mapped.me_cage; - const MPoly *mpoly = me_cage->mpoly; - for (int p = 0; p < poly_len; p++, mpoly++) { - const MLoop *mloop = me_cage->mloop + mpoly->loopstart; - for (int l = 0; l < mpoly->totloop; l++, mloop++) { - if (me_cage->mvert[mloop->v].flag & ME_VERT_FACEDOT) { - if (add_edit_facedot_subdiv(rdata, - vbo_facedots_pos_nor_data, - attr_id.fdot_pos, - attr_id.fdot_nor_flag, - mloop->v, - p, - facedot_len_used)) { - facedot_len_used += 1; - } - } - } - } - } - else { - for (int i = 0; i < poly_len; i++) { - if (add_edit_facedot_mapped(rdata, - vbo_facedots_pos_nor_data, - attr_id.fdot_pos, - attr_id.fdot_nor_flag, - i, - facedot_len_used)) { - facedot_len_used += 1; - } - } - } - } - - /* Resize & Finish */ - if (facedot_len_used != verts_facedot_len) { - if (vbo_facedots_pos_nor_data != NULL) { - GPU_vertbuf_data_resize(vbo_facedots_pos_nor_data, facedot_len_used); - } - } -} - -static void mesh_create_edit_mesh_analysis(MeshRenderData *rdata, GPUVertBuf *vbo_mesh_analysis) -{ - const MeshStatVis *mesh_stat_vis = &rdata->toolsettings->statvis; - - int mesh_analysis_len_used = 0; - - const uint loops_len = mesh_render_data_loops_len_get(rdata); - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter_efa, iter_loop; - BMFace *efa; - BMLoop *loop; - - static struct { - uint weight; - } attr_id; - static GPUVertFormat mesh_analysis_format = {0}; - if (mesh_analysis_format.attr_len == 0) { - attr_id.weight = GPU_vertformat_attr_add( - &mesh_analysis_format, "weight_color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - /* TODO(jbakker): Maybe move data generation to mesh_render_data_create() */ - BKE_editmesh_statvis_calc(rdata->edit_bmesh, rdata->edit_data, mesh_stat_vis); - - if (DRW_TEST_ASSIGN_VBO(vbo_mesh_analysis)) { - GPU_vertbuf_init_with_format(vbo_mesh_analysis, &mesh_analysis_format); - GPU_vertbuf_data_alloc(vbo_mesh_analysis, loops_len); - } - - const bool is_vertex_data = mesh_stat_vis->type == SCE_STATVIS_SHARP; - if (is_vertex_data) { - BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) { - uint vertex_index = BM_elem_index_get(loop->v); - GPU_vertbuf_attr_set(vbo_mesh_analysis, - attr_id.weight, - mesh_analysis_len_used, - &rdata->edit_bmesh->derivedVertColor[vertex_index]); - mesh_analysis_len_used += 1; - } - } - } - else { - uint face_index; - BM_ITER_MESH_INDEX (efa, &iter_efa, bm, BM_FACES_OF_MESH, face_index) { - BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) { - GPU_vertbuf_attr_set(vbo_mesh_analysis, - attr_id.weight, - mesh_analysis_len_used, - &rdata->edit_bmesh->derivedFaceColor[face_index]); - mesh_analysis_len_used += 1; - } - } - } - - // Free temp data in edit bmesh - BKE_editmesh_color_free(rdata->edit_bmesh); - - /* Resize & Finish */ - if (mesh_analysis_len_used != loops_len) { - if (vbo_mesh_analysis != NULL) { - GPU_vertbuf_data_resize(vbo_mesh_analysis, mesh_analysis_len_used); - } - } -} -/* Indices */ - -#define NO_EDGE INT_MAX -static void mesh_create_edges_adjacency_lines(MeshRenderData *rdata, - GPUIndexBuf *ibo, - bool *r_is_manifold, - const bool use_hide) -{ - const MLoopTri *mlooptri; - const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata); - const int tri_len = mesh_render_data_looptri_len_get_maybe_mapped(rdata); - - *r_is_manifold = true; - - /* Allocate max but only used indices are sent to GPU. */ - GPUIndexBufBuilder elb; - GPU_indexbuf_init(&elb, GPU_PRIM_LINES_ADJ, tri_len * 3, vert_len); - - if (rdata->mapped.use) { - Mesh *me_cage = rdata->mapped.me_cage; - mlooptri = BKE_mesh_runtime_looptri_ensure(me_cage); - } - else { - mlooptri = rdata->mlooptri; - } - - EdgeHash *eh = BLI_edgehash_new_ex(__func__, tri_len * 3); - /* Create edges for each pair of triangles sharing an edge. */ - for (int i = 0; i < tri_len; i++) { - for (int e = 0; e < 3; e++) { - uint v0, v1, v2; - if (rdata->mapped.use) { - const MLoop *mloop = rdata->mloop; - const MLoopTri *mlt = mlooptri + i; - const int p_orig = rdata->mapped.p_origindex[mlt->poly]; - if (p_orig != ORIGINDEX_NONE) { - BMesh *bm = rdata->edit_bmesh->bm; - BMFace *efa = BM_face_at_index(bm, p_orig); - /* Assume 'use_hide' */ - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - break; - } - } - v0 = mloop[mlt->tri[e]].v; - v1 = mloop[mlt->tri[(e + 1) % 3]].v; - v2 = mloop[mlt->tri[(e + 2) % 3]].v; - } - else if (rdata->edit_bmesh) { - const BMLoop **bm_looptri = (const BMLoop **)rdata->edit_bmesh->looptris[i]; - if (BM_elem_flag_test(bm_looptri[0]->f, BM_ELEM_HIDDEN)) { - break; - } - v0 = BM_elem_index_get(bm_looptri[e]->v); - v1 = BM_elem_index_get(bm_looptri[(e + 1) % 3]->v); - v2 = BM_elem_index_get(bm_looptri[(e + 2) % 3]->v); - } - else { - const MLoop *mloop = rdata->mloop; - const MLoopTri *mlt = mlooptri + i; - const MPoly *mp = &rdata->mpoly[mlt->poly]; - if (use_hide && (mp->flag & ME_HIDE)) { - break; - } - v0 = mloop[mlt->tri[e]].v; - v1 = mloop[mlt->tri[(e + 1) % 3]].v; - v2 = mloop[mlt->tri[(e + 2) % 3]].v; - } - bool inv_indices = (v1 > v2); - void **pval; - bool value_is_init = BLI_edgehash_ensure_p(eh, v1, v2, &pval); - int v_data = POINTER_AS_INT(*pval); - if (!value_is_init || v_data == NO_EDGE) { - /* Save the winding order inside the sign bit. Because the - * edgehash sort the keys and we need to compare winding later. */ - int value = (int)v0 + 1; /* Int 0 bm_looptricannot be signed */ - *pval = POINTER_FROM_INT((inv_indices) ? -value : value); - } - else { - /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */ - *pval = POINTER_FROM_INT(NO_EDGE); - bool inv_opposite = (v_data < 0); - uint v_opposite = (uint)abs(v_data) - 1; - - if (inv_opposite == inv_indices) { - /* Don't share edge if triangles have non matching winding. */ - GPU_indexbuf_add_line_adj_verts(&elb, v0, v1, v2, v0); - GPU_indexbuf_add_line_adj_verts(&elb, v_opposite, v1, v2, v_opposite); - *r_is_manifold = false; - } - else { - GPU_indexbuf_add_line_adj_verts(&elb, v0, v1, v2, v_opposite); - } - } - } - } - /* Create edges for remaining non manifold edges. */ - EdgeHashIterator *ehi; - for (ehi = BLI_edgehashIterator_new(eh); BLI_edgehashIterator_isDone(ehi) == false; - BLI_edgehashIterator_step(ehi)) { - uint v1, v2; - int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi)); - if (v_data == NO_EDGE) { - continue; - } - BLI_edgehashIterator_getKey(ehi, &v1, &v2); - uint v0 = (uint)abs(v_data) - 1; - if (v_data < 0) { /* inv_opposite */ - SWAP(uint, v1, v2); - } - GPU_indexbuf_add_line_adj_verts(&elb, v0, v1, v2, v0); - *r_is_manifold = false; - } - BLI_edgehashIterator_free(ehi); - BLI_edgehash_free(eh, NULL); - - GPU_indexbuf_build_in_place(&elb, ibo); -} -#undef NO_EDGE - -static void mesh_create_edges_lines(MeshRenderData *rdata, GPUIndexBuf *ibo, const bool use_hide) -{ - const int verts_len = mesh_render_data_verts_len_get_maybe_mapped(rdata); - const int edges_len = mesh_render_data_edges_len_get_maybe_mapped(rdata); - - GPUIndexBufBuilder elb; - GPU_indexbuf_init(&elb, GPU_PRIM_LINES, edges_len, verts_len); - - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter; - BMEdge *eed; - - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - /* use_hide always for edit-mode */ - if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - continue; - } - GPU_indexbuf_add_line_verts(&elb, BM_elem_index_get(eed->v1), BM_elem_index_get(eed->v2)); - } - } - else { - const MEdge *ed = rdata->medge; - for (int i = 0; i < edges_len; i++, ed++) { - if ((ed->flag & ME_EDGERENDER) == 0) { - continue; - } - if (!(use_hide && (ed->flag & ME_HIDE))) { - GPU_indexbuf_add_line_verts(&elb, ed->v1, ed->v2); - } - } - } - } - else { - BMesh *bm = rdata->edit_bmesh->bm; - const MEdge *edge = rdata->medge; - for (int i = 0; i < edges_len; i++, edge++) { - const int p_orig = rdata->mapped.e_origindex[i]; - if (p_orig != ORIGINDEX_NONE) { - BMEdge *eed = BM_edge_at_index(bm, p_orig); - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - GPU_indexbuf_add_line_verts(&elb, edge->v1, edge->v2); - } - } - } - } - - GPU_indexbuf_build_in_place(&elb, ibo); -} - -static void mesh_create_surf_tris(MeshRenderData *rdata, GPUIndexBuf *ibo, const bool use_hide) -{ - const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata); - const int tri_len = mesh_render_data_looptri_len_get(rdata); - - GPUIndexBufBuilder elb; - GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tri_len, vert_len * 3); - - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - for (int i = 0; i < tri_len; i++) { - const BMLoop **bm_looptri = (const BMLoop **)rdata->edit_bmesh->looptris[i]; - const BMFace *bm_face = bm_looptri[0]->f; - /* use_hide always for edit-mode */ - if (BM_elem_flag_test(bm_face, BM_ELEM_HIDDEN)) { - continue; - } - GPU_indexbuf_add_tri_verts(&elb, - BM_elem_index_get(bm_looptri[0]->v), - BM_elem_index_get(bm_looptri[1]->v), - BM_elem_index_get(bm_looptri[2]->v)); - } - } - else { - const MLoop *loops = rdata->mloop; - for (int i = 0; i < tri_len; i++) { - const MLoopTri *mlt = &rdata->mlooptri[i]; - const MPoly *mp = &rdata->mpoly[mlt->poly]; - if (use_hide && (mp->flag & ME_HIDE)) { - continue; - } - GPU_indexbuf_add_tri_verts( - &elb, loops[mlt->tri[0]].v, loops[mlt->tri[1]].v, loops[mlt->tri[2]].v); - } - } - } - else { - /* Note: mapped doesn't support lnors yet. */ - BMesh *bm = rdata->edit_bmesh->bm; - Mesh *me_cage = rdata->mapped.me_cage; - - const MLoop *loops = rdata->mloop; - const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me_cage); - for (int i = 0; i < tri_len; i++) { - const MLoopTri *mlt = &mlooptri[i]; - const int p_orig = rdata->mapped.p_origindex[mlt->poly]; - if (p_orig != ORIGINDEX_NONE) { - /* Assume 'use_hide' */ - BMFace *efa = BM_face_at_index(bm, p_orig); - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - GPU_indexbuf_add_tri_verts( - &elb, loops[mlt->tri[0]].v, loops[mlt->tri[1]].v, loops[mlt->tri[2]].v); - } - } - } - } - - GPU_indexbuf_build_in_place(&elb, ibo); -} - -static void mesh_create_loops_lines(MeshRenderData *rdata, GPUIndexBuf *ibo, const bool use_hide) -{ - const int edge_len = mesh_render_data_edges_len_get(rdata); - const int loop_len = mesh_render_data_loops_len_get(rdata); - const int poly_len = mesh_render_data_polys_len_get(rdata); - - GPUIndexBufBuilder elb; - GPU_indexbuf_init(&elb, GPU_PRIM_LINES, edge_len, loop_len); - - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter; - BMEdge *bm_edge; - - BM_ITER_MESH (bm_edge, &iter, bm, BM_EDGES_OF_MESH) { - /* use_hide always for edit-mode */ - if (!BM_elem_flag_test(bm_edge, BM_ELEM_HIDDEN) && bm_edge->l != NULL) { - BMLoop *bm_loop1 = bm_vert_find_first_loop_visible_inline(bm_edge->v1); - BMLoop *bm_loop2 = bm_vert_find_first_loop_visible_inline(bm_edge->v2); - int v1 = BM_elem_index_get(bm_loop1); - int v2 = BM_elem_index_get(bm_loop2); - if (v1 > v2) { - SWAP(int, v1, v2); - } - GPU_indexbuf_add_line_verts(&elb, v1, v2); - } - } - } - else { - MLoop *mloop = (MLoop *)rdata->mloop; - MEdge *medge = (MEdge *)rdata->medge; - - /* Reset flag */ - for (int edge = 0; edge < edge_len; ++edge) { - /* NOTE: not thread safe. */ - medge[edge].flag &= ~ME_EDGE_TMP_TAG; - } - - for (int poly = 0; poly < poly_len; poly++) { - const MPoly *mp = &rdata->mpoly[poly]; - if (!(use_hide && (mp->flag & ME_HIDE))) { - for (int j = 0; j < mp->totloop; j++) { - MEdge *ed = (MEdge *)rdata->medge + mloop[mp->loopstart + j].e; - if ((ed->flag & ME_EDGE_TMP_TAG) == 0) { - ed->flag |= ME_EDGE_TMP_TAG; - int v1 = mp->loopstart + j; - int v2 = mp->loopstart + (j + 1) % mp->totloop; - GPU_indexbuf_add_line_verts(&elb, v1, v2); - } - } - } - } - } - } - - GPU_indexbuf_build_in_place(&elb, ibo); -} - -static void mesh_create_loops_lines_paint_mask(MeshRenderData *rdata, GPUIndexBuf *ibo) -{ - const int loop_len = mesh_render_data_loops_len_get(rdata); - const int poly_len = mesh_render_data_polys_len_get(rdata); - const int edge_len = mesh_render_data_edges_len_get(rdata); - - GPUIndexBufBuilder elb; - GPU_indexbuf_init(&elb, GPU_PRIM_LINES, loop_len, loop_len); - - if (rdata->edit_bmesh) { - /* painting does not use the edit_bmesh */ - BLI_assert(0); - } - else { - if (rdata->me->editflag & ME_EDIT_PAINT_FACE_SEL) { - /* Each edge has two bits used to count selected edges as 0, 1, 2+. */ - BLI_bitmap *edges_used = BLI_BITMAP_NEW(edge_len * 2, __func__); - - /* Fill the edge bitmap table. */ - for (int poly = 0; poly < poly_len; poly++) { - const MPoly *mpoly = &rdata->mpoly[poly]; - - /* Do not check faces that are hidden and faces that aren't selected */ - if (mpoly->flag & ME_HIDE || ((mpoly->flag & ME_FACE_SEL) == 0)) { - continue; - } - - for (int loop_index = mpoly->loopstart, loop_index_end = mpoly->loopstart + mpoly->totloop; - loop_index < loop_index_end; - loop_index++) { - const MLoop *mloop = &rdata->mloop[loop_index]; - const int e_a = mloop->e * 2; - const int e_b = e_a + 1; - if (!BLI_BITMAP_TEST(edges_used, e_a)) { - BLI_BITMAP_ENABLE(edges_used, e_a); - } - else { - BLI_BITMAP_ENABLE(edges_used, e_b); - } - } - } - - for (int poly = 0; poly < poly_len; poly++) { - const MPoly *mpoly = &rdata->mpoly[poly]; - if (!(mpoly->flag & ME_HIDE)) { - - for (int loop_index_next = mpoly->loopstart, - loop_index_end = mpoly->loopstart + mpoly->totloop, - loop_index_curr = loop_index_end - 1; - loop_index_next < loop_index_end; - loop_index_curr = loop_index_next++) { - const MLoop *mloop = &rdata->mloop[loop_index_curr]; - const int e_a = mloop->e * 2; - const int e_b = e_a + 1; - - /* Draw if a boundary or entirely unselected. */ - if (!BLI_BITMAP_TEST(edges_used, e_b)) { - GPU_indexbuf_add_line_verts(&elb, loop_index_curr, loop_index_next); - } - } - } - } - - MEM_freeN(edges_used); - } - else { - /* Add edges. */ - for (int poly = 0; poly < poly_len; poly++) { - const MPoly *mpoly = &rdata->mpoly[poly]; - for (int loop_index_next = mpoly->loopstart, - loop_index_end = mpoly->loopstart + mpoly->totloop, - loop_index_curr = loop_index_end - 1; - loop_index_next < loop_index_end; - loop_index_curr = loop_index_next++) { - GPU_indexbuf_add_line_verts(&elb, loop_index_curr, loop_index_next); - } - } - } - } - - GPU_indexbuf_build_in_place(&elb, ibo); -} - -static void mesh_create_loops_line_strips(MeshRenderData *rdata, - GPUIndexBuf *ibo, - const bool use_hide) -{ - const int loop_len = mesh_render_data_loops_len_get(rdata); - const int poly_len = mesh_render_data_polys_len_get(rdata); - - GPUIndexBufBuilder elb; - GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, loop_len + poly_len * 2, loop_len); - - uint v_index = 0; - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter; - BMFace *bm_face; - - BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) { - /* use_hide always for edit-mode */ - if (!BM_elem_flag_test(bm_face, BM_ELEM_HIDDEN)) { - for (int i = 0; i < bm_face->len; i++) { - GPU_indexbuf_add_generic_vert(&elb, v_index + i); - } - /* Finish loop and restart primitive. */ - GPU_indexbuf_add_generic_vert(&elb, v_index); - GPU_indexbuf_add_primitive_restart(&elb); - } - v_index += bm_face->len; - } - } - else { - for (int poly = 0; poly < poly_len; poly++) { - const MPoly *mp = &rdata->mpoly[poly]; - if (!(use_hide && (mp->flag & ME_HIDE))) { - const int loopend = mp->loopstart + mp->totloop; - for (int j = mp->loopstart; j < loopend; j++) { - GPU_indexbuf_add_generic_vert(&elb, j); - } - /* Finish loop and restart primitive. */ - GPU_indexbuf_add_generic_vert(&elb, mp->loopstart); - GPU_indexbuf_add_primitive_restart(&elb); - } - v_index += mp->totloop; - } - } - } - else { - /* Implement ... eventually if needed. */ - BLI_assert(0); - } - - GPU_indexbuf_build_in_place(&elb, ibo); -} - -static void mesh_create_loose_edges_lines(MeshRenderData *rdata, - GPUIndexBuf *ibo, - bool *r_no_loose_wire, - const bool use_hide) -{ - const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata); - const int edge_len = mesh_render_data_edges_len_get_maybe_mapped(rdata); - - /* Alloc max (edge_len) and upload only needed range. */ - GPUIndexBufBuilder elb; - GPU_indexbuf_init(&elb, GPU_PRIM_LINES, edge_len, vert_len); - - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - /* No need to support since edit mesh already draw them. - * But some engines may want them ... */ - BMesh *bm = rdata->edit_bmesh->bm; - BMIter eiter; - BMEdge *eed; - BM_ITER_MESH (eed, &eiter, bm, BM_EDGES_OF_MESH) { - if (bm_edge_is_loose_and_visible(eed)) { - GPU_indexbuf_add_line_verts( - &elb, BM_elem_index_get(eed->v1), BM_elem_index_get(eed->v2)); - } - } - } - else { - for (int i = 0; i < edge_len; i++) { - const MEdge *medge = &rdata->medge[i]; - if ((medge->flag & ME_LOOSEEDGE) && !(use_hide && (medge->flag & ME_HIDE))) { - GPU_indexbuf_add_line_verts(&elb, medge->v1, medge->v2); - } - } - } - } - else { - /* Hidden checks are already done when creating the loose edge list. */ - Mesh *me_cage = rdata->mapped.me_cage; - for (int i_iter = 0; i_iter < rdata->mapped.loose_edge_len; i_iter++) { - const int i = rdata->mapped.loose_edges[i_iter]; - const MEdge *medge = &me_cage->medge[i]; - GPU_indexbuf_add_line_verts(&elb, medge->v1, medge->v2); - } - } - - *r_no_loose_wire = (elb.index_len == 0); - - GPU_indexbuf_build_in_place(&elb, ibo); -} - -static void mesh_create_loops_tris(MeshRenderData *rdata, - GPUIndexBuf **ibo, - int ibo_len, - const bool use_hide) -{ - const int loop_len = mesh_render_data_loops_len_get(rdata); - const int tri_len = mesh_render_data_looptri_len_get(rdata); - - GPUIndexBufBuilder *elb = BLI_array_alloca(elb, ibo_len); - - for (int i = 0; i < ibo_len; ++i) { - /* TODO alloc minmum necessary. */ - GPU_indexbuf_init(&elb[i], GPU_PRIM_TRIS, tri_len, loop_len * 3); - } - - if (rdata->mapped.use == false) { - if (rdata->edit_bmesh) { - for (int i = 0; i < tri_len; i++) { - const BMLoop **bm_looptri = (const BMLoop **)rdata->edit_bmesh->looptris[i]; - const BMFace *bm_face = bm_looptri[0]->f; - /* use_hide always for edit-mode */ - if (BM_elem_flag_test(bm_face, BM_ELEM_HIDDEN)) { - continue; - } - int mat = min_ii(ibo_len - 1, bm_face->mat_nr); - GPU_indexbuf_add_tri_verts(&elb[mat], - BM_elem_index_get(bm_looptri[0]), - BM_elem_index_get(bm_looptri[1]), - BM_elem_index_get(bm_looptri[2])); - } - } - else { - for (int i = 0; i < tri_len; i++) { - const MLoopTri *mlt = &rdata->mlooptri[i]; - const MPoly *mp = &rdata->mpoly[mlt->poly]; - if (use_hide && (mp->flag & ME_HIDE)) { - continue; - } - int mat = min_ii(ibo_len - 1, mp->mat_nr); - GPU_indexbuf_add_tri_verts(&elb[mat], mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } - } - } - else { - /* Note: mapped doesn't support lnors yet. */ - BMesh *bm = rdata->edit_bmesh->bm; - Mesh *me_cage = rdata->mapped.me_cage; - - const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me_cage); - for (int i = 0; i < tri_len; i++) { - const MLoopTri *mlt = &mlooptri[i]; - const int p_orig = rdata->mapped.p_origindex[mlt->poly]; - if (p_orig != ORIGINDEX_NONE) { - /* Assume 'use_hide' */ - BMFace *efa = BM_face_at_index(bm, p_orig); - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - int mat = min_ii(ibo_len - 1, efa->mat_nr); - GPU_indexbuf_add_tri_verts(&elb[mat], mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } - } - } - } - - for (int i = 0; i < ibo_len; ++i) { - GPU_indexbuf_build_in_place(&elb[i], ibo[i]); - } -} - -/* Warning! this function is not thread safe! - * It writes to MEdge->flag with ME_EDGE_TMP_TAG. */ -static void mesh_create_edit_loops_points_lines(MeshRenderData *rdata, - GPUIndexBuf *ibo_verts, - GPUIndexBuf *ibo_edges) -{ - BMIter iter; - int i; - - const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata); - const int edge_len = mesh_render_data_edges_len_get_maybe_mapped(rdata); - const int loop_len = mesh_render_data_loops_len_get_maybe_mapped(rdata); - const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata); - const int lvert_len = mesh_render_data_loose_verts_len_get_maybe_mapped(rdata); - const int ledge_len = mesh_render_data_loose_edges_len_get_maybe_mapped(rdata); - const int tot_loop_len = loop_len + ledge_len * 2 + lvert_len; - - GPUIndexBufBuilder elb_vert, elb_edge; - if (DRW_TEST_ASSIGN_IBO(ibo_edges)) { - GPU_indexbuf_init(&elb_edge, GPU_PRIM_LINES, edge_len, tot_loop_len); - } - if (DRW_TEST_ASSIGN_IBO(ibo_verts)) { - GPU_indexbuf_init(&elb_vert, GPU_PRIM_POINTS, tot_loop_len, tot_loop_len); - } - - int loop_idx = 0; - if (rdata->edit_bmesh && (rdata->mapped.use == false)) { - BMesh *bm = rdata->edit_bmesh->bm; - /* Edges not loose. */ - if (ibo_edges) { - BMEdge *eed; - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - BMLoop *l = bm_edge_find_first_loop_visible_inline(eed); - if (l != NULL) { - int v1 = BM_elem_index_get(eed->l); - int v2 = BM_elem_index_get(eed->l->next); - GPU_indexbuf_add_line_verts(&elb_edge, v1, v2); - } - } - } - } - /* Face Loops */ - if (ibo_verts) { - BMVert *eve; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - BMLoop *l = bm_vert_find_first_loop_visible_inline(eve); - if (l != NULL) { - int v = BM_elem_index_get(l); - GPU_indexbuf_add_generic_vert(&elb_vert, v); - } - } - } - } - loop_idx = loop_len; - /* Loose edges */ - for (i = 0; i < ledge_len; ++i) { - if (ibo_verts) { - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + 0); - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + 1); - } - if (ibo_edges) { - GPU_indexbuf_add_line_verts(&elb_edge, loop_idx + 0, loop_idx + 1); - } - loop_idx += 2; - } - /* Loose verts */ - if (ibo_verts) { - for (i = 0; i < lvert_len; ++i) { - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx); - loop_idx += 1; - } - } - } - else if (rdata->mapped.use) { - const MPoly *mpoly = rdata->mapped.me_cage->mpoly; - MVert *mvert = rdata->mapped.me_cage->mvert; - MEdge *medge = rdata->mapped.me_cage->medge; - BMesh *bm = rdata->edit_bmesh->bm; - - const int *v_origindex = rdata->mapped.v_origindex; - const int *e_origindex = rdata->mapped.e_origindex; - const int *p_origindex = rdata->mapped.p_origindex; - - /* Reset flag */ - for (int edge = 0; edge < edge_len; ++edge) { - /* NOTE: not thread safe. */ - medge[edge].flag &= ~ME_EDGE_TMP_TAG; - } - for (int vert = 0; vert < vert_len; ++vert) { - /* NOTE: not thread safe. */ - mvert[vert].flag &= ~ME_VERT_TMP_TAG; - } - - /* Face Loops */ - for (int poly = 0; poly < poly_len; poly++, mpoly++) { - int fidx = p_origindex[poly]; - if (fidx != ORIGINDEX_NONE) { - BMFace *efa = BM_face_at_index(bm, fidx); - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - const MLoop *mloop = &rdata->mapped.me_cage->mloop[mpoly->loopstart]; - for (i = 0; i < mpoly->totloop; ++i, ++mloop) { - if (ibo_verts && (v_origindex[mloop->v] != ORIGINDEX_NONE) && - (mvert[mloop->v].flag & ME_VERT_TMP_TAG) == 0) { - mvert[mloop->v].flag |= ME_VERT_TMP_TAG; - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + i); - } - if (ibo_edges && (e_origindex[mloop->e] != ORIGINDEX_NONE) && - ((medge[mloop->e].flag & ME_EDGE_TMP_TAG) == 0)) { - medge[mloop->e].flag |= ME_EDGE_TMP_TAG; - int v1 = loop_idx + i; - int v2 = loop_idx + ((i + 1) % mpoly->totloop); - GPU_indexbuf_add_line_verts(&elb_edge, v1, v2); - } - } - } - } - loop_idx += mpoly->totloop; - } - /* Loose edges */ - for (i = 0; i < ledge_len; ++i) { - int eidx = e_origindex[rdata->mapped.loose_edges[i]]; - if (eidx != ORIGINDEX_NONE) { - if (ibo_verts) { - const MEdge *ed = &medge[rdata->mapped.loose_edges[i]]; - if (v_origindex[ed->v1] != ORIGINDEX_NONE) { - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + 0); - } - if (v_origindex[ed->v2] != ORIGINDEX_NONE) { - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + 1); - } - } - if (ibo_edges) { - GPU_indexbuf_add_line_verts(&elb_edge, loop_idx + 0, loop_idx + 1); - } - } - loop_idx += 2; - } - /* Loose verts */ - if (ibo_verts) { - for (i = 0; i < lvert_len; ++i) { - int vidx = v_origindex[rdata->mapped.loose_verts[i]]; - if (vidx != ORIGINDEX_NONE) { - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx); - } - loop_idx += 1; - } - } - } - else { - const MPoly *mpoly = rdata->mpoly; - - /* Face Loops */ - for (int poly = 0; poly < poly_len; poly++, mpoly++) { - if ((mpoly->flag & ME_HIDE) == 0) { - for (i = 0; i < mpoly->totloop; ++i) { - if (ibo_verts) { - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + i); - } - if (ibo_edges) { - int v1 = loop_idx + i; - int v2 = loop_idx + ((i + 1) % mpoly->totloop); - GPU_indexbuf_add_line_verts(&elb_edge, v1, v2); - } - } - } - loop_idx += mpoly->totloop; - } - /* TODO(fclem): Until we find a way to detect - * loose verts easily outside of edit mode, this - * will remain disabled. */ -#if 0 - /* Loose edges */ - for (int e = 0; e < edge_len; e++, medge++) { - if (medge->flag & ME_LOOSEEDGE) { - int eidx = e_origindex[e]; - if (eidx != ORIGINDEX_NONE) { - if ((medge->flag & ME_HIDE) == 0) { - for (int j = 0; j < 2; ++j) { - if (ibo_verts) { - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + j); - } - if (ibo_edges) { - GPU_indexbuf_add_generic_vert(&elb_edge, loop_idx + j); - } - } - } - } - loop_idx += 2; - } - } - /* Loose verts */ - for (int v = 0; v < vert_len; v++, mvert++) { - int vidx = v_origindex[v]; - if (vidx != ORIGINDEX_NONE) { - if ((mvert->flag & ME_HIDE) == 0) { - if (ibo_verts) { - GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx); - } - if (ibo_edges) { - GPU_indexbuf_add_generic_vert(&elb_edge, loop_idx); - } - } - loop_idx += 1; - } - } -#endif - } - - if (ibo_verts) { - GPU_indexbuf_build_in_place(&elb_vert, ibo_verts); - } - if (ibo_edges) { - GPU_indexbuf_build_in_place(&elb_edge, ibo_edges); - } -} - -static void mesh_create_edit_loops_tris(MeshRenderData *rdata, GPUIndexBuf *ibo) -{ - const int loop_len = mesh_render_data_loops_len_get_maybe_mapped(rdata); - const int tri_len = mesh_render_data_looptri_len_get_maybe_mapped(rdata); - - GPUIndexBufBuilder elb; - /* TODO alloc minmum necessary. */ - GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tri_len, loop_len * 3); - - if (rdata->edit_bmesh && (rdata->mapped.use == false)) { - for (int i = 0; i < tri_len; i++) { - const BMLoop **bm_looptri = (const BMLoop **)rdata->edit_bmesh->looptris[i]; - const BMFace *bm_face = bm_looptri[0]->f; - /* use_hide always for edit-mode */ - if (BM_elem_flag_test(bm_face, BM_ELEM_HIDDEN)) { - continue; - } - GPU_indexbuf_add_tri_verts(&elb, - BM_elem_index_get(bm_looptri[0]), - BM_elem_index_get(bm_looptri[1]), - BM_elem_index_get(bm_looptri[2])); - } - } - else if (rdata->mapped.use == true) { - BMesh *bm = rdata->edit_bmesh->bm; - Mesh *me_cage = rdata->mapped.me_cage; - - const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me_cage); - for (int i = 0; i < tri_len; i++) { - const MLoopTri *mlt = &mlooptri[i]; - const int p_orig = rdata->mapped.p_origindex[mlt->poly]; - if (p_orig != ORIGINDEX_NONE) { - /* Assume 'use_hide' */ - BMFace *efa = BM_face_at_index(bm, p_orig); - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - GPU_indexbuf_add_tri_verts(&elb, mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } - } - } - } - else { - const MLoopTri *mlt = rdata->mlooptri; - for (int i = 0; i < tri_len; i++, mlt++) { - const MPoly *mpoly = &rdata->mpoly[mlt->poly]; - /* Assume 'use_hide' */ - if ((mpoly->flag & ME_HIDE) == 0) { - GPU_indexbuf_add_tri_verts(&elb, mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } - } - } - - GPU_indexbuf_build_in_place(&elb, ibo); -} - /** \} */ /* ---------------------------------------------------------------------- */ @@ -4563,9 +753,9 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, *auto_layer_count = cache->auto_layer_len; } for (int i = 0; i < cache->mat_len; ++i) { - DRW_batch_request(&cache->surf_per_mat[i]); + DRW_batch_request(&cache->surface_per_mat[i]); } - return cache->surf_per_mat; + return cache->surface_per_mat; } GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me) @@ -4574,9 +764,9 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me) mesh_batch_cache_add_request(cache, MBC_SURF_PER_MAT); texpaint_request_active_uv(cache, me); for (int i = 0; i < cache->mat_len; ++i) { - DRW_batch_request(&cache->surf_per_mat[i]); + DRW_batch_request(&cache->surface_per_mat[i]); } - return cache->surf_per_mat; + return cache->surface_per_mat; } GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me) @@ -4622,6 +812,13 @@ GPUBatch *DRW_mesh_batch_cache_get_edit_vertices(Mesh *me) return DRW_batch_request(&cache->batch.edit_vertices); } +GPUBatch *DRW_mesh_batch_cache_get_edit_vnors(Mesh *me) +{ + MeshBatchCache *cache = mesh_batch_cache_get(me); + mesh_batch_cache_add_request(cache, MBC_EDIT_VNOR); + return DRW_batch_request(&cache->batch.edit_vnor); +} + GPUBatch *DRW_mesh_batch_cache_get_edit_lnors(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); @@ -4633,7 +830,7 @@ GPUBatch *DRW_mesh_batch_cache_get_edit_facedots(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); mesh_batch_cache_add_request(cache, MBC_EDIT_FACEDOTS); - return DRW_batch_request(&cache->batch.edit_facedots); + return DRW_batch_request(&cache->batch.edit_fdots); } /** \} */ @@ -4653,7 +850,7 @@ GPUBatch *DRW_mesh_batch_cache_get_facedots_with_select_id(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); mesh_batch_cache_add_request(cache, MBC_EDIT_SELECTION_FACEDOTS); - return DRW_batch_request(&cache->batch.edit_selection_facedots); + return DRW_batch_request(&cache->batch.edit_selection_fdots); } GPUBatch *DRW_mesh_batch_cache_get_edges_with_select_id(Mesh *me) @@ -4679,6 +876,7 @@ GPUBatch *DRW_mesh_batch_cache_get_verts_with_select_id(Mesh *me) GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_strech_area(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); + texpaint_request_active_uv(cache, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES_STRECH_AREA); return DRW_batch_request(&cache->batch.edituv_faces_strech_area); } @@ -4686,6 +884,7 @@ GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_strech_area(Mesh *me) GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_strech_angle(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); + texpaint_request_active_uv(cache, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES_STRECH_ANGLE); return DRW_batch_request(&cache->batch.edituv_faces_strech_angle); } @@ -4693,6 +892,7 @@ GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_strech_angle(Mesh *me) GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); + texpaint_request_active_uv(cache, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES); return DRW_batch_request(&cache->batch.edituv_faces); } @@ -4700,6 +900,7 @@ GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(Mesh *me) GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); + texpaint_request_active_uv(cache, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_EDGES); return DRW_batch_request(&cache->batch.edituv_edges); } @@ -4707,6 +908,7 @@ GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(Mesh *me) GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); + texpaint_request_active_uv(cache, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_VERTS); return DRW_batch_request(&cache->batch.edituv_verts); } @@ -4714,8 +916,9 @@ GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(Mesh *me) GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); + texpaint_request_active_uv(cache, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACEDOTS); - return DRW_batch_request(&cache->batch.edituv_facedots); + return DRW_batch_request(&cache->batch.edituv_fdots); } GPUBatch *DRW_mesh_batch_cache_get_uv_edges(Mesh *me) @@ -4729,356 +932,11 @@ GPUBatch *DRW_mesh_batch_cache_get_uv_edges(Mesh *me) GPUBatch *DRW_mesh_batch_cache_get_surface_edges(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); + texpaint_request_active_uv(cache, me); mesh_batch_cache_add_request(cache, MBC_WIRE_LOOPS); return DRW_batch_request(&cache->batch.wire_loops); } -/* Compute 3D & 2D areas and their sum. */ -BLI_INLINE void edit_uv_preprocess_stretch_area(BMFace *efa, - const int cd_loop_uv_offset, - uint fidx, - float *totarea, - float *totuvarea, - float (*faces_areas)[2]) -{ - faces_areas[fidx][0] = BM_face_calc_area(efa); - faces_areas[fidx][1] = BM_face_calc_area_uv(efa, cd_loop_uv_offset); - - *totarea += faces_areas[fidx][0]; - *totuvarea += faces_areas[fidx][1]; -} - -BLI_INLINE float edit_uv_get_stretch_area(float area, float uvarea) -{ - if (area < FLT_EPSILON || uvarea < FLT_EPSILON) { - return 1.0f; - } - else if (area > uvarea) { - return 1.0f - (uvarea / area); - } - else { - return 1.0f - (area / uvarea); - } -} - -/* Compute face's normalized contour vectors. */ -BLI_INLINE void edit_uv_preprocess_stretch_angle(float (*auv)[2], - float (*av)[3], - const int cd_loop_uv_offset, - BMFace *efa) -{ - BMLoop *l; - BMIter liter; - int i; - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset); - - sub_v2_v2v2(auv[i], luv_prev->uv, luv->uv); - normalize_v2(auv[i]); - - sub_v3_v3v3(av[i], l->prev->v->co, l->v->co); - normalize_v3(av[i]); - } -} - -#if 0 /* here for reference, this is done in shader now. */ -BLI_INLINE float edit_uv_get_loop_stretch_angle(const float auv0[2], - const float auv1[2], - const float av0[3], - const float av1[3]) -{ - float uvang = angle_normalized_v2v2(auv0, auv1); - float ang = angle_normalized_v3v3(av0, av1); - float stretch = fabsf(uvang - ang) / (float)M_PI; - return 1.0f - pow2f(1.0f - stretch); -} -#endif - -static struct EditUVFormatIndex { - uint area, angle, uv_adj, flag, fdots_uvs, fdots_flag; -} uv_attr_id = {0}; - -static void uvedit_fill_buffer_data(MeshRenderData *rdata, - GPUVertBuf *vbo_area, - GPUVertBuf *vbo_angle, - GPUVertBuf *vbo_fdots_pos, - GPUVertBuf *vbo_fdots_data, - GPUIndexBufBuilder *elb_vert, - GPUIndexBufBuilder *elb_edge, - GPUIndexBufBuilder *elb_face) -{ - BMesh *bm = rdata->edit_bmesh->bm; - BMIter iter, liter; - BMFace *efa; - uint vidx, fidx, fdot_idx, i; - const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata); - float(*faces_areas)[2] = NULL; - float totarea = 0.0f, totuvarea = 0.0f; - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - - BLI_buffer_declare_static(vec3f, vec3_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE); - BLI_buffer_declare_static(vec2f, vec2_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE); - - if (vbo_area) { - faces_areas = MEM_mallocN(sizeof(float) * 2 * bm->totface, "EDITUV faces areas"); - } - - /* Preprocess */ - fidx = 0; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - /* Tag hidden faces */ - BM_elem_flag_set(efa, BM_ELEM_TAG, uvedit_face_visible_nolocal_ex(rdata->toolsettings, efa)); - - if (vbo_area) { - edit_uv_preprocess_stretch_area( - efa, cd_loop_uv_offset, fidx++, &totarea, &totuvarea, faces_areas); - } - } - - vidx = 0; - fidx = 0; - fdot_idx = 0; - if (rdata->mapped.use == false && rdata->edit_bmesh) { - BMLoop *l; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - const bool face_visible = BM_elem_flag_test(efa, BM_ELEM_TAG); - const int efa_len = efa->len; - float fdot[2] = {0.0f, 0.0f}; - float(*av)[3], (*auv)[2]; - ushort area_stretch; - - /* Face preprocess */ - if (vbo_area) { - area_stretch = edit_uv_get_stretch_area(faces_areas[fidx][0] / totarea, - faces_areas[fidx][1] / totuvarea) * - 65534.0f; - } - if (vbo_angle) { - av = (float(*)[3])BLI_buffer_reinit_data(&vec3_buf, vec3f, efa_len); - auv = (float(*)[2])BLI_buffer_reinit_data(&vec2_buf, vec2f, efa_len); - edit_uv_preprocess_stretch_angle(auv, av, cd_loop_uv_offset, efa); - } - - /* Skip hidden faces. */ - if (face_visible) { - if (elb_face) { - for (i = 0; i < efa->len; ++i) { - GPU_indexbuf_add_generic_vert(elb_face, vidx + i); - } - } - if (elb_vert) { - for (i = 0; i < efa->len; ++i) { - GPU_indexbuf_add_generic_vert(elb_vert, vidx + i); - } - } - if (elb_edge) { - for (i = 0; i < efa->len; ++i) { - GPU_indexbuf_add_line_verts(elb_edge, vidx + i, vidx + (i + 1) % efa->len); - } - } - } - - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (vbo_area) { - GPU_vertbuf_attr_set(vbo_area, uv_attr_id.area, vidx, &area_stretch); - } - if (vbo_angle) { - int i_next = (i + 1) % efa_len; - short suv[4]; - /* Send uvs to the shader and let it compute the aspect corrected angle. */ - normal_float_to_short_v2(&suv[0], auv[i]); - normal_float_to_short_v2(&suv[2], auv[i_next]); - GPU_vertbuf_attr_set(vbo_angle, uv_attr_id.uv_adj, vidx, suv); - /* Compute 3D angle here */ - short angle = 32767.0f * angle_normalized_v3v3(av[i], av[i_next]) / (float)M_PI; - GPU_vertbuf_attr_set(vbo_angle, uv_attr_id.angle, vidx, &angle); - } - if (vbo_fdots_pos) { - add_v2_v2(fdot, luv->uv); - } - vidx++; - } - - if (elb_face && face_visible) { - GPU_indexbuf_add_generic_vert(elb_face, vidx - efa->len); - GPU_indexbuf_add_primitive_restart(elb_face); - } - if (vbo_fdots_pos && face_visible) { - mul_v2_fl(fdot, 1.0f / (float)efa->len); - GPU_vertbuf_attr_set(vbo_fdots_pos, uv_attr_id.fdots_uvs, fdot_idx, fdot); - } - if (vbo_fdots_data && face_visible) { - uchar face_flag = mesh_render_data_face_flag(rdata, efa, cd_loop_uv_offset); - GPU_vertbuf_attr_set(vbo_fdots_data, uv_attr_id.fdots_flag, fdot_idx, &face_flag); - } - fdot_idx += face_visible ? 1 : 0; - fidx++; - } - } - else { - const MPoly *mpoly = rdata->mapped.me_cage->mpoly; - // const MEdge *medge = rdata->mapped.me_cage->medge; - // const MVert *mvert = rdata->mapped.me_cage->mvert; - const MLoop *mloop = rdata->mapped.me_cage->mloop; - - const int *v_origindex = rdata->mapped.v_origindex; - const int *e_origindex = rdata->mapped.e_origindex; - const int *p_origindex = rdata->mapped.p_origindex; - - /* Face Loops */ - for (int poly = 0; poly < poly_len; poly++, mpoly++) { - float fdot[2] = {0.0f, 0.0f}; - const MLoop *l = &mloop[mpoly->loopstart]; - int fidx_ori = p_origindex[poly]; - efa = (fidx_ori != ORIGINDEX_NONE) ? BM_face_at_index(bm, fidx_ori) : NULL; - const bool face_visible = efa != NULL && BM_elem_flag_test(efa, BM_ELEM_TAG); - if (efa && vbo_fdots_data) { - uchar face_flag = mesh_render_data_face_flag(rdata, efa, cd_loop_uv_offset); - GPU_vertbuf_attr_set(vbo_fdots_data, uv_attr_id.fdots_flag, fdot_idx, &face_flag); - } - /* Skip hidden faces. */ - if (face_visible) { - if (elb_face) { - for (i = 0; i < mpoly->totloop; ++i) { - GPU_indexbuf_add_generic_vert(elb_face, vidx + i); - } - GPU_indexbuf_add_generic_vert(elb_face, vidx); - GPU_indexbuf_add_primitive_restart(elb_face); - } - if (elb_edge) { - for (i = 0; i < mpoly->totloop; ++i) { - if (e_origindex[l[i].e] != ORIGINDEX_NONE) { - GPU_indexbuf_add_line_verts(elb_edge, vidx + i, vidx + (i + 1) % mpoly->totloop); - } - } - } - if (elb_vert) { - for (i = 0; i < mpoly->totloop; ++i) { - if (v_origindex[l[i].v] != ORIGINDEX_NONE) { - GPU_indexbuf_add_generic_vert(elb_vert, vidx + i); - } - } - } - } - for (i = 0; i < mpoly->totloop; i++, l++) { - /* TODO support stretch. */ - if (vbo_fdots_pos) { - MLoopUV *luv = &rdata->mloopuv[mpoly->loopstart + i]; - add_v2_v2(fdot, luv->uv); - } - vidx++; - } - if (vbo_fdots_pos && face_visible) { - mul_v2_fl(fdot, 1.0f / mpoly->totloop); - GPU_vertbuf_attr_set(vbo_fdots_pos, uv_attr_id.fdots_uvs, fdot_idx, fdot); - } - fidx++; - fdot_idx += face_visible ? 1 : 0; - } - } - - if (faces_areas) { - MEM_freeN(faces_areas); - } - - BLI_buffer_free(&vec3_buf); - BLI_buffer_free(&vec2_buf); - - if (fdot_idx < poly_len) { - if (vbo_fdots_pos) { - GPU_vertbuf_data_resize(vbo_fdots_pos, fdot_idx); - } - if (vbo_fdots_data) { - GPU_vertbuf_data_resize(vbo_fdots_data, fdot_idx); - } - } -} - -static void mesh_create_uvedit_buffers(MeshRenderData *rdata, - GPUVertBuf *vbo_area, - GPUVertBuf *vbo_angle, - GPUVertBuf *vbo_fdots_pos, - GPUVertBuf *vbo_fdots_data, - GPUIndexBuf *ibo_vert, - GPUIndexBuf *ibo_edge, - GPUIndexBuf *ibo_face) -{ - static GPUVertFormat format_area = {0}; - static GPUVertFormat format_angle = {0}; - static GPUVertFormat format_fdots_pos = {0}; - static GPUVertFormat format_fdots_flag = {0}; - - if (format_area.attr_len == 0) { - uv_attr_id.area = GPU_vertformat_attr_add( - &format_area, "stretch", GPU_COMP_U16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - uv_attr_id.angle = GPU_vertformat_attr_add( - &format_angle, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - uv_attr_id.uv_adj = GPU_vertformat_attr_add( - &format_angle, "uv_adj", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - uv_attr_id.fdots_flag = GPU_vertformat_attr_add( - &format_fdots_flag, "flag", GPU_COMP_U8, 1, GPU_FETCH_INT); - uv_attr_id.fdots_uvs = GPU_vertformat_attr_add( - &format_fdots_pos, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_vertformat_alias_add(&format_fdots_pos, "pos"); - } - - const int loop_len = mesh_render_data_loops_len_get_maybe_mapped(rdata); - const int face_len = mesh_render_data_polys_len_get_maybe_mapped(rdata); - const int idx_len = loop_len + face_len * 2; - - if (DRW_TEST_ASSIGN_VBO(vbo_area)) { - GPU_vertbuf_init_with_format(vbo_area, &format_area); - GPU_vertbuf_data_alloc(vbo_area, loop_len); - } - if (DRW_TEST_ASSIGN_VBO(vbo_angle)) { - GPU_vertbuf_init_with_format(vbo_angle, &format_angle); - GPU_vertbuf_data_alloc(vbo_angle, loop_len); - } - if (DRW_TEST_ASSIGN_VBO(vbo_fdots_pos)) { - GPU_vertbuf_init_with_format(vbo_fdots_pos, &format_fdots_pos); - GPU_vertbuf_data_alloc(vbo_fdots_pos, face_len); - } - if (DRW_TEST_ASSIGN_VBO(vbo_fdots_data)) { - GPU_vertbuf_init_with_format(vbo_fdots_data, &format_fdots_flag); - GPU_vertbuf_data_alloc(vbo_fdots_data, face_len); - } - - GPUIndexBufBuilder elb_vert, elb_edge, elb_face; - if (DRW_TEST_ASSIGN_IBO(ibo_vert)) { - GPU_indexbuf_init_ex(&elb_vert, GPU_PRIM_POINTS, loop_len, loop_len); - } - if (DRW_TEST_ASSIGN_IBO(ibo_edge)) { - GPU_indexbuf_init_ex(&elb_edge, GPU_PRIM_LINES, loop_len * 2, loop_len); - } - if (DRW_TEST_ASSIGN_IBO(ibo_face)) { - GPU_indexbuf_init_ex(&elb_face, GPU_PRIM_TRI_FAN, idx_len, loop_len); - } - - uvedit_fill_buffer_data(rdata, - vbo_area, - vbo_angle, - vbo_fdots_pos, - vbo_fdots_data, - ibo_vert ? &elb_vert : NULL, - ibo_edge ? &elb_edge : NULL, - ibo_face ? &elb_face : NULL); - - if (ibo_vert) { - GPU_indexbuf_build_in_place(&elb_vert, ibo_vert); - } - - if (ibo_edge) { - GPU_indexbuf_build_in_place(&elb_edge, ibo_edge); - } - - if (ibo_face) { - GPU_indexbuf_build_in_place(&elb_face, ibo_face); - } -} - /** \} */ /* ---------------------------------------------------------------------- */ @@ -5109,8 +967,12 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) /* Can be called for any surface type. Mesh *me is the final mesh. */ void DRW_mesh_batch_cache_create_requested( - Object *ob, Mesh *me, const ToolSettings *ts, const bool is_paint_mode, const bool use_hide) + Object *ob, Mesh *me, const Scene *scene, const bool is_paint_mode, const bool use_hide) { + const ToolSettings *ts = NULL; + if (scene) { + ts = scene->toolsettings; + } MeshBatchCache *cache = mesh_batch_cache_get(me); /* Early out */ @@ -5136,15 +998,13 @@ void DRW_mesh_batch_cache_create_requested( } } - if (batch_requested & (MBC_SURFACE | MBC_SURF_PER_MAT | MBC_WIRE_LOOPS_UVS)) { + if (batch_requested & + (MBC_SURFACE | MBC_SURF_PER_MAT | MBC_WIRE_LOOPS_UVS | MBC_EDITUV_FACES_STRECH_AREA | + MBC_EDITUV_FACES_STRECH_ANGLE | MBC_EDITUV_FACES | MBC_EDITUV_EDGES | MBC_EDITUV_VERTS)) { /* Modifiers will only generate an orco layer if the mesh is deformed. */ if (cache->cd_needed.orco != 0) { - if (CustomData_get_layer(&me->vdata, CD_ORCO) != NULL) { - /* Orco layer is needed. */ - } - else if (cache->cd_needed.tan_orco == 0) { - /* Skip orco calculation if not needed by tangent generation. - */ + if (CustomData_get_layer(&me->vdata, CD_ORCO) == NULL) { + /* Skip orco calculation */ cache->cd_needed.orco = 0; } } @@ -5155,21 +1015,26 @@ void DRW_mesh_batch_cache_create_requested( * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); if (cd_overlap == false) { - if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv || - (cache->cd_used.tan & cache->cd_needed.tan) != cache->cd_needed.tan || - cache->cd_used.tan_orco != cache->cd_needed.tan_orco) { - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_uv_tan); - } - if (cache->cd_used.orco != cache->cd_needed.orco) { - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_orco); - } - if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { - GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_vcol); + FOREACH_MESH_BUFFER_CACHE(cache, mbuffercache) + { + if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.uv); + } + if ((cache->cd_used.tan & cache->cd_needed.tan) != cache->cd_needed.tan || + cache->cd_used.tan_orco != cache->cd_needed.tan_orco) { + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.tan); + } + if (cache->cd_used.orco != cache->cd_needed.orco) { + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.orco); + } + if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.vcol); + } } /* We can't discard batches at this point as they have been * referenced for drawing. Just clear them in place. */ for (int i = 0; i < cache->mat_len; ++i) { - GPU_BATCH_CLEAR_SAFE(cache->surf_per_mat[i]); + GPU_BATCH_CLEAR_SAFE(cache->surface_per_mat[i]); } GPU_BATCH_CLEAR_SAFE(cache->batch.surface); cache->batch_ready &= ~(MBC_SURFACE | MBC_SURF_PER_MAT); @@ -5186,22 +1051,26 @@ void DRW_mesh_batch_cache_create_requested( const bool is_uvsyncsel = (ts->uv_flag & UV_SYNC_SELECTION); if (cache->is_uvsyncsel != is_uvsyncsel) { cache->is_uvsyncsel = is_uvsyncsel; - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_uv_data); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_stretch_angle); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_stretch_area); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_uv); - GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_uv); - GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_tri_fans); - GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_line_strips); - GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_points); + FOREACH_MESH_BUFFER_CACHE(cache, mbuffercache) + { + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.edituv_data); + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.stretch_angle); + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.stretch_area); + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.uv); + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.fdots_uv); + GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_tris); + GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_lines); + GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_points); + } /* We only clear the batches as they may already have been * referenced. */ + GPU_BATCH_CLEAR_SAFE(cache->batch.wire_loops_uvs); GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces_strech_area); GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces_strech_angle); GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces); GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_edges); GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_verts); - GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_facedots); + GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_fdots); cache->batch_ready &= ~MBC_EDITUV; } } @@ -5217,410 +1086,226 @@ void DRW_mesh_batch_cache_create_requested( cache->batch_ready |= batch_requested; + const bool do_cage = (me->edit_mesh && + (me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage)); + + const bool do_uvcage = me->edit_mesh && !me->edit_mesh->mesh_eval_final->runtime.is_original; + + MeshBufferCache *mbufcache = &cache->final; + /* Init batches and request VBOs & IBOs */ if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.surface, &cache->ibo.loops_tris); - DRW_vbo_request(cache->batch.surface, &cache->ordered.loop_pos_nor); - /* For paint overlay. Active layer should have been queried. */ + DRW_ibo_request(cache->batch.surface, &mbufcache->ibo.tris); + /* Order matters. First ones override latest vbos' attribs. */ + DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.lnor); + DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.pos_nor); if (cache->cd_used.uv != 0) { - DRW_vbo_request(cache->batch.surface, &cache->ordered.loop_uv_tan); + DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.uv); } if (cache->cd_used.vcol != 0) { - DRW_vbo_request(cache->batch.surface, &cache->ordered.loop_vcol); + DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.vcol); } } if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) { - DRW_vbo_request(cache->batch.all_verts, &cache->ordered.pos_nor); + DRW_vbo_request(cache->batch.all_verts, &mbufcache->vbo.pos_nor); } if (DRW_batch_requested(cache->batch.all_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.all_edges, &cache->ibo.edges_lines); - DRW_vbo_request(cache->batch.all_edges, &cache->ordered.pos_nor); + DRW_ibo_request(cache->batch.all_edges, &mbufcache->ibo.lines); + DRW_vbo_request(cache->batch.all_edges, &mbufcache->vbo.pos_nor); } if (DRW_batch_requested(cache->batch.loose_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.loose_edges, &cache->ibo.loose_edges_lines); - DRW_vbo_request(cache->batch.loose_edges, &cache->ordered.pos_nor); + DRW_ibo_request(cache->batch.loose_edges, &mbufcache->ibo.lines); + DRW_vbo_request(cache->batch.loose_edges, &mbufcache->vbo.pos_nor); } if (DRW_batch_requested(cache->batch.edge_detection, GPU_PRIM_LINES_ADJ)) { - DRW_ibo_request(cache->batch.edge_detection, &cache->ibo.edges_adj_lines); - DRW_vbo_request(cache->batch.edge_detection, &cache->ordered.pos_nor); + DRW_ibo_request(cache->batch.edge_detection, &mbufcache->ibo.lines_adjacency); + DRW_vbo_request(cache->batch.edge_detection, &mbufcache->vbo.pos_nor); } if (DRW_batch_requested(cache->batch.surface_weights, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.surface_weights, &cache->ibo.surf_tris); - DRW_vbo_request(cache->batch.surface_weights, &cache->ordered.pos_nor); - DRW_vbo_request(cache->batch.surface_weights, &cache->ordered.weights); + DRW_ibo_request(cache->batch.surface_weights, &mbufcache->ibo.tris); + DRW_vbo_request(cache->batch.surface_weights, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.surface_weights, &mbufcache->vbo.weights); } if (DRW_batch_requested(cache->batch.wire_loops, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.wire_loops, &cache->ibo.loops_lines_paint_mask); - DRW_vbo_request(cache->batch.wire_loops, &cache->ordered.loop_pos_nor); + DRW_ibo_request(cache->batch.wire_loops, &mbufcache->ibo.lines_paint_mask); + DRW_vbo_request(cache->batch.wire_loops, &mbufcache->vbo.pos_nor); } if (DRW_batch_requested(cache->batch.wire_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.wire_edges, &cache->ibo.loops_lines); - DRW_vbo_request(cache->batch.wire_edges, &cache->ordered.loop_pos_nor); - DRW_vbo_request(cache->batch.wire_edges, &cache->ordered.loop_edge_fac); + DRW_ibo_request(cache->batch.wire_edges, &mbufcache->ibo.lines); + DRW_vbo_request(cache->batch.wire_edges, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.wire_edges, &mbufcache->vbo.edge_fac); } - if (DRW_batch_requested(cache->batch.wire_loops_uvs, GPU_PRIM_LINE_STRIP)) { - DRW_ibo_request(cache->batch.wire_loops_uvs, &cache->ibo.loops_line_strips); + if (DRW_batch_requested(cache->batch.wire_loops_uvs, GPU_PRIM_LINES)) { + DRW_ibo_request(cache->batch.wire_loops_uvs, &mbufcache->ibo.edituv_lines); /* For paint overlay. Active layer should have been queried. */ if (cache->cd_used.uv != 0) { - DRW_vbo_request(cache->batch.wire_loops_uvs, &cache->ordered.loop_uv_tan); + DRW_vbo_request(cache->batch.wire_loops_uvs, &mbufcache->vbo.uv); } } - - /* Edit Mesh */ - if (DRW_batch_requested(cache->batch.edit_triangles, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edit_triangles, &cache->ibo.edit_loops_tris); - DRW_vbo_request(cache->batch.edit_triangles, &cache->edit.loop_pos_nor); - DRW_vbo_request(cache->batch.edit_triangles, &cache->edit.loop_data); - } - if (DRW_batch_requested(cache->batch.edit_vertices, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_vertices, &cache->ibo.edit_loops_points); - DRW_vbo_request(cache->batch.edit_vertices, &cache->edit.loop_pos_nor); - DRW_vbo_request(cache->batch.edit_vertices, &cache->edit.loop_data); - } - if (DRW_batch_requested(cache->batch.edit_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.edit_edges, &cache->ibo.edit_loops_lines); - DRW_vbo_request(cache->batch.edit_edges, &cache->edit.loop_pos_nor); - DRW_vbo_request(cache->batch.edit_edges, &cache->edit.loop_data); - } - if (DRW_batch_requested(cache->batch.edit_lnor, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_lnor, &cache->ibo.edit_loops_tris); - DRW_vbo_request(cache->batch.edit_lnor, &cache->edit.loop_pos_nor); - DRW_vbo_request(cache->batch.edit_lnor, &cache->edit.loop_lnor); - } - if (DRW_batch_requested(cache->batch.edit_facedots, GPU_PRIM_POINTS)) { - DRW_vbo_request(cache->batch.edit_facedots, &cache->edit.facedots_pos_nor_data); - } - - /* Mesh Analysis */ if (DRW_batch_requested(cache->batch.edit_mesh_analysis, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edit_mesh_analysis, &cache->ibo.edit_loops_tris); - DRW_vbo_request(cache->batch.edit_mesh_analysis, &cache->edit.loop_pos_nor); - DRW_vbo_request(cache->batch.edit_mesh_analysis, &cache->edit.loop_mesh_analysis); - } - - /* Edit UV */ - if (DRW_batch_requested(cache->batch.edituv_faces, GPU_PRIM_TRI_FAN)) { - DRW_ibo_request(cache->batch.edituv_faces, &cache->ibo.edituv_loops_tri_fans); - DRW_vbo_request(cache->batch.edituv_faces, &cache->edit.loop_uv); - DRW_vbo_request(cache->batch.edituv_faces, &cache->edit.loop_uv_data); - } - if (DRW_batch_requested(cache->batch.edituv_faces_strech_area, GPU_PRIM_TRI_FAN)) { - DRW_ibo_request(cache->batch.edituv_faces_strech_area, &cache->ibo.edituv_loops_tri_fans); - DRW_vbo_request(cache->batch.edituv_faces_strech_area, &cache->edit.loop_uv); - DRW_vbo_request(cache->batch.edituv_faces_strech_area, &cache->edit.loop_uv_data); - DRW_vbo_request(cache->batch.edituv_faces_strech_area, &cache->edit.loop_stretch_area); - } - if (DRW_batch_requested(cache->batch.edituv_faces_strech_angle, GPU_PRIM_TRI_FAN)) { - DRW_ibo_request(cache->batch.edituv_faces_strech_angle, &cache->ibo.edituv_loops_tri_fans); - DRW_vbo_request(cache->batch.edituv_faces_strech_angle, &cache->edit.loop_uv); - DRW_vbo_request(cache->batch.edituv_faces_strech_angle, &cache->edit.loop_uv_data); - DRW_vbo_request(cache->batch.edituv_faces_strech_angle, &cache->edit.loop_stretch_angle); - } - if (DRW_batch_requested(cache->batch.edituv_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.edituv_edges, &cache->ibo.edituv_loops_line_strips); - DRW_vbo_request(cache->batch.edituv_edges, &cache->edit.loop_uv); - DRW_vbo_request(cache->batch.edituv_edges, &cache->edit.loop_uv_data); - } - if (DRW_batch_requested(cache->batch.edituv_verts, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edituv_verts, &cache->ibo.edituv_loops_points); - DRW_vbo_request(cache->batch.edituv_verts, &cache->edit.loop_uv); - DRW_vbo_request(cache->batch.edituv_verts, &cache->edit.loop_uv_data); - } - if (DRW_batch_requested(cache->batch.edituv_facedots, GPU_PRIM_POINTS)) { - DRW_vbo_request(cache->batch.edituv_facedots, &cache->edit.facedots_uv); - DRW_vbo_request(cache->batch.edituv_facedots, &cache->edit.facedots_uv_data); - } - - /* Selection */ - /* TODO reuse ordered.loop_pos_nor if possible. */ - if (DRW_batch_requested(cache->batch.edit_selection_verts, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_selection_verts, &cache->ibo.edit_loops_points); - DRW_vbo_request(cache->batch.edit_selection_verts, &cache->edit.loop_pos_nor); - DRW_vbo_request(cache->batch.edit_selection_verts, &cache->edit.loop_vert_idx); - } - if (DRW_batch_requested(cache->batch.edit_selection_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.edit_selection_edges, &cache->ibo.edit_loops_lines); - DRW_vbo_request(cache->batch.edit_selection_edges, &cache->edit.loop_pos_nor); - DRW_vbo_request(cache->batch.edit_selection_edges, &cache->edit.loop_edge_idx); - } - if (DRW_batch_requested(cache->batch.edit_selection_faces, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edit_selection_faces, &cache->ibo.edit_loops_tris); - DRW_vbo_request(cache->batch.edit_selection_faces, &cache->edit.loop_pos_nor); - DRW_vbo_request(cache->batch.edit_selection_faces, &cache->edit.loop_face_idx); - } - if (DRW_batch_requested(cache->batch.edit_selection_facedots, GPU_PRIM_POINTS)) { - DRW_vbo_request(cache->batch.edit_selection_facedots, &cache->edit.facedots_pos_nor_data); - DRW_vbo_request(cache->batch.edit_selection_facedots, &cache->edit.facedots_idx); + DRW_ibo_request(cache->batch.edit_mesh_analysis, &mbufcache->ibo.tris); + DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbufcache->vbo.mesh_analysis); } /* Per Material */ for (int i = 0; i < cache->mat_len; ++i) { - if (DRW_batch_requested(cache->surf_per_mat[i], GPU_PRIM_TRIS)) { - if (cache->mat_len > 1) { - DRW_ibo_request(cache->surf_per_mat[i], &cache->surf_per_mat_tris[i]); - } - else { - DRW_ibo_request(cache->surf_per_mat[i], &cache->ibo.loops_tris); + if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { + DRW_ibo_request(cache->surface_per_mat[i], &mbufcache->ibo.tris); + /* Order matters. First ones override latest vbos' attribs. */ + DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.lnor); + DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.pos_nor); + if (cache->cd_used.uv != 0) { + DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.uv); } - DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_pos_nor); - if ((cache->cd_used.uv != 0) || (cache->cd_used.tan != 0) || - (cache->cd_used.tan_orco != 0)) { - DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_uv_tan); + if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) { + DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.tan); } if (cache->cd_used.vcol != 0) { - DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_vcol); + DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.vcol); } if (cache->cd_used.orco != 0) { - DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_orco); + DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.orco); } } } -#ifdef DRW_DEBUG_MESH_CACHE_REQUEST - printf("-- %s %s --\n", __func__, ob->id.name + 2); -#endif + mbufcache = (do_cage) ? &cache->cage : &cache->final; - /* Generate MeshRenderData flags */ - eMRDataType mr_flag = 0, mr_edit_flag = 0; - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_flag, cache->ordered.pos_nor, MR_DATATYPE_VERT /* A comment to wrap the line ;) */); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_flag, cache->ordered.weights, MR_DATATYPE_VERT | MR_DATATYPE_DVERT); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_flag, cache->ordered.loop_pos_nor, MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_LOOP_NORMALS); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_flag, cache->ordered.loop_uv_tan, MR_DATATYPE_VERT_LOOP_TRI_POLY | MR_DATATYPE_SHADING); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_flag, cache->ordered.loop_orco, MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_SHADING); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_flag, cache->ordered.loop_vcol, MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_SHADING); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_flag, cache->ordered.loop_edge_fac, MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_EDGE); - - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_flag, cache->ibo.surf_tris, MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_LOOPTRI); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_flag, cache->ibo.loops_tris, MR_DATATYPE_LOOP | MR_DATATYPE_POLY | MR_DATATYPE_LOOPTRI); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_flag, cache->ibo.loops_lines, MR_DATATYPE_LOOP | MR_DATATYPE_EDGE | MR_DATATYPE_POLY); - DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, - cache->ibo.loops_lines_paint_mask, - MR_DATATYPE_LOOP | MR_DATATYPE_EDGE | MR_DATATYPE_POLY); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_flag, cache->ibo.loops_line_strips, MR_DATATYPE_LOOP | MR_DATATYPE_POLY); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_flag, cache->ibo.edges_lines, MR_DATATYPE_VERT | MR_DATATYPE_EDGE); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_flag, cache->ibo.edges_adj_lines, MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_LOOPTRI); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_flag, cache->ibo.loose_edges_lines, MR_DATATYPE_VERT | MR_DATATYPE_EDGE); - for (int i = 0; i < cache->mat_len; ++i) { - int combined_flag = MR_DATATYPE_LOOP | MR_DATATYPE_POLY | MR_DATATYPE_LOOPTRI; - DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->surf_per_mat_tris[i], combined_flag); - } - - int combined_edit_flag = MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_EDGE | - MR_DATATYPE_LOOSE_VERT_EGDE; - int combined_edit_with_lnor_flag = combined_edit_flag | MR_DATATYPE_LOOP_NORMALS; - int combined_edituv_flag = combined_edit_flag | MR_DATATYPE_LOOPUV; - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.loop_pos_nor, combined_edit_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.loop_lnor, combined_edit_with_lnor_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.loop_data, combined_edit_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.loop_uv_data, combined_edit_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.loop_uv, combined_edituv_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.loop_stretch_angle, combined_edit_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.loop_stretch_area, combined_edit_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.loop_mesh_analysis, MR_DATATYPE_VERT_LOOP_POLY); - - DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_edit_flag, cache->edit.loop_vert_idx, combined_edit_flag); - DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_edit_flag, cache->edit.loop_edge_idx, combined_edit_flag); - DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_edit_flag, cache->edit.loop_face_idx, combined_edit_flag); - DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_edit_flag, cache->edit.facedots_idx, MR_DATATYPE_POLY); - - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.facedots_pos_nor_data, MR_DATATYPE_POLY | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.facedots_uv, combined_edituv_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_VBO_REQUEST( - mr_edit_flag, cache->edit.facedots_uv_data, combined_edit_flag | MR_DATATYPE_OVERLAY); - - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_edit_flag, cache->ibo.edituv_loops_points, combined_edit_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_edit_flag, cache->ibo.edituv_loops_line_strips, combined_edit_flag | MR_DATATYPE_OVERLAY); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_edit_flag, cache->ibo.edituv_loops_tri_fans, combined_edit_flag | MR_DATATYPE_OVERLAY); - - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_edit_flag, cache->ibo.edit_loops_points, combined_edit_flag | MR_DATATYPE_LOOPTRI); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_edit_flag, cache->ibo.edit_loops_lines, combined_edit_flag | MR_DATATYPE_LOOPTRI); - DRW_ADD_FLAG_FROM_IBO_REQUEST( - mr_edit_flag, cache->ibo.edit_loops_tris, combined_edit_flag | MR_DATATYPE_LOOPTRI); - - Mesh *me_original = me; - MBC_GET_FINAL_MESH(me); - -#ifdef DRW_DEBUG_MESH_CACHE_REQUEST - printf(" mr_flag %u, mr_edit_flag %u\n\n", mr_flag, mr_edit_flag); -#endif - - if (me_original == me) { - mr_flag |= mr_edit_flag; - } - - MeshRenderData *rdata = NULL; - - if (mr_flag != 0) { - rdata = mesh_render_data_create_ex(me, mr_flag, &cache->cd_used, ts); - } - - /* Generate VBOs */ - if (DRW_vbo_requested(cache->ordered.pos_nor)) { - mesh_create_pos_and_nor(rdata, cache->ordered.pos_nor); - } - if (DRW_vbo_requested(cache->ordered.weights)) { - mesh_create_weights(rdata, cache->ordered.weights, &cache->weight_state); - } - if (DRW_vbo_requested(cache->ordered.loop_pos_nor)) { - mesh_create_loop_pos_and_nor(rdata, cache->ordered.loop_pos_nor); - } - if (DRW_vbo_requested(cache->ordered.loop_edge_fac)) { - mesh_create_loop_edge_fac(rdata, cache->ordered.loop_edge_fac); - } - if (DRW_vbo_requested(cache->ordered.loop_uv_tan)) { - mesh_create_loop_uv_and_tan(rdata, cache->ordered.loop_uv_tan); - } - if (DRW_vbo_requested(cache->ordered.loop_orco)) { - mesh_create_loop_orco(rdata, cache->ordered.loop_orco); - } - if (DRW_vbo_requested(cache->ordered.loop_vcol)) { - mesh_create_loop_vcol(rdata, cache->ordered.loop_vcol); - } - if (DRW_ibo_requested(cache->ibo.edges_lines)) { - mesh_create_edges_lines(rdata, cache->ibo.edges_lines, use_hide); - } - if (DRW_ibo_requested(cache->ibo.edges_adj_lines)) { - mesh_create_edges_adjacency_lines( - rdata, cache->ibo.edges_adj_lines, &cache->is_manifold, use_hide); - } - if (DRW_ibo_requested(cache->ibo.loose_edges_lines)) { - mesh_create_loose_edges_lines( - rdata, cache->ibo.loose_edges_lines, &cache->no_loose_wire, use_hide); - } - if (DRW_ibo_requested(cache->ibo.surf_tris)) { - mesh_create_surf_tris(rdata, cache->ibo.surf_tris, use_hide); - } - if (DRW_ibo_requested(cache->ibo.loops_lines)) { - mesh_create_loops_lines(rdata, cache->ibo.loops_lines, use_hide); - } - if (DRW_ibo_requested(cache->ibo.loops_lines_paint_mask)) { - mesh_create_loops_lines_paint_mask(rdata, cache->ibo.loops_lines_paint_mask); + /* Edit Mesh */ + if (DRW_batch_requested(cache->batch.edit_triangles, GPU_PRIM_TRIS)) { + DRW_ibo_request(cache->batch.edit_triangles, &mbufcache->ibo.tris); + DRW_vbo_request(cache->batch.edit_triangles, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_triangles, &mbufcache->vbo.edit_data); } - if (DRW_ibo_requested(cache->ibo.loops_line_strips)) { - mesh_create_loops_line_strips(rdata, cache->ibo.loops_line_strips, use_hide); + if (DRW_batch_requested(cache->batch.edit_vertices, GPU_PRIM_POINTS)) { + DRW_ibo_request(cache->batch.edit_vertices, &mbufcache->ibo.points); + DRW_vbo_request(cache->batch.edit_vertices, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_vertices, &mbufcache->vbo.edit_data); } - if (DRW_ibo_requested(cache->ibo.loops_tris)) { - mesh_create_loops_tris(rdata, &cache->ibo.loops_tris, 1, use_hide); + if (DRW_batch_requested(cache->batch.edit_edges, GPU_PRIM_LINES)) { + DRW_ibo_request(cache->batch.edit_edges, &mbufcache->ibo.lines); + DRW_vbo_request(cache->batch.edit_edges, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_edges, &mbufcache->vbo.edit_data); } - if (DRW_ibo_requested(cache->surf_per_mat_tris[0])) { - mesh_create_loops_tris(rdata, cache->surf_per_mat_tris, cache->mat_len, use_hide); + if (DRW_batch_requested(cache->batch.edit_vnor, GPU_PRIM_POINTS)) { + DRW_ibo_request(cache->batch.edit_vnor, &mbufcache->ibo.points); + DRW_vbo_request(cache->batch.edit_vnor, &mbufcache->vbo.pos_nor); } - - /* Use original Mesh* to have the correct edit cage. */ - if (me_original != me && mr_edit_flag != 0) { - if (rdata) { - mesh_render_data_free(rdata); - } - rdata = mesh_render_data_create_ex(me_original, mr_edit_flag, NULL, ts); + if (DRW_batch_requested(cache->batch.edit_lnor, GPU_PRIM_POINTS)) { + DRW_ibo_request(cache->batch.edit_lnor, &mbufcache->ibo.tris); + DRW_vbo_request(cache->batch.edit_lnor, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_lnor, &mbufcache->vbo.lnor); } - - if (rdata && rdata->mapped.supported) { - rdata->mapped.use = true; + if (DRW_batch_requested(cache->batch.edit_fdots, GPU_PRIM_POINTS)) { + DRW_ibo_request(cache->batch.edit_fdots, &mbufcache->ibo.fdots); + DRW_vbo_request(cache->batch.edit_fdots, &mbufcache->vbo.fdots_pos); + DRW_vbo_request(cache->batch.edit_fdots, &mbufcache->vbo.fdots_nor); } - if (DRW_vbo_requested(cache->edit.loop_pos_nor) || DRW_vbo_requested(cache->edit.loop_lnor) || - DRW_vbo_requested(cache->edit.loop_data) || DRW_vbo_requested(cache->edit.loop_vert_idx) || - DRW_vbo_requested(cache->edit.loop_edge_idx) || - DRW_vbo_requested(cache->edit.loop_face_idx)) { - mesh_create_edit_vertex_loops(rdata, - cache->edit.loop_pos_nor, - cache->edit.loop_lnor, - NULL, - cache->edit.loop_data, - cache->edit.loop_vert_idx, - cache->edit.loop_edge_idx, - cache->edit.loop_face_idx); - } - if (DRW_vbo_requested(cache->edit.facedots_pos_nor_data)) { - Scene *scene = DRW_context_state_get()->scene; - mesh_create_edit_facedots(rdata, cache->edit.facedots_pos_nor_data, scene, ob); - } - if (DRW_vbo_requested(cache->edit.facedots_idx)) { - Scene *scene = DRW_context_state_get()->scene; - mesh_create_edit_facedots_select_id(rdata, cache->edit.facedots_idx, scene, ob); + /* Selection */ + if (DRW_batch_requested(cache->batch.edit_selection_verts, GPU_PRIM_POINTS)) { + DRW_ibo_request(cache->batch.edit_selection_verts, &mbufcache->ibo.points); + DRW_vbo_request(cache->batch.edit_selection_verts, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_selection_verts, &mbufcache->vbo.vert_idx); } - if (DRW_ibo_requested(cache->ibo.edit_loops_points) || - DRW_ibo_requested(cache->ibo.edit_loops_lines)) { - mesh_create_edit_loops_points_lines( - rdata, cache->ibo.edit_loops_points, cache->ibo.edit_loops_lines); + if (DRW_batch_requested(cache->batch.edit_selection_edges, GPU_PRIM_LINES)) { + DRW_ibo_request(cache->batch.edit_selection_edges, &mbufcache->ibo.lines); + DRW_vbo_request(cache->batch.edit_selection_edges, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_selection_edges, &mbufcache->vbo.edge_idx); } - if (DRW_ibo_requested(cache->ibo.edit_loops_tris)) { - mesh_create_edit_loops_tris(rdata, cache->ibo.edit_loops_tris); + if (DRW_batch_requested(cache->batch.edit_selection_faces, GPU_PRIM_TRIS)) { + DRW_ibo_request(cache->batch.edit_selection_faces, &mbufcache->ibo.tris); + DRW_vbo_request(cache->batch.edit_selection_faces, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_selection_faces, &mbufcache->vbo.poly_idx); } - if (DRW_vbo_requested(cache->edit.loop_mesh_analysis)) { - mesh_create_edit_mesh_analysis(rdata, cache->edit.loop_mesh_analysis); + if (DRW_batch_requested(cache->batch.edit_selection_fdots, GPU_PRIM_POINTS)) { + DRW_ibo_request(cache->batch.edit_selection_fdots, &mbufcache->ibo.fdots); + DRW_vbo_request(cache->batch.edit_selection_fdots, &mbufcache->vbo.fdots_pos); + DRW_vbo_request(cache->batch.edit_selection_fdots, &mbufcache->vbo.fdot_idx); } - /* UV editor */ /** * TODO: The code and data structure is ready to support modified UV display * but the selection code for UVs needs to support it first. So for now, only * display the cage in all cases. */ - if (rdata && rdata->mapped.supported) { - rdata->mapped.use = false; - } + mbufcache = (do_uvcage) ? &cache->uv_cage : &cache->final; - if (DRW_vbo_requested(cache->edit.loop_uv_data) || DRW_vbo_requested(cache->edit.loop_uv)) { - mesh_create_edit_vertex_loops( - rdata, NULL, NULL, cache->edit.loop_uv, cache->edit.loop_uv_data, NULL, NULL, NULL); + /* Edit UV */ + if (DRW_batch_requested(cache->batch.edituv_faces, GPU_PRIM_TRIS)) { + DRW_ibo_request(cache->batch.edituv_faces, &mbufcache->ibo.edituv_tris); + DRW_vbo_request(cache->batch.edituv_faces, &mbufcache->vbo.uv); + DRW_vbo_request(cache->batch.edituv_faces, &mbufcache->vbo.edituv_data); + } + if (DRW_batch_requested(cache->batch.edituv_faces_strech_area, GPU_PRIM_TRIS)) { + DRW_ibo_request(cache->batch.edituv_faces_strech_area, &mbufcache->ibo.edituv_tris); + DRW_vbo_request(cache->batch.edituv_faces_strech_area, &mbufcache->vbo.uv); + DRW_vbo_request(cache->batch.edituv_faces_strech_area, &mbufcache->vbo.edituv_data); + DRW_vbo_request(cache->batch.edituv_faces_strech_area, &mbufcache->vbo.stretch_area); + } + if (DRW_batch_requested(cache->batch.edituv_faces_strech_angle, GPU_PRIM_TRIS)) { + DRW_ibo_request(cache->batch.edituv_faces_strech_angle, &mbufcache->ibo.edituv_tris); + DRW_vbo_request(cache->batch.edituv_faces_strech_angle, &mbufcache->vbo.uv); + DRW_vbo_request(cache->batch.edituv_faces_strech_angle, &mbufcache->vbo.edituv_data); + DRW_vbo_request(cache->batch.edituv_faces_strech_angle, &mbufcache->vbo.stretch_angle); } - if (DRW_vbo_requested(cache->edit.loop_stretch_angle) || - DRW_vbo_requested(cache->edit.loop_stretch_area) || - DRW_vbo_requested(cache->edit.facedots_uv) || - DRW_vbo_requested(cache->edit.facedots_uv_data) || - DRW_ibo_requested(cache->ibo.edituv_loops_points) || - DRW_ibo_requested(cache->ibo.edituv_loops_line_strips) || - DRW_ibo_requested(cache->ibo.edituv_loops_tri_fans)) { - mesh_create_uvedit_buffers(rdata, - cache->edit.loop_stretch_area, - cache->edit.loop_stretch_angle, - cache->edit.facedots_uv, - cache->edit.facedots_uv_data, - cache->ibo.edituv_loops_points, - cache->ibo.edituv_loops_line_strips, - cache->ibo.edituv_loops_tri_fans); + if (DRW_batch_requested(cache->batch.edituv_edges, GPU_PRIM_LINES)) { + DRW_ibo_request(cache->batch.edituv_edges, &mbufcache->ibo.edituv_lines); + DRW_vbo_request(cache->batch.edituv_edges, &mbufcache->vbo.uv); + DRW_vbo_request(cache->batch.edituv_edges, &mbufcache->vbo.edituv_data); + } + if (DRW_batch_requested(cache->batch.edituv_verts, GPU_PRIM_POINTS)) { + DRW_ibo_request(cache->batch.edituv_verts, &mbufcache->ibo.edituv_points); + DRW_vbo_request(cache->batch.edituv_verts, &mbufcache->vbo.uv); + DRW_vbo_request(cache->batch.edituv_verts, &mbufcache->vbo.edituv_data); } + if (DRW_batch_requested(cache->batch.edituv_fdots, GPU_PRIM_POINTS)) { + DRW_ibo_request(cache->batch.edituv_fdots, &mbufcache->ibo.edituv_fdots); + DRW_vbo_request(cache->batch.edituv_fdots, &mbufcache->vbo.fdots_uv); + DRW_vbo_request(cache->batch.edituv_fdots, &mbufcache->vbo.fdots_edituv_data); + } + + /* Meh loose Scene const correctness here. */ + const bool use_subsurf_fdots = scene ? modifiers_usesSubsurfFacedots((Scene *)scene, ob) : false; - if (rdata) { - mesh_render_data_free(rdata); + if (do_uvcage) { + mesh_buffer_cache_create_requested( + cache, cache->uv_cage, me, false, true, false, &cache->cd_used, ts, true); } + if (do_cage) { + mesh_buffer_cache_create_requested( + cache, cache->cage, me, false, false, use_subsurf_fdots, &cache->cd_used, ts, true); + } + + mesh_buffer_cache_create_requested( + cache, cache->final, me, true, false, use_subsurf_fdots, &cache->cd_used, ts, use_hide); + #ifdef DEBUG check: /* Make sure all requested batches have been setup. */ for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); ++i) { BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0)); } + for (int i = 0; i < sizeof(cache->final.vbo) / sizeof(void *); ++i) { + BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i])); + } + for (int i = 0; i < sizeof(cache->final.ibo) / sizeof(void *); ++i) { + BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i])); + } + for (int i = 0; i < sizeof(cache->cage.vbo) / sizeof(void *); ++i) { + BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i])); + } + for (int i = 0; i < sizeof(cache->cage.ibo) / sizeof(void *); ++i) { + BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i])); + } + for (int i = 0; i < sizeof(cache->uv_cage.vbo) / sizeof(void *); ++i) { + BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i])); + } + for (int i = 0; i < sizeof(cache->uv_cage.ibo) / sizeof(void *); ++i) { + BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i])); + } #endif } diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index fa1f1f2aab4..44ed01c47aa 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -253,7 +253,7 @@ void DRW_hair_update(void) * On some system it crashes (see T58489) and on some other it renders garbage (see T60171). * * So instead of using transform feedback we render to a texture, - * readback the result to system memory and reupload as VBO data. + * read back the result to system memory and re-upload as VBO data. * It is really not ideal performance wise, but it is the simplest * and the most local workaround that still uses the power of the GPU. */ diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 802f49d6549..8f26cc72a02 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -23,7 +23,7 @@ /** * DRW Instance Data Manager * This is a special memory manager that keeps memory blocks ready to send as vbo data in one - * continuous allocation. This way we avoid feeding gawain each instance data one by one and + * continuous allocation. This way we avoid feeding #GPUBatch each instance data one by one and * unnecessary memcpy. Since we loose which memory block was used each #DRWShadingGroup we need to * redistribute them in the same order/size to avoid to realloc each frame. This is why * #DRWInstanceDatas are sorted in a list for each different data size. diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 070713ad404..72fcb9ac968 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1725,9 +1725,9 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, if (G.debug_value > 20 && G.debug_value < 30) { GPU_depth_test(false); - rcti rect; /* local coordinate visible rect inside region, to accommodate overlapping ui */ - ED_region_visible_rect(DST.draw_ctx.ar, &rect); - DRW_stats_draw(&rect); + /* local coordinate visible rect inside region, to accommodate overlapping ui */ + const rcti *rect = ED_region_visible_rect(DST.draw_ctx.ar); + DRW_stats_draw(rect); GPU_depth_test(true); } @@ -1864,7 +1864,7 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph DRW_opengl_render_context_enable(re_gl_context); /* We need to query gpu context after a gl context has been bound. */ re_gpu_context = RE_gpu_context_get(render); - DRW_gawain_render_context_enable(re_gpu_context); + DRW_gpu_render_context_enable(re_gpu_context); } else { DRW_opengl_context_enable(); @@ -1949,13 +1949,13 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) DRW_opengl_render_context_enable(re_gl_context); /* We need to query gpu context after a gl context has been bound. */ re_gpu_context = RE_gpu_context_get(render); - DRW_gawain_render_context_enable(re_gpu_context); + DRW_gpu_render_context_enable(re_gpu_context); } else { DRW_opengl_context_enable(); } - /* IMPORTANT: We dont support immediate mode in render mode! + /* IMPORTANT: We don't support immediate mode in render mode! * This shall remain in effect until immediate mode supports * multiple threads. */ @@ -2040,7 +2040,7 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) /* Changing Context */ if (re_gl_context != NULL) { - DRW_gawain_render_context_disable(re_gpu_context); + DRW_gpu_render_context_disable(re_gpu_context); DRW_opengl_render_context_disable(re_gl_context); } else { @@ -2555,23 +2555,13 @@ void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph, DRW_opengl_context_disable(); } -void DRW_draw_select_id(Depsgraph *depsgraph, - ARegion *ar, - View3D *v3d, - Base **bases, - const uint bases_len, - short select_mode) +void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *ar, View3D *v3d, const rcti *rect) { Scene *scene = DEG_get_evaluated_scene(depsgraph); ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); - DRW_select_buffer_context_create(bases, bases_len, select_mode); - - DRW_opengl_context_enable(); - /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); - DST.buffer_finish_called = true; /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */ DST.draw_ctx = (DRWContextState){ @@ -2584,7 +2574,6 @@ void DRW_draw_select_id(Depsgraph *depsgraph, .depsgraph = depsgraph, }; - use_drw_engine(&draw_engine_select_type); drw_context_state_init(); /* Setup viewport */ @@ -2594,20 +2583,24 @@ void DRW_draw_select_id(Depsgraph *depsgraph, /* Update ubos */ DRW_globals_update(); - /* Init engines */ - drw_engines_init(); + /* Init Select Engine */ + struct SELECTID_Context *sel_ctx = DRW_select_engine_context_get(); + sel_ctx->last_rect = *rect; + use_drw_engine(&draw_engine_select_type); + drw_engines_init(); { drw_engines_cache_init(); - /* Keep `base_index` in sync with `e_data.context.last_base_drawn`. - * So don't skip objects. */ - for (uint base_index = 0; base_index < bases_len; base_index++) { - Object *obj_eval = DEG_get_evaluated_object(depsgraph, bases[base_index]->object); + Object **obj = &sel_ctx->objects[0]; + for (uint remaining = sel_ctx->objects_len; remaining--; obj++) { + Object *obj_eval = DEG_get_evaluated_object(depsgraph, *obj); drw_engines_cache_populate(obj_eval); } drw_engines_cache_finish(); + + DRW_render_instance_buffer_finish(); } /* Start Drawing */ @@ -2617,14 +2610,12 @@ void DRW_draw_select_id(Depsgraph *depsgraph, drw_engines_disable(); + drw_viewport_cache_resize(); + #ifdef DEBUG /* Avoid accidental reuse. */ drw_state_ensure_not_reused(&DST); #endif - - /* Changin context */ - GPU_framebuffer_restore(); - DRW_opengl_context_disable(); } /** See #DRW_shgroup_world_clip_planes_from_rv3d. */ @@ -2932,7 +2923,7 @@ void DRW_opengl_context_create(void) /* This changes the active context. */ DST.gl_context = WM_opengl_context_create(); WM_opengl_context_activate(DST.gl_context); - /* Be sure to create gawain.context too. */ + /* Be sure to create gpu_context too. */ DST.gpu_context = GPU_context_create(0); if (!G.background) { immActivate(); @@ -3031,7 +3022,7 @@ void DRW_opengl_render_context_disable(void *re_gl_context) } /* Needs to be called AFTER DRW_opengl_render_context_enable() */ -void DRW_gawain_render_context_enable(void *re_gpu_context) +void DRW_gpu_render_context_enable(void *re_gpu_context) { /* If thread is main you should use DRW_opengl_context_enable(). */ BLI_assert(!BLI_thread_is_main()); @@ -3041,7 +3032,7 @@ void DRW_gawain_render_context_enable(void *re_gpu_context) } /* Needs to be called BEFORE DRW_opengl_render_context_disable() */ -void DRW_gawain_render_context_disable(void *UNUSED(re_gpu_context)) +void DRW_gpu_render_context_disable(void *UNUSED(re_gpu_context)) { DRW_shape_cache_reset(); /* XXX fix that too. */ GPU_context_active_set(NULL); diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 949d3e1d38b..09bf12dba7b 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -22,6 +22,7 @@ #include "draw_manager.h" +#include "BLI_math.h" #include "BLI_math_bits.h" #include "BLI_memblock.h" @@ -488,6 +489,26 @@ bool DRW_culling_plane_test(const DRWView *view, const float plane[4]) return draw_culling_plane_test(&view->frustum_corners, plane); } +/* Return True if the given box intersect the current view frustum. + * This function will have to be replaced when world space bb per objects is implemented. */ +bool DRW_culling_min_max_test(const DRWView *view, float obmat[4][4], float min[3], float max[3]) +{ + view = view ? view : DST.view_default; + float tobmat[4][4]; + transpose_m4_m4(tobmat, obmat); + for (int i = 6; i--;) { + float frustum_plane_local[4], bb_near[3], bb_far[3]; + mul_v4_m4v4(frustum_plane_local, tobmat, view->frustum_planes[i]); + aabb_get_near_far_from_plane(frustum_plane_local, min, max, bb_near, bb_far); + + if (plane_point_side_v3(frustum_plane_local, bb_far) < 0.0f) { + return false; + } + } + + return true; +} + void DRW_culling_frustum_corners_get(const DRWView *view, BoundBox *corners) { view = view ? view : DST.view_default; @@ -601,12 +622,12 @@ BLI_INLINE void draw_geometry_execute(DRWShadingGroup *shgroup, GPU_batch_bind(geom); } - /* XXX hacking gawain. we don't want to call glUseProgram! (huge performance loss) */ + /* XXX hacking #GPUBatch. we don't want to call glUseProgram! (huge performance loss) */ geom->program_in_use = true; GPU_batch_draw_advanced(geom, vert_first, vert_count, inst_first, inst_count); - geom->program_in_use = false; /* XXX hacking gawain */ + geom->program_in_use = false; /* XXX hacking #GPUBatch */ } enum { diff --git a/source/blender/draw/intern/draw_manager_profiling.c b/source/blender/draw/intern/draw_manager_profiling.c index 5e21e5e576c..bab69cf7a57 100644 --- a/source/blender/draw/intern/draw_manager_profiling.c +++ b/source/blender/draw/intern/draw_manager_profiling.c @@ -200,7 +200,7 @@ void DRW_stats_reset(void) } } -static void draw_stat_5row(rcti *rect, int u, int v, const char *txt, const int size) +static void draw_stat_5row(const rcti *rect, int u, int v, const char *txt, const int size) { BLF_draw_default_ascii(rect->xmin + (1 + u * 5) * U.widget_unit, rect->ymax - (3 + v) * U.widget_unit, @@ -209,13 +209,13 @@ static void draw_stat_5row(rcti *rect, int u, int v, const char *txt, const int size); } -static void draw_stat(rcti *rect, int u, int v, const char *txt, const int size) +static void draw_stat(const rcti *rect, int u, int v, const char *txt, const int size) { BLF_draw_default_ascii( rect->xmin + (1 + u) * U.widget_unit, rect->ymax - (3 + v) * U.widget_unit, 0.0f, txt, size); } -void DRW_stats_draw(rcti *rect) +void DRW_stats_draw(const rcti *rect) { char stat_string[64]; int lvl_index[MAX_NESTED_TIMER]; diff --git a/source/blender/draw/intern/draw_manager_profiling.h b/source/blender/draw/intern/draw_manager_profiling.h index 7fe2280f9b2..3da6a4c8b1c 100644 --- a/source/blender/draw/intern/draw_manager_profiling.h +++ b/source/blender/draw/intern/draw_manager_profiling.h @@ -35,6 +35,6 @@ void DRW_stats_group_end(void); void DRW_stats_query_start(const char *name); void DRW_stats_query_end(void); -void DRW_stats_draw(rcti *rect); +void DRW_stats_draw(const rcti *rect); #endif /* __DRAW_MANAGER_PROFILING_H__ */ diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index d0aa6d55c03..9c34cdbac3b 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -178,7 +178,7 @@ static void drw_deferred_shader_compilation_free(void *custom_data) static void drw_deferred_shader_add(GPUMaterial *mat, bool deferred) { - /* Do not deferre the compilation if we are rendering for image. + /* Do not defer the compilation if we are rendering for image. * deferred rendering is only possible when `evil_C` is available */ if (DST.draw_ctx.evil_C == NULL || DRW_state_is_image_render() || !USE_DEFERRED_COMPILATION || !deferred) { diff --git a/source/blender/draw/engines/select/select_buffer.c b/source/blender/draw/intern/draw_select_buffer.c index b184992cb56..974ea22ccea 100644 --- a/source/blender/draw/engines/select/select_buffer.c +++ b/source/blender/draw/intern/draw_select_buffer.c @@ -32,59 +32,81 @@ #include "GPU_select.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "DRW_engine.h" #include "DRW_select_buffer.h" -#include "select_private.h" -#include "select_engine.h" +#include "draw_manager.h" + +#include "../engines/select/select_engine.h" /* -------------------------------------------------------------------- */ /** \name Buffer of select ID's * \{ */ -/* Read a block of pixels from the select frame buffer. */ -uint *DRW_select_buffer_read(const rcti *rect, uint *r_buf_len) +/* Main function to read a block of pixels from the select frame buffer. */ +uint *DRW_select_buffer_read(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const rcti *rect, + uint *r_buf_len) { - struct SELECTID_Context *select_ctx = select_context_get(); + uint *r_buf = NULL; + uint buf_len = 0; - /* clamp rect by texture */ + /* Clamp rect. */ rcti r = { .xmin = 0, - .xmax = GPU_texture_width(select_ctx->texture_u32), + .xmax = ar->winx, .ymin = 0, - .ymax = GPU_texture_height(select_ctx->texture_u32), + .ymax = ar->winy, }; + /* Make sure that the rect is within the bounds of the viewport. + * Some GPUs have problems reading pixels off limits. */ rcti rect_clamp = *rect; if (BLI_rcti_isect(&r, &rect_clamp, &rect_clamp)) { - uint buf_len = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); - uint *r_buf = MEM_mallocN(buf_len * sizeof(*r_buf), __func__); + struct SELECTID_Context *select_ctx = DRW_select_engine_context_get(); DRW_opengl_context_enable(); - GPU_framebuffer_bind(select_ctx->framebuffer_select_id); - glReadBuffer(GL_COLOR_ATTACHMENT0); - glReadPixels(rect_clamp.xmin, - rect_clamp.ymin, - BLI_rcti_size_x(&rect_clamp), - BLI_rcti_size_y(&rect_clamp), - GL_RED_INTEGER, - GL_UNSIGNED_INT, - r_buf); + /* Update the drawing. */ + DRW_draw_select_id(depsgraph, ar, v3d, rect); + + if (select_ctx->index_drawn_len > 1) { + BLI_assert(ar->winx == GPU_texture_width(select_ctx->texture_u32) && + ar->winy == GPU_texture_height(select_ctx->texture_u32)); + + /* Read the UI32 pixels. */ + buf_len = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + r_buf = MEM_mallocN(buf_len * sizeof(*r_buf), __func__); + + GPU_framebuffer_bind(select_ctx->framebuffer_select_id); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glReadPixels(rect_clamp.xmin, + rect_clamp.ymin, + BLI_rcti_size_x(&rect_clamp), + BLI_rcti_size_y(&rect_clamp), + GL_RED_INTEGER, + GL_UNSIGNED_INT, + r_buf); + + if (!BLI_rcti_compare(rect, &rect_clamp)) { + /* The rect has been clamped so you need to realign the buffer and fill in the blanks */ + GPU_select_buffer_stride_realign(rect, &rect_clamp, r_buf); + } + } GPU_framebuffer_restore(); DRW_opengl_context_disable(); + } - if (!BLI_rcti_compare(rect, &rect_clamp)) { - GPU_select_buffer_stride_realign(rect, &rect_clamp, r_buf); - } - - if (r_buf_len) { - *r_buf_len = buf_len; - } - - return r_buf; + if (r_buf_len) { + *r_buf_len = buf_len; } - return NULL; + + return r_buf; } /** \} */ @@ -102,25 +124,27 @@ uint *DRW_select_buffer_read(const rcti *rect, uint *r_buf_len) * \param mask: Specifies the rect pixels (optional). * \returns a #BLI_bitmap the length of \a bitmap_len or NULL on failure. */ -uint *DRW_select_buffer_bitmap_from_rect(const rcti *rect, uint *r_bitmap_len) +uint *DRW_select_buffer_bitmap_from_rect(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const rcti *rect, + uint *r_bitmap_len) { - struct SELECTID_Context *select_ctx = select_context_get(); - - const uint bitmap_len = select_ctx->last_index_drawn; - if (bitmap_len == 0) { - return NULL; - } + struct SELECTID_Context *select_ctx = DRW_select_engine_context_get(); rcti rect_px = *rect; rect_px.xmax += 1; rect_px.ymax += 1; uint buf_len; - uint *buf = DRW_select_buffer_read(&rect_px, &buf_len); + uint *buf = DRW_select_buffer_read(depsgraph, ar, v3d, &rect_px, &buf_len); if (buf == NULL) { return NULL; } + BLI_assert(select_ctx->index_drawn_len > 0); + const uint bitmap_len = select_ctx->index_drawn_len - 1; + BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__); const uint *buf_iter = buf; while (buf_len--) { @@ -145,16 +169,14 @@ uint *DRW_select_buffer_bitmap_from_rect(const rcti *rect, uint *r_bitmap_len) * \param radius: Circle radius. * \returns a #BLI_bitmap the length of \a bitmap_len or NULL on failure. */ -uint *DRW_select_buffer_bitmap_from_circle(const int center[2], +uint *DRW_select_buffer_bitmap_from_circle(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const int center[2], const int radius, uint *r_bitmap_len) { - struct SELECTID_Context *select_ctx = select_context_get(); - - const uint bitmap_len = select_ctx->last_index_drawn; - if (bitmap_len == 0) { - return NULL; - } + struct SELECTID_Context *select_ctx = DRW_select_engine_context_get(); const rcti rect = { .xmin = center[0] - radius, @@ -163,15 +185,17 @@ uint *DRW_select_buffer_bitmap_from_circle(const int center[2], .ymax = center[1] + radius + 1, }; - const uint *buf = DRW_select_buffer_read(&rect, NULL); + const uint *buf = DRW_select_buffer_read(depsgraph, ar, v3d, &rect, NULL); if (buf == NULL) { return NULL; } - const uint *buf_iter = buf; + BLI_assert(select_ctx->index_drawn_len > 0); + const uint bitmap_len = select_ctx->index_drawn_len - 1; BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__); + const uint *buf_iter = buf; const int radius_sq = radius * radius; for (int yc = -radius; yc <= radius; yc++) { for (int xc = -radius; xc <= radius; xc++, buf_iter++) { @@ -215,21 +239,22 @@ static void drw_select_mask_px_cb(int x, int x_end, int y, void *user_data) * \param rect: Polygon boundaries. * \returns a #BLI_bitmap. */ -uint *DRW_select_buffer_bitmap_from_poly(const int poly[][2], const int poly_len, const rcti *rect) +uint *DRW_select_buffer_bitmap_from_poly(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const int poly[][2], + const int poly_len, + const rcti *rect, + uint *r_bitmap_len) { - struct SELECTID_Context *select_ctx = select_context_get(); - - const uint bitmap_len = select_ctx->last_index_drawn; - if (bitmap_len == 0) { - return NULL; - } + struct SELECTID_Context *select_ctx = DRW_select_engine_context_get(); rcti rect_px = *rect; rect_px.xmax += 1; rect_px.ymax += 1; uint buf_len; - uint *buf = DRW_select_buffer_read(&rect_px, &buf_len); + uint *buf = DRW_select_buffer_read(depsgraph, ar, v3d, &rect_px, &buf_len); if (buf == NULL) { return NULL; } @@ -249,6 +274,9 @@ uint *DRW_select_buffer_bitmap_from_poly(const int poly[][2], const int poly_len drw_select_mask_px_cb, &poly_mask_data); + BLI_assert(select_ctx->index_drawn_len > 0); + const uint bitmap_len = select_ctx->index_drawn_len - 1; + BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__); const uint *buf_iter = buf; int i = 0; @@ -263,6 +291,10 @@ uint *DRW_select_buffer_bitmap_from_poly(const int poly[][2], const int poly_len MEM_freeN((void *)buf); MEM_freeN(buf_mask); + if (r_bitmap_len) { + *r_bitmap_len = bitmap_len; + } + return bitmap_buf; } @@ -278,8 +310,13 @@ uint *DRW_select_buffer_bitmap_from_poly(const int poly[][2], const int poly_len /** * Samples a single pixel. */ -uint DRW_select_buffer_sample_point(const int center[2]) +uint DRW_select_buffer_sample_point(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const int center[2]) { + uint ret = 0; + const rcti rect = { .xmin = center[0], .xmax = center[0] + 1, @@ -288,10 +325,13 @@ uint DRW_select_buffer_sample_point(const int center[2]) }; uint buf_len; - uint *buf = DRW_select_buffer_read(&rect, &buf_len); - BLI_assert(0 != buf_len); - uint ret = buf[0]; - MEM_freeN(buf); + uint *buf = DRW_select_buffer_read(depsgraph, ar, v3d, &rect, &buf_len); + if (buf) { + BLI_assert(0 != buf_len); + ret = buf[0]; + MEM_freeN(buf); + } + return ret; } @@ -300,7 +340,10 @@ uint DRW_select_buffer_sample_point(const int center[2]) * \param dist[in,out]: Use to initialize the distance, * when found, this value is set to the distance of the selection that's returned. */ -uint DRW_select_buffer_find_nearest_to_point(const int center[2], +uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph, + struct ARegion *ar, + struct View3D *v3d, + const int center[2], const uint id_min, const uint id_max, uint *dist) @@ -325,7 +368,7 @@ uint DRW_select_buffer_find_nearest_to_point(const int center[2], /* Read from selection framebuffer. */ uint buf_len; - const uint *buf = DRW_select_buffer_read(&rect, &buf_len); + const uint *buf = DRW_select_buffer_read(depsgraph, ar, v3d, &rect, &buf_len); if (buf == NULL) { return index; @@ -399,14 +442,14 @@ bool DRW_select_buffer_elem_get(const uint sel_id, uint *r_base_index, char *r_elem_type) { - struct SELECTID_Context *select_ctx = select_context_get(); + struct SELECTID_Context *select_ctx = DRW_select_engine_context_get(); char elem_type = 0; - uint elem_id; + uint elem_id = 0; uint base_index = 0; - for (; base_index < select_ctx->objects_len; base_index++) { - struct BaseOffset *base_ofs = &select_ctx->index_offsets[base_index]; + for (; base_index < select_ctx->objects_drawn_len; base_index++) { + struct ObjectOffsets *base_ofs = &select_ctx->index_offsets[base_index]; if (base_ofs->face > sel_id) { elem_id = sel_id - base_ofs->face_start; @@ -425,14 +468,15 @@ bool DRW_select_buffer_elem_get(const uint sel_id, } } - if (base_index == select_ctx->objects_len) { + if (base_index == select_ctx->objects_drawn_len) { return false; } *r_elem = elem_id; if (r_base_index) { - *r_base_index = base_index; + Object *obj_orig = DEG_get_original_object(select_ctx->objects_drawn[base_index]); + *r_base_index = obj_orig->runtime.select_id; } if (r_elem_type) { @@ -442,19 +486,31 @@ bool DRW_select_buffer_elem_get(const uint sel_id, return true; } -uint DRW_select_buffer_context_offset_for_object_elem(const uint base_index, char elem_type) +uint DRW_select_buffer_context_offset_for_object_elem(Depsgraph *depsgraph, + Object *object, + char elem_type) { - struct SELECTID_Context *select_ctx = select_context_get(); - struct BaseOffset *base_ofs = &select_ctx->index_offsets[base_index]; + struct SELECTID_Context *select_ctx = DRW_select_engine_context_get(); + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, object); + + SELECTID_ObjectData *sel_data = (SELECTID_ObjectData *)DRW_drawdata_get( + &ob_eval->id, &draw_engine_select_type); + + if (!sel_data || !sel_data->is_drawn) { + return 0; + } + + struct ObjectOffsets *base_ofs = &select_ctx->index_offsets[sel_data->drawn_index]; if (elem_type == SCE_SELECT_VERTEX) { - return base_ofs->vert_start - 1; + return base_ofs->vert_start; } if (elem_type == SCE_SELECT_EDGE) { - return base_ofs->edge_start - 1; + return base_ofs->edge_start; } if (elem_type == SCE_SELECT_FACE) { - return base_ofs->face_start - 1; + return base_ofs->face_start; } BLI_assert(0); return 0; @@ -466,35 +522,29 @@ uint DRW_select_buffer_context_offset_for_object_elem(const uint base_index, cha /** \name Context * \{ */ -void DRW_select_buffer_context_create(Base **UNUSED(bases), - const uint bases_len, - short select_mode) +void DRW_select_buffer_context_create(Base **bases, const uint bases_len, short select_mode) { - struct SELECTID_Context *select_ctx = select_context_get(); + struct SELECTID_Context *select_ctx = DRW_select_engine_context_get(); - select_ctx->select_mode = select_mode; - select_ctx->objects_len = bases_len; + select_ctx->objects = MEM_reallocN(select_ctx->objects, + sizeof(*select_ctx->objects) * bases_len); - MEM_SAFE_FREE(select_ctx->index_offsets); - select_ctx->index_offsets = MEM_mallocN(sizeof(*select_ctx->index_offsets) * bases_len, - __func__); -} + select_ctx->index_offsets = MEM_reallocN(select_ctx->index_offsets, + sizeof(*select_ctx->index_offsets) * bases_len); -/** \} */ + select_ctx->objects_drawn = MEM_reallocN(select_ctx->objects_drawn, + sizeof(*select_ctx->objects_drawn) * bases_len); -/* -------------------------------------------------------------------- */ -/** \name Legacy - * \{ */ + for (uint base_index = 0; base_index < bases_len; base_index++) { + Object *obj = bases[base_index]->object; + select_ctx->objects[base_index] = obj; -void DRW_draw_select_id_object(Depsgraph *depsgraph, - ViewLayer *view_layer, - ARegion *ar, - View3D *v3d, - Object *ob, - short select_mode) -{ - Base *base = BKE_view_layer_base_find(view_layer, ob); - DRW_draw_select_id(depsgraph, ar, v3d, &base, 1, select_mode); -} + /* Weak but necessary for `DRW_select_buffer_elem_get`. */ + obj->runtime.select_id = base_index; + } + select_ctx->objects_len = bases_len; + select_ctx->select_mode = select_mode; + memset(select_ctx->persmat, 0, sizeof(select_ctx->persmat)); +} /** \} */ diff --git a/source/blender/draw/modes/edit_mesh_mode.c b/source/blender/draw/modes/edit_mesh_mode.c index f0e35e47a66..f8247d7929e 100644 --- a/source/blender/draw/modes/edit_mesh_mode.c +++ b/source/blender/draw/modes/edit_mesh_mode.c @@ -128,8 +128,7 @@ typedef struct EDIT_MESH_Shaders { GPUShader *depth; /* Mesh analysis shader */ - GPUShader *mesh_analysis_face; - GPUShader *mesh_analysis_vertex; + GPUShader *mesh_analysis; } EDIT_MESH_Shaders; /* *********** STATIC *********** */ @@ -307,15 +306,9 @@ static void EDIT_MESH_engine_init(void *vedata) }); /* Mesh Analysis */ - sh_data->mesh_analysis_face = GPU_shader_create_from_arrays({ + sh_data->mesh_analysis = GPU_shader_create_from_arrays({ .vert = (const char *[]){lib, datatoc_edit_mesh_overlay_mesh_analysis_vert_glsl, NULL}, .frag = (const char *[]){datatoc_edit_mesh_overlay_mesh_analysis_frag_glsl, NULL}, - .defs = (const char *[]){sh_cfg_data->def, "#define FACE_COLOR\n", NULL}, - }); - sh_data->mesh_analysis_vertex = GPU_shader_create_from_arrays({ - .vert = (const char *[]){lib, datatoc_edit_mesh_overlay_mesh_analysis_vert_glsl, NULL}, - .frag = (const char *[]){datatoc_edit_mesh_overlay_mesh_analysis_frag_glsl, NULL}, - .defs = (const char *[]){sh_cfg_data->def, "#define VERTEX_COLOR\n", NULL}, }); MEM_freeN(lib); @@ -548,10 +541,9 @@ static void EDIT_MESH_cache_init(void *vedata) /* Mesh Analysis Pass */ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; psl->mesh_analysis_pass = DRW_pass_create("Mesh Analysis", state); - const bool is_vertex_color = scene->toolsettings->statvis.type == SCE_STATVIS_SHARP; - g_data->mesh_analysis_shgrp = DRW_shgroup_create( - is_vertex_color ? sh_data->mesh_analysis_vertex : sh_data->mesh_analysis_face, - psl->mesh_analysis_pass); + g_data->mesh_analysis_shgrp = DRW_shgroup_create(sh_data->mesh_analysis, + psl->mesh_analysis_pass); + DRW_shgroup_uniform_texture(g_data->mesh_analysis_shgrp, "weightTex", G_draw.weight_ramp); if (rv3d->rflag & RV3D_CLIPPING) { DRW_shgroup_state_enable(g_data->mesh_analysis_shgrp, DRW_STATE_CLIP_PLANES); } @@ -704,17 +696,10 @@ static void EDIT_MESH_cache_populate(void *vedata, Object *ob) geom = DRW_cache_mesh_surface_weights_get(ob); DRW_shgroup_call_no_cull(g_data->fweights_shgrp, geom, ob); } - - if (do_show_mesh_analysis && !XRAY_ACTIVE(v3d)) { - Mesh *me = (Mesh *)ob->data; - BMEditMesh *embm = me->edit_mesh; - const bool is_original = embm->mesh_eval_final && - (embm->mesh_eval_final->runtime.is_original == true); - if (is_original) { - geom = DRW_cache_mesh_surface_mesh_analysis_get(ob); - if (geom) { - DRW_shgroup_call_no_cull(g_data->mesh_analysis_shgrp, geom, ob); - } + else if (do_show_mesh_analysis && !XRAY_ACTIVE(v3d)) { + geom = DRW_cache_mesh_surface_mesh_analysis_get(ob); + if (geom) { + DRW_shgroup_call_no_cull(g_data->mesh_analysis_shgrp, geom, ob); } } @@ -727,7 +712,7 @@ static void EDIT_MESH_cache_populate(void *vedata, Object *ob) } if (vnormals_do) { - geom = DRW_mesh_batch_cache_get_edit_vertices(ob->data); + geom = DRW_mesh_batch_cache_get_edit_vnors(ob->data); DRW_shgroup_call_no_cull(g_data->vnormals_shgrp, geom, ob); } if (lnormals_do) { diff --git a/source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_frag.glsl b/source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_frag.glsl index 8581453e810..8d96c0e418f 100644 --- a/source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_frag.glsl +++ b/source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_frag.glsl @@ -1,12 +1,6 @@ out vec4 fragColor; -#ifdef FACE_COLOR -flat in vec4 weightColor; -#endif - -#ifdef VERTEX_COLOR in vec4 weightColor; -#endif void main() { diff --git a/source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_vert.glsl b/source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_vert.glsl index 7065ce3df7c..b89a3f407f9 100644 --- a/source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_vert.glsl +++ b/source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_vert.glsl @@ -1,14 +1,25 @@ in vec3 pos; -in vec4 weight_color; +in float weight; -#ifdef FACE_COLOR -flat out vec4 weightColor; -#endif +uniform sampler1D weightTex; -#ifdef VERTEX_COLOR out vec4 weightColor; -#endif + +vec3 weight_to_rgb(float t) +{ + if (t < 0.0) { + /* Minimum color, grey */ + return vec3(0.25, 0.25, 0.25); + } + else if (t > 1.0) { + /* Error color */ + return vec3(1.0, 0.0, 1.0); + } + else { + return texture(weightTex, t).rgb; + } +} void main() { @@ -16,7 +27,7 @@ void main() vec3 world_pos = point_object_to_world(pos); gl_Position = point_world_to_ndc(world_pos); - weightColor = vec4(weight_color.rgb, 1.0); + weightColor = vec4(weight_to_rgb(weight), 1.0); #ifdef USE_WORLD_CLIP_PLANES world_clip_planes_calc_clip_distance(world_pos); diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 625a52fc800..a78a63f1347 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1829,13 +1829,13 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, } } - /* check selection and object type filters */ - if ((ads->filterflag & ADS_FILTER_ONLYSEL) && - !((base->flag & BASE_SELECTED) /*|| (base == scene->basact)*/)) { - /* only selected should be shown */ - continue; + /* check selection and object type filters only for Object mode */ + if (ob->mode == OB_MODE_OBJECT) { + if ((ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & BASE_SELECTED))) { + /* only selected should be shown */ + continue; + } } - /* check if object belongs to the filtering group if option to filter * objects by the grouped status is on * - used to ease the process of doing multiple-character choreographies diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index 7a5b57b1ce6..bd4886817cd 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -60,9 +60,8 @@ typedef struct MPathTarget { Object *ob; /* source object */ bPoseChannel *pchan; /* source posechannel (if applicable) */ - /* "Evaluated" Copies (these come from the background COW copie - * that provide all the coordinates we want to save off) - */ + /* "Evaluated" Copies (these come from the background COW copy + * that provide all the coordinates we want to save off). */ Object *ob_eval; /* evaluated object */ } MPathTarget; diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 935d11a388f..e341a16378c 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -218,7 +218,7 @@ static int add_driver_with_target(ReportList *UNUSED(reports), /* Create a driver variable for the target * - For transform properties, we want to automatically use "transform channel" instead - * (The only issue is with quat rotations vs euler channels...) + * (The only issue is with quaternion rotations vs euler channels...) * - To avoid problems with transform properties depending on the final transform that they * control (thus creating pseudo-cycles - see T48734), we don't use transform channels * when both the source and destinations are in same places. diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index de6e4c2fd0d..7fd2338dbf3 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -1484,10 +1484,10 @@ short insert_keyframe(Main *bmain, flag); } } - } - if (values != value_buffer) { - MEM_freeN(values); + if (values != value_buffer) { + MEM_freeN(values); + } } BKE_animsys_free_nla_keyframing_context_cache(&tmp_nla_cache); @@ -1544,9 +1544,9 @@ static bool delete_keyframe_fcurve(AnimData *adt, FCurve *fcu, float cfra) static void deg_tag_after_keyframe_delete(Main *bmain, ID *id, AnimData *adt) { if (adt->action == NULL) { - /* In the case last f-curve wes removed need to inform dependency graph + /* In the case last f-curve was removed need to inform dependency graph * about relations update, since it needs to get rid of animation operation - * for this datablock. */ + * for this data-block. */ DEG_id_tag_update_ex(bmain, id, ID_RECALC_ANIMATION_NO_FLUSH); DEG_relations_tag_update(bmain); } diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index ccd0fc54611..7d31c6d3e3a 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -712,9 +712,9 @@ int ANIM_scene_get_keyingset_index(Scene *scene, KeyingSet *ks) } } - /* still here, so try builtins list too - * - builtins are from (<= -1) - * - none/invalid is (= 0) + /* Still here, so try built-ins list too: + * - Built-ins are from (<= -1). + * - None/Invalid is (= 0). */ index = BLI_findindex(&builtin_keyingsets, ks); if (index != -1) { diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index d2fa77f90be..1073034383d 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -769,7 +769,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) EditBone *ebone = ebone_iter->temp.ebone; - /* copy flags incase bone is pre-existing data */ + /* Copy flags in case bone is pre-existing data. */ ebone->flag = (ebone->flag & ~flag_copy) | (ebone_iter->flag & flag_copy); if (ebone_iter->parent == NULL) { diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 23ddf77e63d..eff621d7b71 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -46,6 +46,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_view3d.h" @@ -356,6 +357,8 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv } } + ED_outliner_select_sync_from_edit_bone_tag(C); + ED_armature_edit_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); @@ -1027,6 +1030,8 @@ static int armature_de_select_all_exec(bContext *C, wmOperator *op) } CTX_DATA_END; + ED_outliner_select_sync_from_edit_bone_tag(C); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); return OPERATOR_FINISHED; @@ -1148,6 +1153,8 @@ static int armature_de_select_more_exec(bContext *C, wmOperator *UNUSED(op)) WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); } MEM_freeN(objects); + + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -1178,6 +1185,8 @@ static int armature_de_select_less_exec(bContext *C, wmOperator *UNUSED(op)) WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); } MEM_freeN(objects); + + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -1569,6 +1578,8 @@ static int armature_select_similar_exec(bContext *C, wmOperator *op) #undef STRUCT_SIZE_AND_OFFSET + ED_outliner_select_sync_from_edit_bone_tag(C); + return OPERATOR_FINISHED; } @@ -1663,6 +1674,8 @@ static int armature_select_hierarchy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_edit_bone_tag(C); + ED_armature_edit_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); @@ -1748,6 +1761,8 @@ static int armature_select_mirror_exec(bContext *C, wmOperator *op) arm->act_edbone = ebone_mirror_act; } + ED_outliner_select_sync_from_edit_bone_tag(C); + ED_armature_edit_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); @@ -1876,6 +1891,7 @@ static int armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const if (changed) { arm->act_edbone = ebone_dst; + ED_outliner_select_sync_from_edit_bone_tag(C); ED_armature_edit_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 8434fee6e78..beec2f8358f 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -54,6 +54,7 @@ #include "ED_keyframing.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_view3d.h" @@ -449,6 +450,8 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve selectconnected_posebonechildren(base->object, curBone, extend); } + ED_outliner_select_sync_from_pose_bone_tag(C); + ED_pose_bone_select_tag_update(base->object); return OPERATOR_FINISHED; @@ -514,6 +517,8 @@ static int pose_de_select_all_exec(bContext *C, wmOperator *op) } CTX_DATA_END; + ED_outliner_select_sync_from_pose_bone_tag(C); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); return OPERATOR_FINISHED; @@ -560,6 +565,8 @@ static int pose_select_parent_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_pose_bone_tag(C); + ED_pose_bone_select_tag_update(ob); return OPERATOR_FINISHED; } @@ -624,6 +631,8 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_pose_bone_tag(C); + return OPERATOR_FINISHED; } @@ -712,6 +721,8 @@ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_pose_bone_tag(C); + ED_pose_bone_select_tag_update(ob); return OPERATOR_FINISHED; @@ -1061,6 +1072,8 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op) /* report done status */ if (changed) { + ED_outliner_select_sync_from_pose_bone_tag(C); + return OPERATOR_FINISHED; } else { @@ -1172,6 +1185,8 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); + ED_outliner_select_sync_from_pose_bone_tag(C); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index c4bb5eec723..c93531bb6cc 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1399,8 +1399,12 @@ static int separate_exec(bContext *C, wmOperator *op) } /* 2. Duplicate the object and data. */ - newbase = ED_object_add_duplicate( - bmain, scene, view_layer, oldbase, 0); /* 0 = fully linked. */ + newbase = ED_object_add_duplicate(bmain, + scene, + view_layer, + oldbase, + /* 0 = fully linked. */ + 0); DEG_relations_tag_update(bmain); newob = newbase->object; diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 5e0053782d4..c7c19aa2d02 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -120,7 +120,7 @@ struct CurveDrawData { struct { float mouse[2]; - /* used incase we can't calculate the depth */ + /* Used in case we can't calculate the depth. */ float location_world[3]; float location_world_valid[3]; @@ -1053,7 +1053,7 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - /* fallback (incase we can't find the depth on first test) */ + /* Fallback (in case we can't find the depth on first test). */ { const float mval_fl[2] = {UNPACK2(event->mval)}; float center[3]; diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c index d23965269ab..fa9c0f1fbb2 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c @@ -363,8 +363,8 @@ static void gizmo_arrow_exit(bContext *C, wmGizmo *gz, const bool cancel) const bool is_prop_valid = WM_gizmo_target_property_is_valid(gz_prop); if (!cancel) { - /* Assign incase applying the operation needs an updated offset - * editmesh bisect needs this. */ + /* Assign in case applying the operation needs an updated offset + * edit-mesh bisect needs this. */ if (is_prop_valid) { const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform"); const bool constrained = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED) != 0; 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 e2a86469da1..406f76bc65e 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -358,7 +358,7 @@ static void gizmo_cage3d_draw_intern( bool show = false; if (gz->highlight_part == ED_GIZMO_CAGE3D_PART_TRANSLATE) { /* Only show if we're drawing the center handle - * otherwise the entire rectangle is the hotspot. */ + * otherwise the entire rectangle is the hot-spot. */ if (draw_options & ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE) { show = true; } diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index dce6ed29c05..697d06aa098 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -936,7 +936,6 @@ static void annotation_draw_data_layers( /* draw a short status message in the top-right corner */ static void annotation_draw_status_text(const bGPdata *gpd, ARegion *ar) { - rcti rect; /* Cannot draw any status text when drawing OpenGL Renders */ if (G.f & G_FLAG_RENDER_VIEWPORT) { @@ -944,7 +943,7 @@ static void annotation_draw_status_text(const bGPdata *gpd, ARegion *ar) } /* Get bounds of region - Necessary to avoid problems with region overlap */ - ED_region_visible_rect(ar, &rect); + const rcti *rect = ED_region_visible_rect(ar); /* for now, this should only be used to indicate when we are in stroke editmode */ if (gpd->flag & GP_DATA_STROKE_EDITMODE) { @@ -956,8 +955,8 @@ static void annotation_draw_status_text(const bGPdata *gpd, ARegion *ar) BLF_width_and_height( font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - int xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; - int yco = (rect.ymax - U.widget_unit); + int xco = (rect->xmax - U.widget_unit) - (int)printable_size[0]; + int yco = (rect->ymax - U.widget_unit); /* text label */ UI_FontThemeColor(font_id, TH_TEXT_HI); diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index c9c4a67644e..22f1753a810 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1156,7 +1156,7 @@ static tGPsdata *gp_session_initpaint(bContext *C) /* create new context data */ p = MEM_callocN(sizeof(tGPsdata), "Annotation Drawing Data"); - /* Try to initialise context data + /* Try to initialize context data * WARNING: This may not always succeed (e.g. using GP in an annotation-only context) */ if (gp_session_initdata(C, p) == 0) { @@ -1252,15 +1252,6 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps /* Ensure active frame is set correctly... */ p->gpf = p->gpl->actframe; - /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on - * (though this is only available in editmode) - */ - if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { - if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { - p->flags |= GP_PAINTFLAG_SELECTMASK; - } - } - if (has_layer_to_erase == false) { p->status = GP_STATUS_CAPTURE; // if (G.debug & G_DEBUG) @@ -2242,11 +2233,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (p->ar) { - rcti region_rect; - - /* Perform bounds check using */ - ED_region_visible_rect(p->ar, ®ion_rect); - in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); + /* Perform bounds check. */ + const rcti *region_rect = ED_region_visible_rect(p->ar); + in_bounds = BLI_rcti_isect_pt_v(region_rect, event->mval); } else { /* No region */ diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 2b31af5ff1f..7c76f3aeab6 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -1173,15 +1173,14 @@ void ED_gp_draw_fill(tGPDdraw *tgpw) /* draw a short status message in the top-right corner */ static void UNUSED_FUNCTION(gp_draw_status_text)(const bGPdata *gpd, ARegion *ar) { - rcti rect; /* Cannot draw any status text when drawing OpenGL Renders */ if (G.f & G_FLAG_RENDER_VIEWPORT) { return; } - /* Get bounds of region - Necessary to avoid problems with region overlap */ - ED_region_visible_rect(ar, &rect); + /* Get bounds of region - Necessary to avoid problems with region overlap. */ + const rcti *rect = ED_region_visible_rect(ar); /* for now, this should only be used to indicate when we are in stroke editmode */ if (gpd->flag & GP_DATA_STROKE_EDITMODE) { @@ -1193,8 +1192,8 @@ static void UNUSED_FUNCTION(gp_draw_status_text)(const bGPdata *gpd, ARegion *ar BLF_width_and_height( font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - int xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; - int yco = (rect.ymax - U.widget_unit); + int xco = (rect->xmax - U.widget_unit) - (int)printable_size[0]; + int yco = (rect->ymax - U.widget_unit); /* text label */ UI_FontThemeColor(font_id, TH_TEXT_HI); diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 8e4d2655ef0..4312bed657e 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -105,6 +105,7 @@ typedef struct tGP_BrushEditData { eGP_Sculpt_Types brush_type; eGP_Sculpt_Types brush_type_old; eGP_Sculpt_Flag flag; + eGP_Sculpt_SelectMaskFlag mask; /* Space Conversion Data */ GP_SpaceConversion gsc; @@ -1276,6 +1277,9 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op) gso->sa = CTX_wm_area(C); gso->ar = CTX_wm_region(C); + /* save mask */ + gso->mask = ts->gpencil_selectmode_sculpt; + /* multiframe settings */ gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; @@ -1486,7 +1490,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, /* Skip if neither one is selected * (and we are only allowed to edit/consider selected points) */ - if ((gso->settings->flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) && (!gso->is_weight_mode)) { + if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) && (!gso->is_weight_mode)) { if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { include_last = false; continue; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index f3ab0b45122..d03c53bf9cb 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -220,6 +220,17 @@ void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) } /* set select mode */ +static bool gpencil_selectmode_toggle_poll(bContext *C) +{ + /* edit only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL) || (ob->mode != OB_MODE_EDIT_GPENCIL)) { + return false; + } + + return ED_operator_view3d_active(C); +} + static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -227,7 +238,7 @@ static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op) const int mode = RNA_int_get(op->ptr, "mode"); /* Just set mode */ - ts->gpencil_selectmode = mode; + ts->gpencil_selectmode_edit = mode; WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); @@ -246,7 +257,7 @@ void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot) /* callbacks */ ot->exec = gpencil_selectmode_toggle_exec; - ot->poll = gp_strokes_edit3d_poll; + ot->poll = gpencil_selectmode_toggle_poll; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; @@ -3331,7 +3342,7 @@ typedef enum eGP_ReprojectModes { GP_REPROJECT_FRONT = 0, GP_REPROJECT_SIDE, GP_REPROJECT_TOP, - /* On same plane, parallel to viewplane */ + /* On same plane, parallel to view-plane. */ GP_REPROJECT_VIEW, /* Reprojected on to the scene geometry */ GP_REPROJECT_SURFACE, @@ -3374,11 +3385,10 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { float xy[2]; - /* 3D to Screenspace */ - /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace - * coordinates, resulting in lost precision, which in turn causes stairstepping - * artifacts in the final points. - */ + /* 3D to Screen-space */ + /* Note: We can't use gp_point_to_xy() here because that uses ints for the screen-space + * coordinates, resulting in lost precision, which in turn causes stair-stepping + * artifacts in the final points. */ bGPDspoint pt2; gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2); gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); @@ -3427,16 +3437,15 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) /* apply parent again */ gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt); } - /* Project screenspace back to 3D space (from current perspective) - * so that all points have been treated the same way - */ + /* Project screen-space back to 3D space (from current perspective) + * so that all points have been treated the same way. */ else if (mode == GP_REPROJECT_VIEW) { - /* Planar - All on same plane parallel to the viewplane */ + /* Planar - All on same plane parallel to the view-plane. */ gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); } else { /* Geometry - Snap to surfaces of visible geometry */ - /* XXX: There will be precision loss (possible stairstep artifacts) + /* XXX: There will be precision loss (possible stair-step artifacts) * from this conversion to satisfy the API's */ const int screen_co[2] = {(int)xy[0], (int)xy[1]}; @@ -3548,7 +3557,7 @@ static void gp_smooth_stroke(bContext *C, wmOperator *op) } if (smooth_thickness) { /* thickness need to repeat process several times */ - for (int r2 = 0; r2 < r * 10; r2++) { + for (int r2 = 0; r2 < r * 20; r2++) { BKE_gpencil_smooth_stroke_thickness(gps, i, factor); } } @@ -4052,8 +4061,10 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) } /* add duplicate materials */ - ma = give_current_material( - ob, gps->mat_nr + 1); /* XXX same material can be in multiple slots */ + + /* XXX same material can be in multiple slots. */ + ma = give_current_material(ob, gps->mat_nr + 1); + idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma); /* selected points mode */ @@ -4302,7 +4313,7 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5); + prop = RNA_def_int(ot->srna, "repeat", 1, 1, 50, "Repeat", "", 1, 20); RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f); @@ -4438,7 +4449,7 @@ static int gpencil_cutter_lasso_select(bContext *C, if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) { continue; } - /* convert point coords to screenspace */ + /* convert point coords to screen-space */ const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); if (is_inside) { tot_inside++; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 2a608d44a0b..715665fe6e9 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -635,6 +635,10 @@ struct GP_EditableStrokes_Iter { } \ (void)0 +#define GPENCIL_ANY_SCULPT_MASK(flag) \ + ((flag & (GP_SCULPT_MASK_SELECTMODE_POINT | GP_SCULPT_MASK_SELECTMODE_STROKE | \ + GP_SCULPT_MASK_SELECTMODE_SEGMENT))) + /* ****************************************************** */ #endif /* __GPENCIL_INTERN_H__ */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 06ff0e744b9..ee78a947b55 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -563,7 +563,7 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) { bGPdata *gpd = p->gpd; - short num_points = gpd->runtime.sbuffer_used; + const short num_points = gpd->runtime.sbuffer_used; /* Do nothing if not enough points to smooth out */ if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { @@ -571,10 +571,7 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) } tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; - float steps = 4.0f; - if (idx < 4) { - steps--; - } + const float steps = (idx < 4) ? 3.0f : 4.0f; tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; @@ -583,29 +580,36 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) float sco[2] = {0.0f}; float a[2], b[2], c[2], d[2]; + float pressure = 0.0f; const float average_fac = 1.0f / steps; /* Compute smoothed coordinate by taking the ones nearby */ if (pta) { copy_v2_v2(a, &pta->x); madd_v2_v2fl(sco, a, average_fac); + pressure += pta->pressure * average_fac; } if (ptb) { copy_v2_v2(b, &ptb->x); madd_v2_v2fl(sco, b, average_fac); + pressure += ptb->pressure * average_fac; } if (ptc) { copy_v2_v2(c, &ptc->x); madd_v2_v2fl(sco, c, average_fac); + pressure += ptc->pressure * average_fac; } if (ptd) { copy_v2_v2(d, &ptd->x); madd_v2_v2fl(sco, d, average_fac); + pressure += ptd->pressure * average_fac; } - /* Based on influence factor, blend between original and optimal smoothed coordinate */ + /* Based on influence factor, blend between original and optimal smoothed coordinate. */ interp_v2_v2v2(c, c, sco, inf); copy_v2_v2(&ptc->x, c); + /* Interpolate pressure. */ + ptc->pressure = interpf(ptc->pressure, pressure, inf); } /* add current stroke-point to buffer (returns whether point was successfully added) */ @@ -2128,15 +2132,6 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps /* Ensure this gets set... */ p->gpf = p->gpl->actframe; - /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on - * (though this is only available in editmode) - */ - if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { - if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { - p->flags |= GP_PAINTFLAG_SELECTMASK; - } - } - if (has_layer_to_erase == false) { p->status = GP_STATUS_ERROR; return; @@ -3460,12 +3455,18 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* We don't pass on key events, GP is used with key-modifiers - * prevents Dkey to insert drivers. */ if (ISKEYBOARD(event->type)) { - if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) { + if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY)) { /* allow some keys: * - for frame changing [#33412] * - for undo (during sketching sessions) */ } + else if (event->type == ZKEY) { + if (event->ctrl) { + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } else if (ELEM(event->type, PAD0, PAD1, PAD2, PAD3, PAD4, PAD5, PAD6, PAD7, PAD8, PAD9)) { /* allow numpad keys so that camera/view manipulations can still take place * - PAD0 in particular is really important for Grease Pencil drawing, @@ -3623,11 +3624,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (p->ar) { - rcti region_rect; - /* Perform bounds check using */ - ED_region_visible_rect(p->ar, ®ion_rect); - in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); + const rcti *region_rect = ED_region_visible_rect(p->ar); + in_bounds = BLI_rcti_isect_pt_v(region_rect, event->mval); } else { /* No region */ diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 5a962809954..4c185b7fb8a 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -67,10 +67,34 @@ /** \name Shared Utilities * \{ */ +/* Convert sculpt mask mode to Select mode */ +static int gpencil_select_mode_from_sculpt(eGP_Sculpt_SelectMaskFlag mode) +{ + if (mode & GP_SCULPT_MASK_SELECTMODE_POINT) { + return GP_SELECTMODE_POINT; + } + else if (mode & GP_SCULPT_MASK_SELECTMODE_STROKE) { + return GP_SELECTMODE_STROKE; + } + else if (GP_SCULPT_MASK_SELECTMODE_SEGMENT) { + return GP_SELECTMODE_SEGMENT; + } + else { + return GP_SELECTMODE_POINT; + } +} + static bool gpencil_select_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); + if (GPENCIL_SCULPT_MODE(gpd)) { + ToolSettings *ts = CTX_data_tool_settings(C); + if (!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) { + return false; + } + } + /* we just need some visible strokes, and to be in editmode or other modes only to catch event */ if (GPENCIL_ANY_MODE(gpd)) { /* TODO: include a check for visible strokes? */ @@ -910,7 +934,11 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); ToolSettings *ts = CTX_data_tool_settings(C); - const int selectmode = ts->gpencil_selectmode; + Object *ob = CTX_data_active_object(C); + + const int selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? + gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : + ts->gpencil_selectmode_edit; const float scale = ts->gp_sculpt.isect_threshold; /* if not edit/sculpt mode, the event is catched but not processed */ @@ -1016,12 +1044,18 @@ static int gpencil_generic_select_exec(bContext *C, GPencilTestFn is_inside_fn, void *user_data) { + Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); ToolSettings *ts = CTX_data_tool_settings(C); ScrArea *sa = CTX_wm_area(C); - const bool strokemode = ((ts->gpencil_selectmode == GP_SELECTMODE_STROKE) && + + const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? + gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : + ts->gpencil_selectmode_edit; + + const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); - const bool segmentmode = ((ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) && + const bool segmentmode = ((selectmode == GP_SELECTMODE_SEGMENT) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); const float scale = ts->gp_sculpt.isect_threshold; @@ -1064,7 +1098,6 @@ static int gpencil_generic_select_exec(bContext *C, for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { /* convert point coords to screenspace */ const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); - if (strokemode == false) { const bool is_select = (pt->flag & GP_SPOINT_SELECT) != 0; const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); @@ -1285,6 +1318,7 @@ static void deselect_all_selected(bContext *C) static int gpencil_select_exec(bContext *C, wmOperator *op) { ScrArea *sa = CTX_wm_area(C); + Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); ToolSettings *ts = CTX_data_tool_settings(C); const float scale = ts->gp_sculpt.isect_threshold; @@ -1315,8 +1349,12 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) } /* if select mode is stroke, use whole stroke */ - if (ts->gpencil_selectmode == GP_SELECTMODE_STROKE) { - whole = true; + if ((ob) && (ob->mode == OB_MODE_SCULPT_GPENCIL)) { + whole = (bool)(gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) == + GP_SELECTMODE_STROKE); + } + else { + whole = (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE); } /* init space conversion stuff */ @@ -1417,7 +1455,11 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) hit_stroke->flag |= GP_STROKE_SELECT; /* expand selection to segment */ - if (ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) { + const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? + gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : + ts->gpencil_selectmode_edit; + + if (selectmode == GP_SELECTMODE_SEGMENT) { float r_hita[3], r_hitb[3]; bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT); ED_gpencil_select_stroke_segment( diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index a232e1376d3..cb6c66ed795 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -25,9 +25,9 @@ #define __ED_ANIM_API_H__ struct AnimData; +struct Depsgraph; struct ID; struct ListBase; -struct Depsgraph; struct ARegion; struct Main; diff --git a/source/blender/editors/include/ED_outliner.h b/source/blender/editors/include/ED_outliner.h index e94aedc2b2b..30e2624604c 100644 --- a/source/blender/editors/include/ED_outliner.h +++ b/source/blender/editors/include/ED_outliner.h @@ -30,4 +30,17 @@ bool ED_outliner_collections_editor_poll(struct bContext *C); void ED_outliner_selected_objects_get(const struct bContext *C, struct ListBase *objects); +Base *ED_outliner_give_base_under_cursor(struct bContext *C, const int mval[2]); + +void ED_outliner_select_sync_from_object_tag(struct bContext *C); +void ED_outliner_select_sync_from_edit_bone_tag(struct bContext *C); +void ED_outliner_select_sync_from_pose_bone_tag(struct bContext *C); +void ED_outliner_select_sync_from_sequence_tag(struct bContext *C); + +bool ED_outliner_select_sync_is_dirty(const struct bContext *C); + +void ED_outliner_select_sync_from_outliner(struct bContext *C, struct SpaceOutliner *soops); + +void ED_outliner_select_sync_flag_outliners(const struct bContext *C); + #endif /* __ED_OUTLINER_H__ */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index e67a3b003fc..c7ee7be49b5 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -124,7 +124,8 @@ void ED_region_image_metadata_draw( void ED_region_image_metadata_panel_draw(struct ImBuf *ibuf, struct uiLayout *layout); void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy); float ED_region_blend_alpha(struct ARegion *ar); -void ED_region_visible_rect(struct ARegion *ar, struct rcti *rect); +void ED_region_visible_rect_calc(struct ARegion *ar, struct rcti *rect); +const rcti *ED_region_visible_rect(ARegion *ar); bool ED_region_is_overlap(int spacetype, int regiontype); int ED_region_snap_size_test(const struct ARegion *ar); diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 86e108a26c6..034e002f86a 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -45,4 +45,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, /* sculpt_undo.c */ void ED_sculpt_undosys_type(struct UndoType *ut); +void ED_sculpt_undo_geometry_begin(struct Object *ob); +void ED_sculpt_undo_geometry_end(struct Object *ob); + #endif /* __ED_SCULPT_H__ */ diff --git a/source/blender/editors/include/ED_text.h b/source/blender/editors/include/ED_text.h index ed71439bd37..ade1dde6c33 100644 --- a/source/blender/editors/include/ED_text.h +++ b/source/blender/editors/include/ED_text.h @@ -26,8 +26,10 @@ struct ARegion; struct SpaceText; +struct Text; struct UndoStep; struct UndoType; +struct bContext; bool ED_text_region_location_from_cursor(struct SpaceText *st, struct ARegion *ar, @@ -39,4 +41,7 @@ void ED_text_undosys_type(struct UndoType *ut); struct UndoStep *ED_text_undo_push_init(struct bContext *C); +/* text_format.c */ +bool ED_text_is_syntax_highlight_supported(struct Text *text); + #endif /* __ED_TEXT_H__ */ diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 3b080b6df95..29022adac6c 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -255,6 +255,7 @@ typedef enum ThemeColorID { TH_MATCH, /* highlight color for search matches */ TH_SELECT_HIGHLIGHT, /* highlight color for selected outliner item */ + TH_SELECT_ACTIVE, /* highlight color for active outliner item */ TH_SELECTED_OBJECT, /* selected object color for outliner */ TH_ACTIVE_OBJECT, /* active object color for outliner */ TH_EDITED_OBJECT, /* edited object color for outliner */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index f9c65249918..7a123599be5 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2553,8 +2553,8 @@ void ui_but_string_get_ex(uiBut *but, } } else { + const int int_digits_num = integer_digits_f(value); if (use_exp_float) { - const int int_digits_num = integer_digits_f(value); if (int_digits_num < -6 || int_digits_num > 12) { BLI_snprintf(str, maxlen, "%.*g", prec, value); if (r_use_exp_float) { @@ -2568,10 +2568,8 @@ void ui_but_string_get_ex(uiBut *but, } } else { -#if 0 /* TODO, but will likely break some stuff, so better after 2.79 release. */ prec -= int_digits_num; CLAMP(prec, 0, UI_PRECISION_FLOAT_MAX); -#endif BLI_snprintf(str, maxlen, "%.*f", prec, value); } } diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 88fe8704082..22b75da4968 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -523,6 +523,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) /* determine if we can key a single component of an array */ const bool is_array = RNA_property_array_length(&but->rnapoin, but->rnaprop) != 0; const bool is_array_component = (is_array && but->rnaindex != -1); + const bool is_whole_array = (is_array && but->rnaindex == -1); const int override_status = RNA_property_override_library_status(ptr, prop, -1); const bool is_overridable = (override_status & RNA_OVERRIDE_STATUS_OVERRIDABLE) != 0; @@ -658,21 +659,23 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) 1); } - uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Driver"), - ICON_NONE, - "ANIM_OT_copy_driver_button"); - if (ANIM_driver_can_paste()) { + if (!is_whole_array) { uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Paste Driver"), + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Driver"), ICON_NONE, - "ANIM_OT_paste_driver_button"); - } + "ANIM_OT_copy_driver_button"); + if (ANIM_driver_can_paste()) { + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Paste Driver"), + ICON_NONE, + "ANIM_OT_paste_driver_button"); + } - uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Edit Driver"), - ICON_DRIVER, - "ANIM_OT_driver_button_edit"); + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Edit Driver"), + ICON_DRIVER, + "ANIM_OT_driver_button_edit"); + } uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Open Drivers Editor"), @@ -690,11 +693,13 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) ICON_DRIVER, "ANIM_OT_driver_button_add"); - if (ANIM_driver_can_paste()) { - uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Paste Driver"), - ICON_NONE, - "ANIM_OT_paste_driver_button"); + if (!is_whole_array) { + if (ANIM_driver_can_paste()) { + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Paste Driver"), + ICON_NONE, + "ANIM_OT_paste_driver_button"); + } } uiItemO(layout, @@ -862,7 +867,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) "UI_OT_unset_property_button"); } - if (is_idprop && !is_array_component && ELEM(type, PROP_INT, PROP_FLOAT)) { + if (is_idprop && !is_array && ELEM(type, PROP_INT, PROP_FLOAT)) { uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Assign Value as Default"), ICON_NONE, @@ -899,7 +904,8 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) ICON_NONE, "UI_OT_copy_data_path_button"); - if (ptr->id.data && ELEM(type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM)) { + if (ptr->id.data && !is_whole_array && + ELEM(type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM)) { uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy As New Driver"), ICON_NONE, diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index 658aa4f67f9..efbe5922aa5 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -51,6 +51,7 @@ #include "ED_space_api.h" #include "ED_screen.h" #include "ED_view3d.h" +#include "ED_outliner.h" #include "interface_intern.h" #include "interface_eyedropper_intern.h" @@ -67,6 +68,7 @@ typedef struct DataDropper { ID *init_id; /* for resetting on cancel */ + ScrArea *cursor_area; /* Area under the cursor */ ARegionType *art; void *draw_handle_pixel; char name[200]; @@ -103,6 +105,7 @@ static int datadropper_init(bContext *C, wmOperator *op) ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + ddr->cursor_area = CTX_wm_area(C); ddr->art = art; ddr->draw_handle_pixel = ED_region_draw_cb_activate( art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); @@ -141,7 +144,7 @@ static void datadropper_exit(bContext *C, wmOperator *op) /* *** datadropper id helper functions *** */ /** - * \brief get the ID from the screen. + * \brief get the ID from the 3D view or outliner. */ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id) { @@ -155,7 +158,7 @@ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int ddr->name[0] = '\0'; if (sa) { - if (sa->spacetype == SPACE_VIEW3D) { + if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) { ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my); if (ar) { const int mval[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; @@ -167,7 +170,13 @@ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int /* grr, always draw else we leave stale text */ ED_region_tag_redraw(ar); - base = ED_view3d_give_base_under_cursor(C, mval); + if (sa->spacetype == SPACE_VIEW3D) { + base = ED_view3d_give_base_under_cursor(C, mval); + } + else { + base = ED_outliner_give_base_under_cursor(C, mval); + } + if (base) { Object *ob = base->object; ID *id = NULL; @@ -232,6 +241,36 @@ static void datadropper_cancel(bContext *C, wmOperator *op) datadropper_exit(C, op); } +/* To switch the draw callback when region under mouse event changes */ +static void datadropper_set_draw_callback_region(bContext *C, + DataDropper *ddr, + const int mx, + const int my) +{ + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, -1, mx, my); + + if (sa) { + /* If spacetype changed */ + if (sa->spacetype != ddr->cursor_area->spacetype) { + /* Remove old callback */ + ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel); + + /* Redraw old area */ + ARegion *ar = BKE_area_find_region_type(ddr->cursor_area, RGN_TYPE_WINDOW); + ED_region_tag_redraw(ar); + + /* Set draw callback in new region */ + ARegionType *art = BKE_regiontype_from_id(sa->type, RGN_TYPE_WINDOW); + + ddr->cursor_area = sa; + ddr->art = art; + ddr->draw_handle_pixel = ED_region_draw_cb_activate( + art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); + } + } +} + /* main modal status check */ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) { @@ -260,6 +299,10 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) } else if (event->type == MOUSEMOVE) { ID *id = NULL; + + /* Set the region for eyedropper cursor text drawing */ + datadropper_set_draw_callback_region(C, ddr, event->x, event->y); + datadropper_id_sample_pt(C, ddr, event->x, event->y, &id); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 52933a89045..19eb9699fc8 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4001,6 +4001,11 @@ static int ui_do_but_HOTKEYEVT(bContext *C, return WM_UI_HANDLER_CONTINUE; } else if (event->type == UNKNOWNKEY) { + WM_report(RPT_WARNING, "Unsupported key: Unknown"); + return WM_UI_HANDLER_CONTINUE; + } + else if (event->type == CAPSLOCKKEY) { + WM_report(RPT_WARNING, "Unsupported key: CapsLock"); return WM_UI_HANDLER_CONTINUE; } @@ -4795,37 +4800,24 @@ static int ui_do_but_NUM( if (click) { /* we can click on the side arrows to increment/decrement, * or click inside to edit the value directly */ - float tempf, softmin, softmax; - int temp; - - softmin = but->softmin; - softmax = but->softmax; + const float softmin = but->softmin; + const float softmax = but->softmax; if (!ui_but_is_float(but)) { - if (but->drawflag & UI_BUT_ACTIVE_LEFT) { + /* Integer Value. */ + if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - - temp = (int)data->value - 1; - if (temp >= softmin && temp <= softmax) { - data->value = (double)temp; + const int value_step = (int)but->a1; + BLI_assert(value_step > 0); + const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ? + (double)max_ii((int)softmin, (int)data->value - value_step) : + (double)min_ii((int)softmax, (int)data->value + value_step); + if (value_test != data->value) { + data->value = (double)value_test; } else { data->cancel = true; } - - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) { - button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - - temp = (int)data->value + 1; - if (temp >= softmin && temp <= softmax) { - data->value = (double)temp; - } - else { - data->cancel = true; - } - button_activate_state(C, but, BUTTON_STATE_EXIT); } else { @@ -4833,26 +4825,20 @@ static int ui_do_but_NUM( } } else { - if (but->drawflag & UI_BUT_ACTIVE_LEFT) { + /* Float Value. */ + if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - - tempf = (float)data->value - (UI_PRECISION_FLOAT_SCALE * but->a1); - if (tempf < softmin) { - tempf = softmin; + const double value_step = (double)but->a1 * UI_PRECISION_FLOAT_SCALE; + BLI_assert(value_step > 0.0f); + const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ? + (double)max_ff(softmin, (float)(data->value - value_step)) : + (double)min_ff(softmax, (float)(data->value + value_step)); + if (value_test != data->value) { + data->value = value_test; } - data->value = tempf; - - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) { - button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - - tempf = (float)data->value + (UI_PRECISION_FLOAT_SCALE * but->a1); - if (tempf > softmax) { - tempf = softmax; + else { + data->cancel = true; } - data->value = tempf; - button_activate_state(C, but, BUTTON_STATE_EXIT); } else { @@ -8198,7 +8184,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) else { /* Do this so we can still mouse-up, closing the menu and running the button. * This is nice to support but there are times when the button gets left pressed. - * Keep disavled for now. */ + * Keep disabled for now. */ WM_event_remove_timer(data->wm, data->window, data->hold_action_timer); data->hold_action_timer = NULL; } diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 25fedf8519a..78eed98eb77 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -648,7 +648,7 @@ static void ui_item_array(uiLayout *layout, * to work with common cases, but may need to be re-worked */ /* special case, boolean array in a menu, this could be used in a more generic way too */ - if (ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA) && !expand) { + if (ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA) && !expand && ELEM(len, 3, 4)) { uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, 0, 0, w, UI_UNIT_Y); } else { @@ -1832,7 +1832,9 @@ static void ui_item_rna_size(uiLayout *layout, h += len * UI_UNIT_Y; } } - else if (ui_layout_variable_size(layout)) { + + /* Increase width requirement if in a variable size layout. */ + if (ui_layout_variable_size(layout)) { if (type == PROP_BOOLEAN && name[0]) { w += UI_UNIT_X / 5; } diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index f5a894d7620..fbd422a9752 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -172,7 +172,8 @@ static bool copy_as_driver_button_poll(bContext *C) UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (ptr.id.data && ptr.data && prop && - ELEM(RNA_property_type(prop), PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM)) { + ELEM(RNA_property_type(prop), PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM) && + (index >= 0 || !RNA_property_array_check(prop))) { path = RNA_path_from_ID_to_property(&ptr, prop); if (path) { @@ -1118,7 +1119,7 @@ static int reports_to_text_exec(bContext *C, wmOperator *UNUSED(op)) txt = BKE_text_add(bmain, "Recent Reports"); /* convert entire list to a display string, and add this to the text-block - * - if commandline debug option enabled, show debug reports too + * - if command-line debug option enabled, show debug reports too * - otherwise, up to info (which is what users normally see) */ str = BKE_reports_string(reports, (G.debug & G_DEBUG) ? RPT_DEBUG : RPT_INFO); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 37eb1770f68..22573c9c7d2 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2523,7 +2523,7 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) /* enabled */ UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiItemR(row, &ptr, "mute", 0, "", (con->flag & CONSTRAINT_OFF) ? ICON_HIDE_ON : ICON_HIDE_OFF); + uiItemR(row, &ptr, "mute", 0, "", 0); UI_block_emboss_set(block, UI_EMBOSS); uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT); @@ -6392,22 +6392,17 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C) block, UI_BTYPE_LABEL, 0, icon, 2, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, ""); } - UI_block_emboss_set(block, UI_EMBOSS); - - uiDefBut(block, - UI_BTYPE_LABEL, - 0, - report->message, - UI_UNIT_X + 5, - 0, - UI_UNIT_X + width, - UI_UNIT_Y, - NULL, - 0.0f, - 0.0f, - 0, - 0, - ""); + but = uiDefButO(block, + UI_BTYPE_BUT, + "SCREEN_OT_info_log_show", + WM_OP_INVOKE_REGION_WIN, + report->message, + UI_UNIT_X + 5, + 0, + UI_UNIT_X + width, + UI_UNIT_Y, + "Show in Info Log"); + rgba_float_to_uchar(but->col, rti->col); } void uiTemplateInputStatus(uiLayout *layout, struct bContext *C) diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index bf52cc625c2..9c8787d002f 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -417,8 +417,9 @@ void ui_rna_collection_search_cb(const struct bContext *C, } } - name = RNA_struct_name_get_alloc( - &itemptr, NULL, 0, NULL); /* could use the string length here */ + /* Could use the string length here. */ + name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); + iconid = 0; if (itemptr.type && RNA_struct_is_ID(itemptr.type)) { iconid = ui_id_icon_get(C, itemptr.data, false); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index c31de60c7ed..7c5d5401d08 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -794,6 +794,10 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) cp = ts->selected_highlight; break; + case TH_SELECT_ACTIVE: + cp = ts->active; + break; + case TH_SELECTED_OBJECT: cp = ts->selected_object; break; diff --git a/source/blender/editors/interface/view2d_gizmo_navigate.c b/source/blender/editors/interface/view2d_gizmo_navigate.c index 883f16c63f2..9b15f2309a1 100644 --- a/source/blender/editors/interface/view2d_gizmo_navigate.c +++ b/source/blender/editors/interface/view2d_gizmo_navigate.c @@ -201,21 +201,20 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g struct NavigateWidgetGroup *navgroup = gzgroup->customdata; ARegion *ar = CTX_wm_region(C); - rcti rect_visible; - ED_region_visible_rect(ar, &rect_visible); + const rcti *rect_visible = ED_region_visible_rect(ar); - if ((navgroup->state.rect_visible.xmax == rect_visible.xmax) && - (navgroup->state.rect_visible.ymax == rect_visible.ymax)) { + if ((navgroup->state.rect_visible.xmax == rect_visible->xmax) && + (navgroup->state.rect_visible.ymax == rect_visible->ymax)) { return; } - navgroup->state.rect_visible = rect_visible; + navgroup->state.rect_visible = *rect_visible; const float icon_size = GIZMO_SIZE; const float icon_offset_mini = icon_size * GIZMO_MINI_OFFSET_FAC * UI_DPI_FAC; const float co[2] = { - rect_visible.xmax - (icon_offset_mini * 0.75f), - rect_visible.ymax - (icon_offset_mini * 0.75f), + rect_visible->xmax - (icon_offset_mini * 0.75f), + rect_visible->ymax - (icon_offset_mini * 0.75f), }; wmGizmo *gz; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index f32fcffabd4..032fb7e4cc2 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -1844,7 +1844,7 @@ static void scroller_activate_init(bContext *C, */ scrollers = UI_view2d_scrollers_calc(v2d, NULL); - /* use a union of 'cur' & 'tot' incase the current view is far outside 'tot'. In this cases + /* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases * moving the scroll bars has far too little effect and the view can get stuck T31476. */ tot_cur_union = v2d->tot; BLI_rctf_union(&tot_cur_union, &v2d->cur); diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index 74700e59e99..7155348fed5 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -1008,8 +1008,17 @@ static void gizmo_mesh_spin_redo_setup(const bContext *C, wmGizmoGroup *gzgroup) }); } - /* Become modal as soon as it's started. */ - gizmo_mesh_spin_redo_modal_from_setup(C, gzgroup); + wmWindow *win = CTX_wm_window(C); + if (win && win->active) { + bScreen *screen = WM_window_get_active_screen(win); + if (screen->active_region) { + ARegion *ar = CTX_wm_region(C); + if (screen->active_region == ar) { + /* Become modal as soon as it's started. */ + gizmo_mesh_spin_redo_modal_from_setup(C, gzgroup); + } + } + } } static void gizmo_mesh_spin_redo_draw_prepare(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index bb584094580..ab1595997e5 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -2949,7 +2949,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) case KNF_MODAL_ADD_CUT_CLOSED: if (kcd->mode == MODE_DRAGGING) { - /* shouldn't be possible with default key-layout, just incase... */ + /* Shouldn't be possible with default key-layout, just in case. */ if (kcd->is_drag_hold) { kcd->is_drag_hold = false; knifetool_update_mval(kcd, kcd->curr.mval); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 2f4688e2de7..94ffd9a34d6 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -317,9 +317,10 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc, /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */ { - DRW_draw_select_id(vc->depsgraph, vc->ar, vc->v3d, bases, bases_len, SCE_SELECT_VERTEX); + DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_VERTEX); - index = DRW_select_buffer_find_nearest_to_point(vc->mval, 1, UINT_MAX, &dist_px); + index = DRW_select_buffer_find_nearest_to_point( + vc->depsgraph, vc->ar, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px); if (index) { eve = (BMVert *)edbm_select_id_bm_elem_get(bases, index, &base_index); @@ -539,9 +540,10 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc, /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */ { - DRW_draw_select_id(vc->depsgraph, vc->ar, vc->v3d, bases, bases_len, SCE_SELECT_EDGE); + DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_EDGE); - index = DRW_select_buffer_find_nearest_to_point(vc->mval, 1, UINT_MAX, &dist_px); + index = DRW_select_buffer_find_nearest_to_point( + vc->depsgraph, vc->ar, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px); if (index) { eed = (BMEdge *)edbm_select_id_bm_elem_get(bases, index, &base_index); @@ -745,9 +747,9 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, BMFace *efa; { - DRW_draw_select_id(vc->depsgraph, vc->ar, vc->v3d, bases, bases_len, SCE_SELECT_FACE); + DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_FACE); - index = DRW_select_buffer_sample_point(vc->mval); + index = DRW_select_buffer_sample_point(vc->depsgraph, vc->ar, vc->v3d, vc->mval); if (index) { efa = (BMFace *)edbm_select_id_bm_elem_get(bases, index, &base_index); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index cb147772b6a..4e58fee61a2 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3190,7 +3190,7 @@ void MESH_OT_remove_doubles(wmOperatorType *ot) 1e-6f, 50.0f, "Merge Distance", - "Minimum distance between elements to merge", + "Maximum distance between elements to merge", 1e-5f, 10.0f); RNA_def_boolean(ot->srna, @@ -4040,7 +4040,7 @@ static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const ma_obdata = NULL; } - BKE_material_clear_id(bmain, obdata, true); + BKE_material_clear_id(bmain, obdata); BKE_material_resize_object(bmain, ob, 1, true); BKE_material_resize_id(bmain, obdata, 1, true); @@ -4051,7 +4051,7 @@ static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const id_us_plus((ID *)ma_obdata); } else { - BKE_material_clear_id(bmain, obdata, true); + BKE_material_clear_id(bmain, obdata); BKE_material_resize_object(bmain, ob, 0, true); BKE_material_resize_id(bmain, obdata, 0, true); } @@ -5791,7 +5791,7 @@ void MESH_OT_dissolve_degenerate(wmOperatorType *ot) 1e-6f, 50.0f, "Merge Distance", - "Minimum distance between elements to merge", + "Maximum distance between elements to merge", 1e-5f, 10.0f); } diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 0bdc59c7185..8d9d0e40f44 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -1116,11 +1116,12 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, if (dist_px) { /* sample rect to increase chances of selecting, so that when clicking * on an edge in the backbuf, we can still select a face */ - *r_index = DRW_select_buffer_find_nearest_to_point(mval, 1, me->totpoly + 1, &dist_px); + *r_index = DRW_select_buffer_find_nearest_to_point( + vc.depsgraph, vc.ar, vc.v3d, mval, 1, me->totpoly + 1, &dist_px); } else { /* sample only on the exact position */ - *r_index = DRW_select_buffer_sample_point(mval); + *r_index = DRW_select_buffer_sample_point(vc.depsgraph, vc.ar, vc.v3d, mval); } if ((*r_index) == 0 || (*r_index) > (unsigned int)me->totpoly) { @@ -1297,11 +1298,12 @@ bool ED_mesh_pick_vert( if (dist_px > 0) { /* sample rect to increase chances of selecting, so that when clicking * on an face in the backbuf, we can still select a vert */ - *r_index = DRW_select_buffer_find_nearest_to_point(mval, 1, me->totvert + 1, &dist_px); + *r_index = DRW_select_buffer_find_nearest_to_point( + vc.depsgraph, vc.ar, vc.v3d, mval, 1, me->totvert + 1, &dist_px); } else { /* sample only on the exact position */ - *r_index = DRW_select_buffer_sample_point(mval); + *r_index = DRW_select_buffer_sample_point(vc.depsgraph, vc.ar, vc.v3d, mval); } if ((*r_index) == 0 || (*r_index) > (uint)me->totvert) { diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index eaef9313431..2490f88b5eb 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -57,6 +57,7 @@ set(SRC object_ops.c object_random.c object_relations.c + object_remesh.c object_select.c object_shader_fx.c object_shapekey.c diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 7e031866dec..80d150506ad 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -103,6 +103,7 @@ #include "ED_mesh.h" #include "ED_node.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_physics.h" #include "ED_render.h" #include "ED_screen.h" @@ -502,6 +503,8 @@ Object *ED_object_add_type(bContext *C, /* TODO(sergey): Use proper flag for tagging here. */ DEG_id_tag_update(&scene->id, 0); + ED_outliner_select_sync_from_object_tag(C); + return ob; } diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index e697c25b37f..4b369c10e4d 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -279,6 +279,9 @@ void OBJECT_OT_bake(wmOperatorType *ot); /* object_random.c */ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot); +/* object_remesh.c */ +void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot); + /* object_transfer_data.c */ void OBJECT_OT_data_transfer(struct wmOperatorType *ot); void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index b653c7fa70c..38c06319450 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -257,6 +257,8 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_hide_view_clear); WM_operatortype_append(OBJECT_OT_hide_view_set); WM_operatortype_append(OBJECT_OT_hide_collection); + + WM_operatortype_append(OBJECT_OT_voxel_remesh); } void ED_operatormacros_object(void) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index a69f4872e72..88ef0be3bc6 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1744,7 +1744,7 @@ static Collection *single_object_users_collection(Main *bmain, } /* Since master collection has already be duplicated as part of scene copy, - * we do not duplictae it here. + * we do not duplicate it here. * However, this means its children need to be re-added manually here, * otherwise their parent lists are empty (which will lead to crashes, see T63101). */ CollectionChild *child_next, *child = collection->children.first; diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c new file mode 100644 index 00000000000..bc94b3f7875 --- /dev/null +++ b/source/blender/editors/object/object_remesh.c @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 by Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup edobj + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <float.h> +#include <ctype.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_customdata.h" +#include "BKE_mesh_remesh_voxel.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "ED_mesh.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_undo.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "WM_message.h" +#include "WM_toolsystem.h" + +#include "object_intern.h" // own include + +static bool object_remesh_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if (BKE_object_is_in_editmode(ob)) { + CTX_wm_operator_poll_msg_set(C, "The voxel remesher cannot run from edit mode."); + return false; + } + + if (ob->mode == OB_MODE_SCULPT && ob->sculpt->bm) { + CTX_wm_operator_poll_msg_set(C, "The voxel remesher cannot run with dyntopo activated."); + } + + return ED_operator_object_active_editable_mesh(C); +} + +static int voxel_remesh_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Main *bmain = CTX_data_main(C); + + Mesh *mesh = ob->data; + Mesh *new_mesh; + + if (mesh->remesh_voxel_size <= 0.0f) { + BKE_report(op->reports, RPT_ERROR, "Voxel remesher cannot run with a voxel size of 0.0."); + return OPERATOR_CANCELLED; + } + + if (ob->mode == OB_MODE_SCULPT) { + ED_sculpt_undo_geometry_begin(ob); + } + + new_mesh = BKE_mesh_remesh_voxel_to_mesh_nomain(mesh, mesh->remesh_voxel_size); + + if (!new_mesh) { + return OPERATOR_CANCELLED; + } + + Mesh *obj_mesh_copy = NULL; + if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) { + obj_mesh_copy = BKE_mesh_new_nomain_from_template(mesh, mesh->totvert, 0, 0, 0, 0); + CustomData_copy( + &mesh->vdata, &obj_mesh_copy->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, mesh->totvert); + for (int i = 0; i < mesh->totvert; i++) { + copy_v3_v3(obj_mesh_copy->mvert[i].co, mesh->mvert[i].co); + } + } + + BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); + + if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) { + BKE_remesh_reproject_paint_mask(mesh, obj_mesh_copy); + BKE_mesh_free(obj_mesh_copy); + } + + if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) { + BKE_mesh_smooth_flag_set(ob, true); + } + + if (ob->mode == OB_MODE_SCULPT) { + ED_sculpt_undo_geometry_end(ob); + } + + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_voxel_remesh(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Voxel Remesh"; + ot->description = + "Calculates a new manifold mesh based on the volume of the current mesh. All data layers " + "will be lost"; + ot->idname = "OBJECT_OT_voxel_remesh"; + + /* api callbacks */ + ot->poll = object_remesh_poll; + ot->exec = voxel_remesh_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index da06707ebac..28242b986f1 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -69,6 +69,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_keyframing.h" @@ -436,6 +437,8 @@ static int object_select_by_type_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -717,6 +720,7 @@ static int object_select_linked_exec(bContext *C, wmOperator *op) if (changed) { DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; } @@ -1100,6 +1104,7 @@ static int object_select_grouped_exec(bContext *C, wmOperator *op) if (changed) { DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; } @@ -1150,6 +1155,8 @@ static int object_select_all_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } else if (any_visible == false) { @@ -1218,6 +1225,8 @@ static int object_select_same_collection_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -1281,6 +1290,8 @@ static int object_select_mirror_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -1369,6 +1380,9 @@ static int object_select_more_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } else { @@ -1399,6 +1413,9 @@ static int object_select_less_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } else { @@ -1448,6 +1465,8 @@ static int object_select_random_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c index 6212269c099..457d2421253 100644 --- a/source/blender/editors/object/object_shader_fx.c +++ b/source/blender/editors/object/object_shader_fx.c @@ -282,8 +282,9 @@ void OBJECT_OT_shaderfx_add(wmOperatorType *ot) ot->prop = RNA_def_enum( ot->srna, "type", rna_enum_object_shaderfx_type_items, eShaderFxType_Blur, "Type", ""); RNA_def_enum_funcs(ot->prop, shaderfx_add_itemf); - RNA_def_property_translation_context(ot->prop, - BLT_I18NCONTEXT_ID_ID); /* Abused, for "Light"... */ + + /* Abused, for "Light"... */ + RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_ID); } /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index a43ac59c9b8..08fe5e818b2 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -1139,7 +1139,7 @@ static bool vgroup_normalize(Object *ob) int i, dvert_tot = 0; const int def_nr = ob->actdef - 1; - const int use_vert_sel = vertex_group_use_vert_sel(ob); + const bool use_vert_sel = vertex_group_use_vert_sel(ob); if (!BLI_findlink(&ob->defbase, def_nr)) { return false; @@ -1623,7 +1623,7 @@ static bool vgroup_normalize_all(Object *ob, int i, dvert_tot = 0; const int def_nr = ob->actdef - 1; - const int use_vert_sel = vertex_group_use_vert_sel(ob); + const bool use_vert_sel = vertex_group_use_vert_sel(ob); if (subset_count == 0) { BKE_report(reports, RPT_ERROR, "No vertex groups to operate on"); @@ -2047,7 +2047,7 @@ static int vgroup_limit_total_subset(Object *ob, { MDeformVert *dv, **dvert_array = NULL; int i, dvert_tot = 0; - const int use_vert_sel = vertex_group_use_vert_sel(ob); + const bool use_vert_sel = vertex_group_use_vert_sel(ob); int remove_tot = 0; ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index cd5edcdc3f4..3154d5d0985 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -223,8 +223,8 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) /* get the correct image, and scale it */ sima->image = BKE_image_verify_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"); - /* if we're rendering to full screen, set appropriate hints on image editor - * so it can restore properly on pressing esc */ + /* If we're rendering to full screen, set appropriate hints on image editor + * so it can restore properly on pressing escape. */ if (sa->full) { sima->flag |= SI_FULLWINDOW; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index e30a21d3c6c..16af61f06f3 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -188,51 +188,17 @@ void ED_area_do_refresh(bContext *C, ScrArea *sa) /** * \brief Corner widget use for quitting fullscreen. */ -static void area_draw_azone_fullscreen(short x1, short y1, short x2, short y2, float alpha) +static void area_draw_azone_fullscreen( + short UNUSED(x1), short UNUSED(y1), short x2, short y2, float alpha) { - int x = x2 - ((float)x2 - x1) * 0.5f / UI_DPI_FAC; - int y = y2 - ((float)y2 - y1) * 0.5f / UI_DPI_FAC; - - /* adjust the icon distance from the corner */ - x += 36.0f / UI_DPI_FAC; - y += 36.0f / UI_DPI_FAC; - - /* draws from the left bottom corner of the icon */ - x -= UI_DPI_ICON_SIZE; - y -= UI_DPI_ICON_SIZE; - - alpha = min_ff(alpha, 0.75f); - - UI_icon_draw_ex(x, y, ICON_FULLSCREEN_EXIT, 0.7f * U.inv_dpi_fac, 0.0f, alpha, NULL, false); - - /* debug drawing : - * The click_rect is the same as defined in fullscreen_click_rcti_init - * Keep them both in sync */ - - if (G.debug_value == 101) { - rcti click_rect; - float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC; - - BLI_rcti_init(&click_rect, x, x + icon_size, y, y + icon_size); - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor4f(1.0f, 0.0f, 0.0f, alpha); - imm_draw_box_wire_2d(pos, click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax); - - immUniformColor4f(0.0f, 1.0f, 1.0f, alpha); - immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, click_rect.xmin, click_rect.ymin); - immVertex2f(pos, click_rect.xmax, click_rect.ymax); - immVertex2f(pos, click_rect.xmin, click_rect.ymax); - immVertex2f(pos, click_rect.xmax, click_rect.ymin); - immEnd(); - - immUnbindProgram(); - } + UI_icon_draw_ex(x2 - U.widget_unit, + y2 - U.widget_unit, + ICON_FULLSCREEN_EXIT, + U.inv_dpi_fac, + min_ff(alpha, 0.75f), + 0.0f, + NULL, + false); } /** @@ -368,7 +334,9 @@ static void region_draw_azones(ScrArea *sa, ARegion *ar) } } else if (az->type == AZONE_FULLSCREEN) { - area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha); + if (az->alpha > 0.0f) { + area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha); + } } } if (!IS_EQF(az->alpha, 0.0f) && ELEM(az->type, AZONE_FULLSCREEN, AZONE_REGION_SCROLL)) { @@ -906,10 +874,18 @@ static void fullscreen_azone_initialize(ScrArea *sa, ARegion *ar) az->ar = ar; az->alpha = 0.0f; - az->x1 = ar->winrct.xmax - (AZONEFADEOUT - 1); - az->y1 = ar->winrct.ymax - (AZONEFADEOUT - 1); - az->x2 = ar->winrct.xmax; - az->y2 = ar->winrct.ymax; + if (U.uiflag2 & USER_REGION_OVERLAP) { + const rcti *rect_visible = ED_region_visible_rect(ar); + az->x2 = ar->winrct.xmin + rect_visible->xmax; + az->y2 = ar->winrct.ymin + rect_visible->ymax; + } + else { + az->x2 = ar->winrct.xmax; + az->y2 = ar->winrct.ymax; + } + az->x1 = az->x2 - AZONEFADEOUT; + az->y1 = az->y2 - AZONEFADEOUT; + BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } @@ -1512,6 +1488,9 @@ static void region_rect_recursive( if (ar->winx != prev_winx || ar->winy != prev_winy) { ED_region_tag_redraw(ar); } + + /* Clear, initialize on demand. */ + memset(&ar->runtime.visible_rect, 0, sizeof(ar->runtime.visible_rect)); } static void area_calc_totrct(ScrArea *sa, const rcti *window_rect) @@ -2810,11 +2789,10 @@ void ED_region_info_draw_multiline(ARegion *ar, uiStyle *style = UI_style_get_dpi(); int fontid = style->widget.uifont_id; int scissor[4]; - rcti rect; int num_lines = 0; /* background box */ - ED_region_visible_rect(ar, &rect); + rcti rect = *ED_region_visible_rect(ar); /* Box fill entire width or just around text. */ if (!full_redraw) { @@ -3292,7 +3270,7 @@ void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy) /* If the area has overlapping regions, it returns visible rect for Region *ar */ /* rect gets returned in local region coordinates */ -void ED_region_visible_rect(ARegion *ar, rcti *rect) +static void region_visible_rect_calc(ARegion *ar, rcti *rect) { ARegion *arn = ar; @@ -3339,6 +3317,15 @@ void ED_region_visible_rect(ARegion *ar, rcti *rect) BLI_rcti_translate(rect, -ar->winrct.xmin, -ar->winrct.ymin); } +const rcti *ED_region_visible_rect(ARegion *ar) +{ + rcti *rect = &ar->runtime.visible_rect; + if (rect->xmin == 0 && rect->ymin == 0 && rect->xmax == 0 && rect->ymax == 0) { + region_visible_rect_calc(ar, rect); + } + return rect; +} + /* Cache display helpers */ void ED_region_cache_draw_background(const ARegion *ar) diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index dddc33e3ad0..326bbbd8770 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1116,7 +1116,7 @@ void ED_screen_full_prevspace(bContext *C, ScrArea *sa) void ED_screen_restore_temp_type(bContext *C, ScrArea *sa) { - /* incase nether functions below run */ + /* In case nether functions below run. */ ED_area_tag_redraw(sa); if (sa->flag & AREA_FLAG_TEMP_TYPE) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 4fb5e0c1af3..885cd1ee77d 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -696,21 +696,9 @@ static bool actionzone_area_poll(bContext *C) /* the debug drawing of the click_rect is in area_draw_azone_fullscreen, keep both in sync */ static void fullscreen_click_rcti_init( - rcti *rect, const short x1, const short y1, const short x2, const short y2) + rcti *rect, const short UNUSED(x1), const short UNUSED(y1), const short x2, const short y2) { - int x = x2 - ((float)x2 - x1) * 0.5f / UI_DPI_FAC; - int y = y2 - ((float)y2 - y1) * 0.5f / UI_DPI_FAC; - float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC; - - /* adjust the icon distance from the corner */ - x += 36.0f / UI_DPI_FAC; - y += 36.0f / UI_DPI_FAC; - - /* draws from the left bottom corner of the icon */ - x -= UI_DPI_ICON_SIZE; - y -= UI_DPI_ICON_SIZE; - - BLI_rcti_init(rect, x, x + icon_size, y, y + icon_size); + BLI_rcti_init(rect, x2 - U.widget_unit, x2, y2 - U.widget_unit, y2); } static bool azone_clipped_rect_calc(const AZone *az, rcti *r_rect_clip) @@ -3754,7 +3742,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op) rv3d->viewlock = 0; rv3d->rflag &= ~RV3D_CLIPPING; - /* accumulate locks, incase they're mixed */ + /* Accumulate locks, in case they're mixed. */ for (ar_iter = sa->regionbase.first; ar_iter; ar_iter = ar_iter->next) { if (ar_iter->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d_iter = ar_iter->regiondata; @@ -4787,6 +4775,40 @@ static void SCREEN_OT_drivers_editor_show(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Show Info Log Operator + * \{ */ + +static int info_log_show_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int sizex = 900 * UI_DPI_FAC; + int sizey = 580 * UI_DPI_FAC; + int shift_y = 480; + + /* changes context! */ + if (WM_window_open_temp(C, event->x, event->y + shift_y, sizex, sizey, WM_WINDOW_INFO) != NULL) { + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_ERROR, "Failed to open window!"); + return OPERATOR_CANCELLED; + } +} + +static void SCREEN_OT_info_log_show(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Show Info Log"; + ot->description = "Show info log in a separate window"; + ot->idname = "SCREEN_OT_info_log_show"; + + /* api callbacks */ + ot->invoke = info_log_show_invoke; + ot->poll = ED_operator_screenactive; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name New Screen Operator * \{ */ @@ -5253,6 +5275,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_screenshot); WM_operatortype_append(SCREEN_OT_userpref_show); WM_operatortype_append(SCREEN_OT_drivers_editor_show); + WM_operatortype_append(SCREEN_OT_info_log_show); WM_operatortype_append(SCREEN_OT_region_blend); WM_operatortype_append(SCREEN_OT_space_type_set_or_cycle); WM_operatortype_append(SCREEN_OT_space_context_cycle); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 342d0b6e820..397b2981ace 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1719,9 +1719,9 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps, normalize_v3(no); } else { - /* incase the */ #if 1 - /* normalizing per pixel isn't optimal, we could cache or check ps->*/ + /* 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, diff --git a/source/blender/editors/sculpt_paint/paint_image_undo.c b/source/blender/editors/sculpt_paint/paint_image_undo.c index c7ec4f3f2b9..93dcd3ad0f6 100644 --- a/source/blender/editors/sculpt_paint/paint_image_undo.c +++ b/source/blender/editors/sculpt_paint/paint_image_undo.c @@ -75,7 +75,8 @@ typedef struct UndoImageTile { * adds unnecessary overhead restoring undo steps when most tiles share the same image. */ UndoRefID_Image image_ref; - short source, use_float; + short source; + bool use_float; char gen_type; bool valid; @@ -172,7 +173,7 @@ void *image_undo_find_tile(ListBase *undo_tiles, bool validate) { UndoImageTile *tile; - short use_float = ibuf->rect_float ? 1 : 0; + const bool use_float = (ibuf->rect_float != NULL); for (tile = undo_tiles->first; tile; tile = tile->next) { if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && @@ -214,7 +215,7 @@ void *image_undo_push_tile(ListBase *undo_tiles, { UndoImageTile *tile; int allocsize; - short use_float = ibuf->rect_float ? 1 : 0; + const bool use_float = (ibuf->rect_float != NULL); void *data; /* check if tile is already pushed */ @@ -315,7 +316,6 @@ static void image_undo_restore_list(ListBase *lb) IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); for (UndoImageTile *tile = lb->first; tile; tile = tile->next) { - short use_float; Image *ima = tile->image_ref.ptr; ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); @@ -341,7 +341,7 @@ static void image_undo_restore_list(ListBase *lb) continue; } - use_float = ibuf->rect_float ? 1 : 0; + const bool use_float = (ibuf->rect_float != NULL); if (use_float != tile->use_float) { BKE_image_release_ibuf(ima, ibuf, NULL); @@ -450,7 +450,7 @@ static bool image_undosys_step_encode(struct bContext *C, tile = tmp_tile; } else { - us->step.data_size += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char)); + us->step.data_size += allocsize * (tile->use_float ? sizeof(float) : sizeof(char)); tile = tile->next; } } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 6144f5751f2..694dae49d30 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -233,6 +233,23 @@ static bool paint_tool_require_location(Brush *brush, ePaintMode mode) return true; } +static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) +{ + switch (mode) { + case PAINT_MODE_SCULPT: + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB)) { + return false; + } + else { + return true; + } + default: + break; + } + + return true; +} + /* Initialize the stroke cache variants from operator properties */ static bool paint_brush_update(bContext *C, Brush *brush, @@ -1188,6 +1205,10 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) bool redraw = false; float pressure; + if (event->type == INBETWEEN_MOUSEMOVE && !paint_tool_require_inbetween_mouse_events(br, mode)) { + return OPERATOR_RUNNING_MODAL; + } + /* see if tablet affects event. Line, anchored and drag dot strokes do not support pressure */ pressure = ((br->flag & (BRUSH_LINE | BRUSH_ANCHORED | BRUSH_DRAG_DOT)) ? 1.0f : diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 806b7c471c6..4b9d9a2cc01 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -392,7 +392,7 @@ static int imapaint_pick_face(ViewContext *vc, /* sample only on the exact position */ ED_view3d_select_id_validate(vc); - *r_index = DRW_select_buffer_sample_point(mval); + *r_index = DRW_select_buffer_sample_point(vc->depsgraph, vc->ar, vc->v3d, mval); if ((*r_index) == 0 || (*r_index) > (unsigned int)totpoly) { return 0; 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 72fc08cc38d..4aa9dc8a295 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -580,7 +580,7 @@ typedef struct WPGradient_userData { BLI_bitmap *vert_visit; /* options */ - short use_select; + bool use_select; short type; float weightpaint; } WPGradient_userData; @@ -786,7 +786,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) data.sco_end = sco_end; data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end); data.def_nr = ob->actdef - 1; - data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)); + data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; data.vert_cache = vert_cache; data.vert_visit = NULL; data.type = RNA_enum_get(op->ptr, "type"); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 285e6aff7d0..440c4d42cae 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -92,6 +92,240 @@ #include <stdlib.h> #include <string.h> +/* Sculpt PBVH abstraction API */ + +/* Do not use these functions while working with PBVH_GRIDS data in SculptSession */ + +/* TODO: why is this kept, should it be removed? */ +#if 0 /* UNUSED */ + +static int sculpt_active_vertex_get(SculptSession *ss) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return ss->active_vertex_index; + case PBVH_BMESH: + return ss->active_vertex_index; + default: + return 0; + } +} + +static int sculpt_vertex_count_get(SculptSession *ss) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return ss->totvert; + case PBVH_BMESH: + return BM_mesh_elem_count(BKE_pbvh_get_bmesh(ss->pbvh), BM_VERT); + default: + return 0; + } +} + +static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3]) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + normal_short_to_float_v3(no, ss->mvert[index].no); + return; + case PBVH_BMESH: + copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no); + default: + return; + } +} + +static float *sculpt_vertex_co_get(SculptSession *ss, int index) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return ss->mvert[index].co; + case PBVH_BMESH: + return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; + default: + return NULL; + } +} + +static void sculpt_vertex_co_set(SculptSession *ss, int index, float co[3]) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + copy_v3_v3(ss->mvert[index].co, co); + return; + case PBVH_BMESH: + copy_v3_v3(BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co, co); + return; + default: + return; + } +} + +static void sculpt_vertex_mask_set(SculptSession *ss, int index, float mask) +{ + BMVert *v; + float *mask_p; + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + ss->vmask[index] = mask; + return; + case PBVH_BMESH: + v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); + mask_p = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); + *(mask_p) = mask; + return; + default: + return; + } +} + +static float sculpt_vertex_mask_get(SculptSession *ss, int index) +{ + BMVert *v; + float *mask; + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return ss->vmask[index]; + case PBVH_BMESH: + v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); + mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); + return *mask; + default: + return 0; + } +} + +static void sculpt_vertex_tag_update(SculptSession *ss, int index) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE; + return; + case PBVH_BMESH: + return; + default: + return; + } +} + +# define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 + +typedef struct SculptVertexNeighborIter { + int *neighbors; + int size; + int capacity; + + int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + + int index; + int i; +} SculptVertexNeighborIter; + +static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index) +{ + for (int i = 0; i < iter->size; i++) { + if (iter->neighbors[i] == neighbor_index) { + return; + } + } + + if (iter->size >= iter->capacity) { + iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + + if (iter->neighbors == iter->neighbors_fixed) { + iter->neighbors = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); + memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(int) * iter->size); + } + else { + iter->neighbors = MEM_reallocN_id( + iter->neighbors, iter->capacity * sizeof(int), "neighbor array"); + } + } + + iter->neighbors[iter->size] = neighbor_index; + iter->size++; +} + +static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss, + int index, + SculptVertexNeighborIter *iter) +{ + BMVert *v = BM_vert_at_index(ss->bm, index); + BMIter liter; + BMLoop *l; + iter->size = 0; + iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + iter->neighbors = iter->neighbors_fixed; + + int i = 0; + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + if (BM_elem_index_get(v_other) != (int)index) { + sculpt_vertex_neighbor_add(iter, BM_elem_index_get(v_other)); + } + } + } +} + +static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, + int index, + SculptVertexNeighborIter *iter) +{ + int i; + MeshElemMap *vert_map = &ss->pmap[(int)index]; + iter->size = 0; + iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + iter->neighbors = iter->neighbors_fixed; + + for (i = 0; i < ss->pmap[(int)index].count; i++) { + const MPoly *p = &ss->mpoly[vert_map->indices[i]]; + unsigned f_adj_v[2]; + if (poly_get_adj_loops_from_vert(p, ss->mloop, (int)index, f_adj_v) != -1) { + int j; + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { + if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { + if (f_adj_v[j] != (int)index) { + sculpt_vertex_neighbor_add(iter, f_adj_v[j]); + } + } + } + } + } +} + +static void sculpt_vertex_neighbors_get(SculptSession *ss, + int index, + SculptVertexNeighborIter *iter) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + sculpt_vertex_neighbors_get_faces(ss, index, iter); + return; + case PBVH_BMESH: + sculpt_vertex_neighbors_get_bmesh(ss, index, iter); + return; + default: + break; + } +} + +# define sculpt_vertex_neighbors_iter_begin(ss, v_index, neighbor_iterator) \ + sculpt_vertex_neighbors_get(ss, v_index, &neighbor_iterator); \ + for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ + neighbor_iterator.i++) { \ + neighbor_iterator.index = ni.neighbors[ni.i]; + +# define sculpt_vertex_neighbors_iter_end(neighbor_iterator) \ + } \ + if (neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ + MEM_freeN(neighbor_iterator.neighbors); \ + } + +#endif /* UNUSED */ + /** \name Tool Capabilities * * Avoid duplicate checks, internal logic only, @@ -5695,31 +5929,19 @@ static void sculpt_dynamic_topology_disable_ex( CustomData_free(&me->pdata, me->totpoly); /* Copy over stored custom data */ - me->totvert = unode->bm_enter_totvert; - me->totloop = unode->bm_enter_totloop; - me->totpoly = unode->bm_enter_totpoly; - me->totedge = unode->bm_enter_totedge; + me->totvert = unode->geom_totvert; + me->totloop = unode->geom_totloop; + me->totpoly = unode->geom_totpoly; + me->totedge = unode->geom_totedge; me->totface = 0; - CustomData_copy(&unode->bm_enter_vdata, - &me->vdata, - CD_MASK_MESH.vmask, - CD_DUPLICATE, - unode->bm_enter_totvert); - CustomData_copy(&unode->bm_enter_edata, - &me->edata, - CD_MASK_MESH.emask, - CD_DUPLICATE, - unode->bm_enter_totedge); - CustomData_copy(&unode->bm_enter_ldata, - &me->ldata, - CD_MASK_MESH.lmask, - CD_DUPLICATE, - unode->bm_enter_totloop); - CustomData_copy(&unode->bm_enter_pdata, - &me->pdata, - CD_MASK_MESH.pmask, - CD_DUPLICATE, - unode->bm_enter_totpoly); + CustomData_copy( + &unode->geom_vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, unode->geom_totvert); + CustomData_copy( + &unode->geom_edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, unode->geom_totedge); + CustomData_copy( + &unode->geom_ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, unode->geom_totloop); + CustomData_copy( + &unode->geom_pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, unode->geom_totpoly); BKE_mesh_update_customdata_pointers(me, false); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index e66e1c49685..e646accf108 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -63,6 +63,7 @@ typedef enum { SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END, SCULPT_UNDO_DYNTOPO_SYMMETRIZE, + SCULPT_UNDO_GEOMETRY, } SculptUndoType; typedef struct SculptUndoNode { @@ -94,18 +95,20 @@ typedef struct SculptUndoNode { /* bmesh */ struct BMLogEntry *bm_entry; bool applied; - CustomData bm_enter_vdata; - CustomData bm_enter_edata; - CustomData bm_enter_ldata; - CustomData bm_enter_pdata; - int bm_enter_totvert; - int bm_enter_totedge; - int bm_enter_totloop; - int bm_enter_totpoly; /* shape keys */ char shapeName[sizeof(((KeyBlock *)0))->name]; + /* geometry modification operations and bmesh enter data */ + CustomData geom_vdata; + CustomData geom_edata; + CustomData geom_ldata; + CustomData geom_pdata; + int geom_totvert; + int geom_totedge; + int geom_totloop; + int geom_totpoly; + size_t undo_size; } SculptUndoNode; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index d4c97faa0a6..3a3487227a3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -44,6 +44,7 @@ #include "BKE_ccg.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_multires.h" #include "BKE_paint.h" #include "BKE_key.h" @@ -425,6 +426,32 @@ static void sculpt_undo_bmesh_restore_end(bContext *C, } } +static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *ob) +{ + Mesh *me; + sculpt_pbvh_clear(ob); + me = ob->data; + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + CustomData_free(&me->ldata, me->totloop); + CustomData_free(&me->pdata, me->totpoly); + me->totvert = unode->geom_totvert; + me->totedge = unode->geom_totedge; + me->totloop = unode->geom_totloop; + me->totpoly = unode->geom_totpoly; + me->totface = 0; + CustomData_copy( + &unode->geom_vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, unode->geom_totvert); + CustomData_copy( + &unode->geom_edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, unode->geom_totedge); + CustomData_copy( + &unode->geom_ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, unode->geom_totloop); + CustomData_copy( + &unode->geom_pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, unode->geom_totpoly); + BKE_mesh_update_customdata_pointers(me, false); +} + /* Handle all dynamic-topology updates * * Returns true if this was a dynamic-topology undo step, otherwise @@ -442,7 +469,6 @@ static int sculpt_undo_bmesh_restore(bContext *C, case SCULPT_UNDO_DYNTOPO_END: sculpt_undo_bmesh_restore_end(C, unode, ob, ss); return true; - default: if (ss->bm_log) { sculpt_undo_bmesh_restore_generic(C, unode, ob, ss); @@ -480,6 +506,24 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + if (lb->first) { + unode = lb->first; + if (unode->type == SCULPT_UNDO_GEOMETRY) { + if (unode->applied) { + sculpt_undo_geometry_restore(unode->next, ob); + unode->next->applied = true; + unode->applied = false; + } + else { + sculpt_undo_geometry_restore(unode, ob); + unode->next->applied = false; + unode->applied = true; + } + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask); + return; + } + } + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask); if (lb->first && sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) { @@ -487,6 +531,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } for (unode = lb->first; unode; unode = unode->next) { + if (!STREQ(unode->idname, ob->id.name)) { continue; } @@ -530,6 +575,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: BLI_assert(!"Dynamic topology should've already been handled"); break; + case SCULPT_UNDO_GEOMETRY: + break; } } @@ -617,17 +664,17 @@ static void sculpt_undo_free_list(ListBase *lb) BM_log_entry_drop(unode->bm_entry); } - if (unode->bm_enter_totvert) { - CustomData_free(&unode->bm_enter_vdata, unode->bm_enter_totvert); + if (unode->geom_totvert) { + CustomData_free(&unode->geom_vdata, unode->geom_totvert); } - if (unode->bm_enter_totedge) { - CustomData_free(&unode->bm_enter_edata, unode->bm_enter_totedge); + if (unode->geom_totedge) { + CustomData_free(&unode->geom_edata, unode->geom_totedge); } - if (unode->bm_enter_totloop) { - CustomData_free(&unode->bm_enter_ldata, unode->bm_enter_totloop); + if (unode->geom_totloop) { + CustomData_free(&unode->geom_ldata, unode->geom_totloop); } - if (unode->bm_enter_totpoly) { - CustomData_free(&unode->bm_enter_pdata, unode->bm_enter_totpoly); + if (unode->geom_totpoly) { + CustomData_free(&unode->geom_pdata, unode->geom_totpoly); } MEM_freeN(unode); @@ -743,6 +790,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: BLI_assert(!"Dynamic topology should've already been handled"); + case SCULPT_UNDO_GEOMETRY: break; } @@ -824,6 +872,36 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) BKE_pbvh_vertex_iter_end; } +static SculptUndoNode *sculpt_undo_geometry_push(Object *ob, SculptUndoType type) +{ + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + Mesh *me = ob->data; + bool applied; + + SculptUndoNode *unode = usculpt->nodes.first; + /* Store the original mesh in the first node, modifications in the second */ + applied = unode != NULL; + + unode = MEM_callocN(sizeof(*unode), __func__); + + BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); + unode->type = type; + unode->applied = applied; + + CustomData_copy(&me->vdata, &unode->geom_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert); + CustomData_copy(&me->edata, &unode->geom_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge); + CustomData_copy(&me->ldata, &unode->geom_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop); + CustomData_copy(&me->pdata, &unode->geom_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly); + unode->geom_totvert = me->totvert; + unode->geom_totedge = me->totedge; + unode->geom_totloop = me->totloop; + unode->geom_totpoly = me->totpoly; + + BLI_addtail(&usculpt->nodes, unode); + + return unode; +} + static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); @@ -852,17 +930,17 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt * (converting polys to triangles) that the BMLog can't * fully restore from */ CustomData_copy( - &me->vdata, &unode->bm_enter_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert); + &me->vdata, &unode->geom_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert); CustomData_copy( - &me->edata, &unode->bm_enter_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge); + &me->edata, &unode->geom_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge); CustomData_copy( - &me->ldata, &unode->bm_enter_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop); + &me->ldata, &unode->geom_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop); CustomData_copy( - &me->pdata, &unode->bm_enter_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly); - unode->bm_enter_totvert = me->totvert; - unode->bm_enter_totedge = me->totedge; - unode->bm_enter_totloop = me->totloop; - unode->bm_enter_totpoly = me->totpoly; + &me->pdata, &unode->geom_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly); + unode->geom_totvert = me->totvert; + unode->geom_totedge = me->totedge; + unode->geom_totloop = me->totloop; + unode->geom_totpoly = me->totpoly; unode->bm_entry = BM_log_entry_add(ss->bm_log); BM_log_all_added(ss->bm, ss->bm_log); @@ -906,6 +984,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + case SCULPT_UNDO_GEOMETRY: break; } } @@ -928,6 +1007,11 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType BLI_thread_unlock(LOCK_CUSTOM1); return unode; } + else if (type == SCULPT_UNDO_GEOMETRY) { + unode = sculpt_undo_geometry_push(ob, type); + BLI_thread_unlock(LOCK_CUSTOM1); + return unode; + } else if ((unode = sculpt_undo_get_node(node))) { BLI_thread_unlock(LOCK_CUSTOM1); return unode; @@ -967,6 +1051,7 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: BLI_assert(!"Dynamic topology should've already been handled"); + case SCULPT_UNDO_GEOMETRY: break; } @@ -1163,6 +1248,18 @@ static void sculpt_undosys_step_free(UndoStep *us_p) sculpt_undo_free_list(&us->data.nodes); } +void ED_sculpt_undo_geometry_begin(struct Object *ob) +{ + sculpt_undo_push_begin("voxel remesh"); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); +} + +void ED_sculpt_undo_geometry_end(struct Object *ob) +{ + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); + sculpt_undo_push_end(); +} + /* Export for ED_undo_sys. */ void ED_sculpt_undosys_type(UndoType *ut) { diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index fde8b8f85f8..bb381e0dd1e 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -785,11 +785,11 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r SpaceProperties *sbuts = CTX_wm_space_properties(C); ButsContextPath *path = sbuts ? sbuts->path : NULL; - if (sbuts->mainb == BCONTEXT_TOOL) { + if (!path) { return 0; } - if (!path) { + if (sbuts->mainb == BCONTEXT_TOOL) { return 0; } diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 97a3c7f2480..9a2b0d95c20 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -156,9 +156,8 @@ void ED_image_draw_info(Scene *scene, char str[256]; int dx = 6; /* local coordinate visible rect inside region, to accommodate overlapping ui */ - rcti rect; - ED_region_visible_rect(ar, &rect); - const int ymin = rect.ymin; + const rcti *rect = ED_region_visible_rect(ar); + const int ymin = rect->ymin; const int dy = ymin + 0.3f * UI_UNIT_Y; /* text colors */ diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 17f808f727d..5fa4fe3e077 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -501,11 +501,10 @@ static void image_main_region_set_view2d(SpaceImage *sima, ARegion *ar) int winy = BLI_rcti_size_y(&ar->winrct) + 1; /* For region overlap, move center so image doesn't overlap header. */ - rcti visible_rect; - ED_region_visible_rect(ar, &visible_rect); - const int visible_winy = BLI_rcti_size_y(&visible_rect) + 1; + const rcti *visible_rect = ED_region_visible_rect(ar); + const int visible_winy = BLI_rcti_size_y(visible_rect) + 1; int visible_centerx = 0; - int visible_centery = visible_rect.ymin + (visible_winy - winy) / 2; + int visible_centery = visible_rect->ymin + (visible_winy - winy) / 2; ar->v2d.tot.xmin = 0; ar->v2d.tot.ymin = 0; @@ -587,7 +586,7 @@ static void image_main_region_draw(const bContext *C, ARegion *ar) float col[3]; /* XXX This is in order to draw UI batches with the DRW - * olg context since we now use it for drawing the entire area */ + * old context since we now use it for drawing the entire area. */ gpu_batch_presets_reset(); GPUViewport *viewport = diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 946274de882..106edc290d5 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -574,7 +574,7 @@ void ED_info_stats_clear(ViewLayer *view_layer) const char *ED_info_stats_string(Main *bmain, Scene *scene, ViewLayer *view_layer) { - /* Loopin through dependency graph when interface is locked in not safe. + /* Looping through dependency graph when interface is locked in not safe. * Thew interface is marked as locked when jobs wants to modify the * dependency graph. */ wmWindowManager *wm = bmain->wm.first; diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 03c83305618..f8c30f9a688 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -71,6 +71,10 @@ if(WITH_COMPOSITOR) add_definitions(-DWITH_COMPOSITOR) endif() +if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) +endif() + add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index e63c8331f18..c3ecc34aaf4 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -249,6 +249,11 @@ static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA } } +static void node_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "clamp", 0, NULL, ICON_NONE); +} + static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -1209,6 +1214,9 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_VALTORGB: ntype->draw_buttons = node_buts_colorramp; break; + case SH_NODE_MAP_RANGE: + ntype->draw_buttons = node_buts_map_range; + break; case SH_NODE_MATH: ntype->draw_buttons = node_buts_math; break; @@ -2683,6 +2691,15 @@ static void node_composit_buts_brightcontrast(uiLayout *layout, uiItemR(layout, ptr, "use_premultiply", 0, NULL, ICON_NONE); } +static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ +#ifndef WITH_OPENIMAGEDENOISE + uiItemL(layout, IFACE_("Disabled, built without OpenImageDenoise"), ICON_ERROR); +#endif + + uiItemR(layout, ptr, "use_hdr", 0, NULL, ICON_NONE); +} + /* only once called */ static void node_composit_set_butfunc(bNodeType *ntype) { @@ -2916,6 +2933,10 @@ static void node_composit_set_butfunc(bNodeType *ntype) break; case CMP_NODE_BRIGHTCONTRAST: ntype->draw_buttons = node_composit_buts_brightcontrast; + break; + case CMP_NODE_DENOISE: + ntype->draw_buttons = node_composit_buts_denoise; + break; } } @@ -3333,7 +3354,18 @@ static void std_node_socket_draw( uiItemR(layout, ptr, "default_value", 0, text, 0); break; case SOCK_VECTOR: - uiTemplateComponentMenu(layout, ptr, "default_value", text); + if (sock->flag & SOCK_COMPACT) { + uiTemplateComponentMenu(layout, ptr, "default_value", text); + } + else { + if (sock->typeinfo->subtype == PROP_DIRECTION) { + uiItemR(layout, ptr, "default_value", 0, "", ICON_NONE); + } + else { + uiLayout *column = uiLayoutColumn(layout, true); + uiItemR(column, ptr, "default_value", 0, text, ICON_NONE); + } + } break; case SOCK_RGBA: case SOCK_STRING: { diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index d235dd47136..616915dbc2c 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC outliner_edit.c outliner_ops.c outliner_select.c + outliner_sync.c outliner_tools.c outliner_tree.c outliner_utils.c diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index d8276aa2bbc..6031ba5cffc 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -326,38 +326,57 @@ static bool parent_drop_poll(bContext *C, return false; } -static int parent_drop_exec(bContext *C, wmOperator *op) +static void parent_drop_set_parents( + bContext *C, ReportList *reports, wmDragID *drag, Object *parent, short parent_type) { - Object *par = NULL, *ob = NULL; Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - int partype = -1; - char parname[MAX_NAME], childname[MAX_NAME]; + SpaceOutliner *soops = CTX_wm_space_outliner(C); - partype = RNA_enum_get(op->ptr, "type"); - RNA_string_get(op->ptr, "parent", parname); - par = (Object *)BKE_libblock_find_name(bmain, ID_OB, parname); - RNA_string_get(op->ptr, "child", childname); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, childname); + TreeElement *te = outliner_find_id(soops, &soops->tree, &parent->id); + Scene *scene = (Scene *)outliner_search_back(soops, te, ID_SCE); - if (ID_IS_LINKED(ob)) { - BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); - return OPERATOR_CANCELLED; + if (scene == NULL) { + /* currently outliner organized in a way, that if there's no parent scene + * element for object it means that all displayed objects belong to + * active scene and parenting them is allowed (sergey) + */ + + scene = CTX_data_scene(C); } - ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL); + bool parent_set = false; + bool linked_objects = false; - DEG_relations_tag_update(bmain); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); + for (wmDragID *drag_id = drag; drag_id; drag_id = drag_id->next) { + if (GS(drag_id->id->name) == ID_OB) { + Object *object = (Object *)drag_id->id; - return OPERATOR_FINISHED; + /* Do nothing to linked data */ + if (ID_IS_LINKED(object)) { + linked_objects = true; + continue; + } + + if (ED_object_parent_set( + reports, C, scene, object, parent, parent_type, false, false, NULL)) { + parent_set = true; + } + } + } + + if (linked_objects) { + BKE_report(reports, RPT_INFO, "Can't edit library linked object(s)"); + } + + if (parent_set) { + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); + } } static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Main *bmain = CTX_data_main(C); - SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te = outliner_drop_find(C, event); TreeStoreElem *tselem = te ? TREESTORE(te) : NULL; @@ -374,107 +393,15 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (ob == par) { return OPERATOR_CANCELLED; } - if (ID_IS_LINKED(ob)) { - BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); - return OPERATOR_CANCELLED; - } - - char childname[MAX_NAME]; - char parname[MAX_NAME]; - STRNCPY(childname, ob->id.name + 2); - STRNCPY(parname, par->id.name + 2); - RNA_string_set(op->ptr, "child", childname); - RNA_string_set(op->ptr, "parent", parname); - Scene *scene = (Scene *)outliner_search_back(soops, te, ID_SCE); - - if (scene == NULL) { - /* currently outlier organized in a way, that if there's no parent scene - * element for object it means that all displayed objects belong to - * active scene and parenting them is allowed (sergey) - */ - - scene = CTX_data_scene(C); + if (event->custom != EVT_DATA_DRAGDROP) { + return OPERATOR_CANCELLED; } - if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) { - int partype = 0; - if (ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL)) { - DEG_relations_tag_update(bmain); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); - } - } - else { - /* Menu creation */ - wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_parent_drop", false); - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Set Parent To"), ICON_NONE); - uiLayout *layout = UI_popup_menu_layout(pup); - PointerRNA ptr; - - /* Cannot use uiItemEnumO()... have multiple properties to set. */ - uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_OBJECT); - - /* par becomes parent, make the associated menus */ - if (par->type == OB_ARMATURE) { - uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE); - - uiItemFullO_ptr( - layout, ot, IFACE_(" With Empty Groups"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME); - - uiItemFullO_ptr( - layout, ot, IFACE_(" With Envelope Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE); - - uiItemFullO_ptr( - layout, ot, IFACE_(" With Automatic Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO); - - uiItemFullO_ptr(layout, ot, IFACE_("Bone"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_BONE); - } - else if (par->type == OB_CURVE) { - uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_CURVE); - - uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_FOLLOW); - - uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_PATH_CONST); - } - else if (par->type == OB_LATTICE) { - uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_LATTICE); - } - - UI_popup_menu_end(C, pup); + ListBase *lb = event->customdata; + wmDrag *drag = lb->first; - return OPERATOR_INTERFACE; - } + parent_drop_set_parents(C, op->reports, drag->ids.first, par, PAR_OBJECT); return OPERATOR_FINISHED; } @@ -488,17 +415,11 @@ void OUTLINER_OT_parent_drop(wmOperatorType *ot) /* api callbacks */ ot->invoke = parent_drop_invoke; - ot->exec = parent_drop_exec; ot->poll = ED_operator_outliner_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - /* properties */ - RNA_def_string(ot->srna, "child", "Object", MAX_NAME, "Child", "Child Object"); - RNA_def_string(ot->srna, "parent", "Object", MAX_NAME, "Parent", "Parent Object"); - RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); } /* ******************** Parent Clear Operator *********************** */ @@ -549,13 +470,21 @@ static bool parent_clear_poll(bContext *C, static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { Main *bmain = CTX_data_main(C); - Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB); - if (ob == NULL) { + if (event->custom != EVT_DATA_DRAGDROP) { return OPERATOR_CANCELLED; } - ED_object_parent_clear(ob, 0); + ListBase *lb = event->customdata; + wmDrag *drag = lb->first; + + for (wmDragID *drag_id = drag->ids.first; drag_id; drag_id = drag_id->next) { + if (GS(drag_id->id->name) == ID_OB) { + Object *object = (Object *)drag_id->id; + + ED_object_parent_clear(object, 0); + } + } DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); @@ -966,6 +895,12 @@ static int outliner_item_drag_drop_invoke(bContext *C, return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) { + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + wmDrag *drag = WM_event_start_drag(C, data.icon, WM_DRAG_ID, NULL, 0.0, WM_DRAG_NOP); if (ELEM(GS(data.drag_id->name), ID_OB, ID_GR)) { diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index e4881a6f13d..9c45fb15f6b 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -31,6 +31,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_constraint_types.h" #include "DNA_object_force_types.h" #include "BLI_math.h" @@ -60,6 +61,7 @@ #include "ED_armature.h" #include "ED_keyframing.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "WM_api.h" @@ -848,6 +850,7 @@ typedef struct RestrictProperties { PropertyRNA *layer_collection_holdout, *layer_collection_indirect_only, *layer_collection_hide_viewport; PropertyRNA *modifier_show_viewport, *modifier_show_render; + PropertyRNA *constraint_enable; } RestrictProperties; /* We don't care about the value of the property @@ -865,6 +868,7 @@ typedef struct RestrictPropertiesActive { bool layer_collection_hide_viewport; bool modifier_show_viewport; bool modifier_show_render; + bool constraint_enable; } RestrictPropertiesActive; static void outliner_restrict_properties_enable_collection_set( @@ -878,6 +882,7 @@ static void outliner_restrict_properties_enable_collection_set( props_active->layer_collection_indirect_only = false; props_active->object_hide_render = false; props_active->modifier_show_render = false; + props_active->constraint_enable = false; } } @@ -891,6 +896,7 @@ static void outliner_restrict_properties_enable_collection_set( props_active->object_hide_viewport = false; props_active->base_hide_viewport = false; props_active->modifier_show_viewport = false; + props_active->constraint_enable = false; } } @@ -995,6 +1001,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, props.modifier_show_viewport = RNA_struct_type_find_property(&RNA_Modifier, "show_viewport"); props.modifier_show_render = RNA_struct_type_find_property(&RNA_Modifier, "show_render"); + props.constraint_enable = RNA_struct_type_find_property(&RNA_Constraint, "mute"); + props.initialized = true; } @@ -1181,6 +1189,35 @@ static void outliner_draw_restrictbuts(uiBlock *block, } } } + else if (tselem->type == TSE_CONSTRAINT) { + bConstraint *con = (bConstraint *)te->directdata; + + PointerRNA ptr; + RNA_pointer_create(tselem->id, &RNA_Constraint, con, &ptr); + + if (soops->show_restrict_flags & SO_RESTRICT_HIDE) { + bt = uiDefIconButR_prop(block, + UI_BTYPE_ICON_TOGGLE, + 0, + 0, + (int)(ar->v2d.cur.xmax - restrict_offsets.hide), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + &ptr, + props.constraint_enable, + -1, + 0, + 0, + -1, + -1, + NULL); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + if (!props_active.constraint_enable) { + UI_but_flag_enable(bt, UI_BUT_INACTIVE); + } + } + } else if (tselem->type == TSE_MODIFIER) { ModifierData *md = (ModifierData *)te->directdata; @@ -1878,6 +1915,9 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case TSE_DEFGROUP_BASE: data.icon = ICON_GROUP_VERTEX; break; + case TSE_DEFGROUP: + data.icon = ICON_GROUP_VERTEX; + break; case TSE_BONE: case TSE_EBONE: data.icon = ICON_BONE_DATA; @@ -1885,6 +1925,100 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case TSE_CONSTRAINT_BASE: data.icon = ICON_CONSTRAINT; break; + case TSE_CONSTRAINT: { + bConstraint *con = te->directdata; + switch ((eBConstraint_Types)con->type) { + case CONSTRAINT_TYPE_CAMERASOLVER: + data.icon = ICON_CON_CAMERASOLVER; + break; + case CONSTRAINT_TYPE_FOLLOWTRACK: + data.icon = ICON_CON_FOLLOWTRACK; + break; + case CONSTRAINT_TYPE_OBJECTSOLVER: + data.icon = ICON_CON_OBJECTSOLVER; + break; + case CONSTRAINT_TYPE_LOCLIKE: + data.icon = ICON_CON_LOCLIKE; + break; + case CONSTRAINT_TYPE_ROTLIKE: + data.icon = ICON_CON_ROTLIKE; + break; + case CONSTRAINT_TYPE_SIZELIKE: + data.icon = ICON_CON_SIZELIKE; + break; + case CONSTRAINT_TYPE_TRANSLIKE: + data.icon = ICON_CON_TRANSLIKE; + break; + case CONSTRAINT_TYPE_DISTLIMIT: + data.icon = ICON_CON_DISTLIMIT; + break; + case CONSTRAINT_TYPE_LOCLIMIT: + data.icon = ICON_CON_LOCLIMIT; + break; + case CONSTRAINT_TYPE_ROTLIMIT: + data.icon = ICON_CON_ROTLIMIT; + break; + case CONSTRAINT_TYPE_SIZELIMIT: + data.icon = ICON_CON_SIZELIMIT; + break; + case CONSTRAINT_TYPE_SAMEVOL: + data.icon = ICON_CON_SAMEVOL; + break; + case CONSTRAINT_TYPE_TRANSFORM: + data.icon = ICON_CON_TRANSFORM; + break; + case CONSTRAINT_TYPE_TRANSFORM_CACHE: + data.icon = ICON_CON_TRANSFORM_CACHE; + break; + case CONSTRAINT_TYPE_CLAMPTO: + data.icon = ICON_CON_CLAMPTO; + break; + case CONSTRAINT_TYPE_DAMPTRACK: + data.icon = ICON_CON_TRACKTO; + break; + case CONSTRAINT_TYPE_KINEMATIC: + data.icon = ICON_CON_KINEMATIC; + break; + case CONSTRAINT_TYPE_LOCKTRACK: + data.icon = ICON_CON_LOCKTRACK; + break; + case CONSTRAINT_TYPE_SPLINEIK: + data.icon = ICON_CON_SPLINEIK; + break; + case CONSTRAINT_TYPE_STRETCHTO: + data.icon = ICON_CON_STRETCHTO; + break; + case CONSTRAINT_TYPE_TRACKTO: + data.icon = ICON_CON_TRACKTO; + break; + case CONSTRAINT_TYPE_ACTION: + data.icon = ICON_ACTION; + break; + case CONSTRAINT_TYPE_ARMATURE: + data.icon = ICON_CON_ARMATURE; + break; + case CONSTRAINT_TYPE_CHILDOF: + data.icon = ICON_CON_CHILDOF; + break; + case CONSTRAINT_TYPE_MINMAX: + data.icon = ICON_CON_FLOOR; + break; + case CONSTRAINT_TYPE_FOLLOWPATH: + data.icon = ICON_CON_FOLLOWPATH; + break; + case CONSTRAINT_TYPE_PIVOT: + data.icon = ICON_CON_PIVOT; + break; + case CONSTRAINT_TYPE_SHRINKWRAP: + data.icon = ICON_CON_SHRINKWRAP; + break; + + default: + data.icon = ICON_DOT; + break; + } + break; + } case TSE_MODIFIER_BASE: data.icon = ICON_MODIFIER_DATA; break; @@ -2137,23 +2271,57 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.icon = ICON_GROUP_BONE; break; case TSE_SEQUENCE: - if (te->idcode == SEQ_TYPE_MOVIE) { - data.icon = ICON_SEQUENCE; - } - else if (te->idcode == SEQ_TYPE_META) { - data.icon = ICON_DOT; - } - else if (te->idcode == SEQ_TYPE_SCENE) { - data.icon = ICON_SCENE; - } - else if (te->idcode == SEQ_TYPE_SOUND_RAM) { - data.icon = ICON_SOUND; - } - else if (te->idcode == SEQ_TYPE_IMAGE) { - data.icon = ICON_IMAGE; - } - else { - data.icon = ICON_PARTICLES; + switch (te->idcode) { + case SEQ_TYPE_SCENE: + data.icon = ICON_SCENE_DATA; + break; + case SEQ_TYPE_MOVIECLIP: + data.icon = ICON_TRACKER; + break; + case SEQ_TYPE_MASK: + data.icon = ICON_MOD_MASK; + break; + case SEQ_TYPE_MOVIE: + data.icon = ICON_FILE_MOVIE; + break; + case SEQ_TYPE_SOUND_RAM: + data.icon = ICON_SOUND; + break; + case SEQ_TYPE_IMAGE: + data.icon = ICON_FILE_IMAGE; + break; + case SEQ_TYPE_COLOR: + case SEQ_TYPE_ADJUSTMENT: + data.icon = ICON_COLOR; + break; + case SEQ_TYPE_TEXT: + data.icon = ICON_FONT_DATA; + break; + case SEQ_TYPE_ADD: + case SEQ_TYPE_SUB: + case SEQ_TYPE_MUL: + case SEQ_TYPE_OVERDROP: + case SEQ_TYPE_ALPHAOVER: + case SEQ_TYPE_ALPHAUNDER: + case SEQ_TYPE_COLORMIX: + case SEQ_TYPE_MULTICAM: + case SEQ_TYPE_TRANSFORM: + case SEQ_TYPE_SPEED: + case SEQ_TYPE_GLOW: + case SEQ_TYPE_GAUSSIAN_BLUR: + data.icon = ICON_SHADERFX; + break; + case SEQ_TYPE_CROSS: + case SEQ_TYPE_GAMCROSS: + case SEQ_TYPE_WIPE: + data.icon = ICON_ARROW_LEFTRIGHT; + break; + case SEQ_TYPE_META: + data.icon = ICON_DOT; + break; + default: + data.icon = ICON_DOT; + break; } break; case TSE_SEQ_STRIP: @@ -2459,7 +2627,11 @@ static void tselem_draw_icon(uiBlock *block, return; } + /* Icon is covered by restrict buttons */ if (!is_clickable || x >= xmax) { + /* Reduce alpha to match icon buttons */ + alpha *= 0.8f; + /* placement of icons, copied from interface_widgets.c */ float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT; x += 2.0f * aspect; @@ -2567,7 +2739,6 @@ static void outliner_draw_iconrow_doit(uiBlock *block, float ufac = UI_UNIT_X / 20.0f; float icon_color[4], icon_border[4]; outliner_icon_background_colors(icon_color, icon_border); - icon_color[3] *= alpha_fac; if (active == OL_DRAWSEL_ACTIVE) { UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_color); icon_border[3] = 0.3f; @@ -2592,6 +2763,9 @@ static void outliner_draw_iconrow_doit(uiBlock *block, GPU_blend(true); /* Roundbox disables. */ } + if (tselem->flag & TSE_HIGHLIGHTED) { + alpha_fac += 0.5; + } tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, alpha_fac, false); te->xs = *offsx; te->ys = ys; @@ -2599,7 +2773,12 @@ static void outliner_draw_iconrow_doit(uiBlock *block, if (num_elements > 1) { outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements); + te->flag |= TE_ICONROW_MERGED; + } + else { + te->flag |= TE_ICONROW; } + (*offsx) += UI_UNIT_X; } @@ -2609,7 +2788,7 @@ static void outliner_draw_iconrow_doit(uiBlock *block, * We use a continuum of indices until we get to the object data-blocks * and we then make room for the object types. */ -static int tree_element_id_type_to_index(TreeElement *te) +int tree_element_id_type_to_index(TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); @@ -2739,7 +2918,7 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta TreeElement *ten; /* closed items may be displayed in row of parent, don't change their coordinate! */ - if ((te->flag & TE_ICONROW) == 0) { + if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { /* store coord and continue, we need coordinates for elements outside view too */ te->xs = startx; te->ys = starty; @@ -3193,6 +3372,7 @@ static void outliner_draw_highlights_recursive(unsigned pos, const SpaceOutliner *soops, const ListBase *lb, const float col_selection[4], + const float col_active[4], const float col_highlight[4], const float col_searchmatch[4], int start_x, @@ -3206,7 +3386,11 @@ static void outliner_draw_highlights_recursive(unsigned pos, const int start_y = *io_start_y; /* selection status */ - if (tselem->flag & TSE_SELECTED) { + if ((tselem->flag & TSE_ACTIVE) && (tselem->flag & TSE_SELECTED)) { + immUniformColor4fv(col_active); + immRecti(pos, 0, start_y, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y); + } + else if (tselem->flag & TSE_SELECTED) { immUniformColor4fv(col_selection); immRecti(pos, 0, start_y, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y); } @@ -3260,6 +3444,7 @@ static void outliner_draw_highlights_recursive(unsigned pos, soops, &te->subtree, col_selection, + col_active, col_highlight, col_searchmatch, start_x + UI_UNIT_X, @@ -3271,10 +3456,12 @@ static void outliner_draw_highlights_recursive(unsigned pos, static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int startx, int *starty) { const float col_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f}; - float col_selection[4], col_searchmatch[4]; + float col_selection[4], col_active[4], col_searchmatch[4]; UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col_selection); col_selection[3] = 1.0f; /* no alpha */ + UI_GetThemeColor3fv(TH_SELECT_ACTIVE, col_active); + col_active[3] = 1.0f; /* no alpha */ UI_GetThemeColor4fv(TH_MATCH, col_searchmatch); col_searchmatch[3] = 0.5f; @@ -3282,8 +3469,16 @@ static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int star GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - outliner_draw_highlights_recursive( - pos, ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch, startx, starty); + outliner_draw_highlights_recursive(pos, + ar, + soops, + &soops->tree, + col_selection, + col_active, + col_highlight, + col_searchmatch, + startx, + starty); immUnbindProgram(); GPU_blend(false); } @@ -3439,6 +3634,17 @@ void draw_outliner(const bContext *C) outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always + /* If global sync select is dirty, flag other outliners */ + if (ED_outliner_select_sync_is_dirty(C)) { + ED_outliner_select_sync_flag_outliners(C); + } + + /* Sync selection state from view layer */ + if (!ELEM(soops->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS) && + soops->flag & SO_SYNC_SELECT) { + outliner_sync_selection(C, soops); + } + /* force display to pixel coords */ v2d->flag |= (V2D_PIXELOFS_X | V2D_PIXELOFS_Y); /* set matrix for 2d-view controls */ diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index de6e89e47c4..318d90d0dca 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -101,9 +101,15 @@ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const ARegion *ar = CTX_wm_region(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); - const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]); - TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, my); + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + if (hovered_te) { + hovered_te = outliner_find_item_at_x_in_row(soops, hovered_te, view_mval[0], NULL); + } bool changed = false; if (!hovered_te || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED)) { @@ -134,59 +140,108 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot) /* Toggle Open/Closed ------------------------------------------- */ -static int do_outliner_item_openclose( - bContext *C, SpaceOutliner *soops, TreeElement *te, const bool all, const float mval[2]) +/* Open or close a tree element, optionally toggling all children recursively */ +void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all) { + TreeStoreElem *tselem = TREESTORE(te); - if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { - TreeStoreElem *tselem = TREESTORE(te); + if (open) { + tselem->flag &= ~TSE_CLOSED; + } + else { + tselem->flag |= TSE_CLOSED; + } - /* all below close/open? */ - if (all) { - tselem->flag &= ~TSE_CLOSED; - outliner_flag_set( - &te->subtree, TSE_CLOSED, !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1)); - } - else { - if (tselem->flag & TSE_CLOSED) { - tselem->flag &= ~TSE_CLOSED; - } - else { - tselem->flag |= TSE_CLOSED; + if (toggle_all) { + outliner_flag_set(&te->subtree, TSE_CLOSED, !open); + } +} + +typedef struct OpenCloseData { + TreeStoreElem *prev_tselem; + bool open; + int x_location; +} OpenCloseData; + +static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOutliner *soops = CTX_wm_space_outliner(C); + + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + if (event->type == MOUSEMOVE) { + TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + OpenCloseData *data = (OpenCloseData *)op->customdata; + + /* Only openclose if mouse is not over the previously toggled element */ + if (te && TREESTORE(te) != data->prev_tselem) { + + /* Only toggle openclose on the same level as the first clicked element */ + if (te->xs == data->x_location) { + outliner_item_openclose(te, data->open, false); + ED_region_tag_redraw(ar); } } - return 1; + if (te) { + data->prev_tselem = TREESTORE(te); + } + else { + data->prev_tselem = NULL; + } } + else if (event->val == KM_RELEASE) { + MEM_freeN(op->customdata); - for (te = te->subtree.first; te; te = te->next) { - if (do_outliner_item_openclose(C, soops, te, all, mval)) { - return 1; - } + return OPERATOR_FINISHED; } - return 0; + + return OPERATOR_RUNNING_MODAL; } -/* event can enterkey, then it opens/closes */ -static int outliner_item_openclose(bContext *C, wmOperator *op, const wmEvent *event) +static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); - TreeElement *te; - float fmval[2]; - const bool all = RNA_boolean_get(op->ptr, "all"); - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + const bool toggle_all = RNA_boolean_get(op->ptr, "all"); - for (te = soops->tree.first; te; te = te->next) { - if (do_outliner_item_openclose(C, soops, te, all, fmval)) { - break; + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + if (te && outliner_item_is_co_within_close_toggle(te, view_mval[0])) { + TreeStoreElem *tselem = TREESTORE(te); + + const bool open = (tselem->flag & TSE_CLOSED) || + (toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1))); + + outliner_item_openclose(te, open, toggle_all); + ED_region_tag_redraw(ar); + + /* Only toggle once for single click toggling */ + if (event->type == LEFTMOUSE) { + return OPERATOR_FINISHED; } - } - ED_region_tag_redraw(ar); + /* Store last expanded tselem and x coordinate of disclosure triangle */ + OpenCloseData *toggle_data = MEM_callocN(sizeof(OpenCloseData), "open_close_data"); + toggle_data->prev_tselem = tselem; + toggle_data->open = open; + toggle_data->x_location = te->xs; - return OPERATOR_FINISHED; + /* Store the first clicked on element */ + op->customdata = toggle_data; + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } void OUTLINER_OT_item_openclose(wmOperatorType *ot) @@ -195,11 +250,12 @@ void OUTLINER_OT_item_openclose(wmOperatorType *ot) ot->idname = "OUTLINER_OT_item_openclose"; ot->description = "Toggle whether item under cursor is enabled or closed"; - ot->invoke = outliner_item_openclose; + ot->invoke = outliner_item_openclose_invoke; + ot->modal = outliner_item_openclose_modal; ot->poll = ED_operator_outliner_active; - RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items"); + RNA_def_boolean(ot->srna, "all", false, "All", "Close or open all items"); } /* -------------------------------------------------------------------- */ @@ -330,10 +386,10 @@ void item_rename_cb(bContext *C, do_item_rename(ar, te, tselem, reports); } -static int do_outliner_item_rename(ReportList *reports, - ARegion *ar, - TreeElement *te, - const float mval[2]) +static void do_outliner_item_rename(ReportList *reports, + ARegion *ar, + TreeElement *te, + const float mval[2]) { if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { TreeStoreElem *tselem = TREESTORE(te); @@ -341,17 +397,12 @@ static int do_outliner_item_rename(ReportList *reports, /* click on name */ if (mval[0] > te->xs + UI_UNIT_X * 2 && mval[0] < te->xend) { do_item_rename(ar, te, tselem, reports); - return 1; } - return 0; } for (te = te->subtree.first; te; te = te->next) { - if (do_outliner_item_rename(reports, ar, te, mval)) { - return 1; - } + do_outliner_item_rename(reports, ar, te, mval); } - return 0; } static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *event) @@ -360,25 +411,34 @@ static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *even SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te; float fmval[2]; - bool changed = false; - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + /* Rename active element if key pressed, otherwise rename element at cursor coordinates */ + if (event->val == KM_PRESS) { + TreeElement *active_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE); - for (te = soops->tree.first; te; te = te->next) { - if (do_outliner_item_rename(op->reports, ar, te, fmval)) { - changed = true; - break; + if (active_element) { + do_item_rename(ar, active_element, TREESTORE(active_element), op->reports); + } + else { + BKE_report(op->reports, RPT_WARNING, "No active item to rename"); } } + else { + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - return changed ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH; + for (te = soops->tree.first; te; te = te->next) { + do_outliner_item_rename(op->reports, ar, te, fmval); + } + } + + return OPERATOR_FINISHED; } void OUTLINER_OT_item_rename(wmOperatorType *ot) { ot->name = "Rename"; ot->idname = "OUTLINER_OT_item_rename"; - ot->description = "Rename item under cursor"; + ot->description = "Rename the active element"; ot->invoke = outliner_item_rename; @@ -1103,6 +1163,10 @@ static int outliner_select_all_exec(bContext *C, wmOperator *op) break; } + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); ED_region_tag_redraw_no_rebuild(ar); @@ -1179,20 +1243,17 @@ static int outliner_open_back(TreeElement *te) return retval; } -static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) +/* Return element representing the active base or bone in the outliner, or NULL if none exists */ +static TreeElement *outliner_show_active_get_element(bContext *C, + SpaceOutliner *so, + ViewLayer *view_layer) { - SpaceOutliner *so = CTX_wm_space_outliner(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - ARegion *ar = CTX_wm_region(C); - View2D *v2d = &ar->v2d; - TreeElement *te; - int xdelta, ytop; Object *obact = OBACT(view_layer); if (!obact) { - return OPERATOR_CANCELLED; + return NULL; } te = outliner_find_id(so, &so->tree, &obact->id); @@ -1215,25 +1276,50 @@ static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) } } - if (te) { - /* open up tree to active object/bone */ + return te; +} + +static void outliner_show_active(SpaceOutliner *so, ARegion *ar, TreeElement *te, ID *id) +{ + /* open up tree to active object/bone */ + if (TREESTORE(te)->id == id) { if (outliner_open_back(te)) { outliner_set_coordinates(ar, so); } + return; + } + + for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) { + outliner_show_active(so, ar, ten, id); + } +} + +static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceOutliner *so = CTX_wm_space_outliner(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ARegion *ar = CTX_wm_region(C); + View2D *v2d = &ar->v2d; + + TreeElement *active_element = outliner_show_active_get_element(C, so, view_layer); - /* make te->ys center of view */ - ytop = te->ys + BLI_rcti_size_y(&v2d->mask) / 2; - if (ytop > 0) { - ytop = 0; + if (active_element) { + ID *id = TREESTORE(active_element)->id; + + /* Expand all elements in the outliner with matching ID */ + for (TreeElement *te = so->tree.first; te; te = te->next) { + outliner_show_active(so, ar, te, id); } - v2d->cur.ymax = (float)ytop; - v2d->cur.ymin = (float)(ytop - BLI_rcti_size_y(&v2d->mask)); + /* Center view on first element found */ + int size_y = BLI_rcti_size_y(&v2d->mask) + 1; + int ytop = (active_element->ys + (size_y / 2)); + int delta_y = ytop - v2d->cur.ymax; - /* make te->xs ==> te->xend center of view */ - xdelta = (int)(te->xs - v2d->cur.xmin); - v2d->cur.xmin += xdelta; - v2d->cur.xmax += xdelta; + outliner_scroll_view(ar, delta_y); + } + else { + return OPERATOR_CANCELLED; } ED_region_tag_redraw_no_rebuild(ar); @@ -1259,18 +1345,15 @@ void OUTLINER_OT_show_active(wmOperatorType *ot) static int outliner_scroll_page_exec(bContext *C, wmOperator *op) { ARegion *ar = CTX_wm_region(C); - int dy = BLI_rcti_size_y(&ar->v2d.mask); - int up = 0; + int size_y = BLI_rcti_size_y(&ar->v2d.mask) + 1; - if (RNA_boolean_get(op->ptr, "up")) { - up = 1; - } + bool up = RNA_boolean_get(op->ptr, "up"); - if (up == 0) { - dy = -dy; + if (!up) { + size_y = -size_y; } - ar->v2d.cur.ymin += dy; - ar->v2d.cur.ymax += dy; + + outliner_scroll_view(ar, size_y); ED_region_tag_redraw_no_rebuild(ar); diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index fa28d119244..466e6684eca 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -50,6 +50,14 @@ typedef enum TreeElementInsertType { TE_INSERT_INTO, } TreeElementInsertType; +/* Use generic walk select after D4771 is committed */ +typedef enum WalkSelectDirection { + OUTLINER_SELECT_WALK_UP, + OUTLINER_SELECT_WALK_DOWN, + OUTLINER_SELECT_WALK_LEFT, + OUTLINER_SELECT_WALK_RIGHT, +} WalkSelectDirection; + typedef enum TreeTraversalAction { /* Continue traversal regularly, don't skip children. */ TRAVERSE_CONTINUE = 0, @@ -131,6 +139,9 @@ enum { TE_DISABLED = (1 << 4), TE_DRAGGING = (1 << 5), TE_CHILD_NOT_IN_COLLECTION = (1 << 6), + /* Child elements of the same type in the icon-row are drawn merged as one icon. + * This flag is set for an element that is part of these merged child icons. */ + TE_ICONROW_MERGED = (1 << 7), }; /* button events */ @@ -223,6 +234,8 @@ void outliner_collection_isolate_flag(struct Scene *scene, const char *propname, const bool value); +int tree_element_id_type_to_index(TreeElement *te); + /* outliner_select.c -------------------------------------------- */ eOLDrawState tree_element_type_active(struct bContext *C, struct Scene *scene, @@ -253,6 +266,10 @@ void outliner_object_mode_toggle(struct bContext *C, ViewLayer *view_layer, Base *base); +void outliner_element_activate(struct SpaceOutliner *soops, struct TreeStoreElem *tselem); + +bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x); + /* outliner_edit.c ---------------------------------------------- */ typedef void (*outliner_operation_cb)(struct bContext *C, struct ReportList *, @@ -337,6 +354,8 @@ void item_object_mode_exit_cb(struct bContext *C, void outliner_set_coordinates(struct ARegion *ar, struct SpaceOutliner *soops); +void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all); + /* outliner_dragdrop.c */ void outliner_dropboxes(void); @@ -364,6 +383,7 @@ void OUTLINER_OT_show_active(struct wmOperatorType *ot); void OUTLINER_OT_show_hierarchy(struct wmOperatorType *ot); void OUTLINER_OT_select_box(struct wmOperatorType *ot); +void OUTLINER_OT_select_walk(struct wmOperatorType *ot); void OUTLINER_OT_select_all(struct wmOperatorType *ot); void OUTLINER_OT_expanded_toggle(struct wmOperatorType *ot); @@ -380,6 +400,10 @@ void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot); /* outliner_tools.c ---------------------------------------------- */ +void merged_element_search_menu_invoke(struct bContext *C, + TreeElement *parent_te, + TreeElement *activate_te); + void OUTLINER_OT_operation(struct wmOperatorType *ot); void OUTLINER_OT_scene_operation(struct wmOperatorType *ot); void OUTLINER_OT_object_operation(struct wmOperatorType *ot); @@ -439,7 +463,8 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, float view_co_y); TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *soops, const TreeElement *parent_te, - float view_co_x); + float view_co_x, + bool *multiple_objects); TreeElement *outliner_find_tse(struct SpaceOutliner *soops, const TreeStoreElem *tse); TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem); TreeElement *outliner_find_parent_element(ListBase *lb, @@ -456,5 +481,12 @@ bool outliner_tree_traverse(const SpaceOutliner *soops, TreeTraversalFunc func, void *customdata); float outliner_restrict_columns_width(const struct SpaceOutliner *soops); +TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag); +bool outliner_is_element_visible(const TreeElement *te); +void outliner_scroll_view(struct ARegion *ar, int delta_y); + +/* outliner_sync.c ---------------------------------------------- */ + +void outliner_sync_selection(const struct bContext *C, struct SpaceOutliner *soops); #endif /* __OUTLINER_INTERN_H__ */ diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index f155a2d5f89..4b57d4ad771 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -50,6 +50,7 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_highlight_update); WM_operatortype_append(OUTLINER_OT_item_activate); WM_operatortype_append(OUTLINER_OT_select_box); + WM_operatortype_append(OUTLINER_OT_select_walk); WM_operatortype_append(OUTLINER_OT_item_openclose); WM_operatortype_append(OUTLINER_OT_item_rename); WM_operatortype_append(OUTLINER_OT_item_drag_drop); diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index c932766ab93..19fd4511e50 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -51,14 +51,16 @@ #include "BKE_workspace.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "ED_armature.h" +#include "ED_gpencil.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_sequencer.h" #include "ED_undo.h" -#include "ED_gpencil.h" #include "WM_api.h" #include "WM_types.h" @@ -251,9 +253,7 @@ static eOLDrawState active_viewlayer(bContext *C, } /** - * Select object tree: - * CTRL+LMB: Select/Deselect object and all children. - * CTRL+SHIFT+LMB: Add/Remove object and all children. + * Select object tree */ static void do_outliner_object_select_recursive(ViewLayer *view_layer, Object *ob_parent, @@ -450,9 +450,9 @@ static eOLDrawState tree_element_active_material(bContext *C, return OL_DRAWSEL_NONE; } -static eOLDrawState tree_element_active_camera(bContext *UNUSED(C), +static eOLDrawState tree_element_active_camera(bContext *C, Scene *scene, - ViewLayer *UNUSED(sl), + ViewLayer *UNUSED(view_layer), SpaceOutliner *soops, TreeElement *te, const eOLSetState set) @@ -460,10 +460,21 @@ static eOLDrawState tree_element_active_camera(bContext *UNUSED(C), Object *ob = (Object *)outliner_search_back(soops, te, ID_OB); if (set != OL_SETSEL_NONE) { + scene->camera = ob; + + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = bmain->wm.first; + + WM_windows_scene_data_sync(&wm->windows, scene); + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | NA_EDITED, NULL); + return OL_DRAWSEL_NONE; } - - return scene->camera == ob; + else { + return scene->camera == ob; + } } static eOLDrawState tree_element_active_world(bContext *C, @@ -1083,6 +1094,13 @@ eOLDrawState tree_element_type_active(bContext *C, /* ================================================ */ +/* Activate a tree store element and set the walk navigation start element */ +void outliner_element_activate(SpaceOutliner *soops, TreeStoreElem *tselem) +{ + outliner_flag_set(&soops->tree, TSE_ACTIVE | TSE_ACTIVE_WALK, false); + tselem->flag |= TSE_ACTIVE | TSE_ACTIVE_WALK; +} + /** * Action when clicking to activate an item (typically under the mouse cursor), * but don't do any cursor intersection checks. @@ -1114,7 +1132,8 @@ static void do_outliner_item_activate_tree_element(bContext *C, else if (tselem->type == TSE_POSE_BASE) { /* Support pose mode toggle, keeping the active object as is. */ } - else { + else if (soops->flag & SO_SYNC_SELECT) { + /* Only activate when synced selection is enabled */ tree_element_set_active_object(C, scene, view_layer, @@ -1125,6 +1144,9 @@ static void do_outliner_item_activate_tree_element(bContext *C, recursive && tselem->type == 0); } + /* Mark as active in the outliner */ + outliner_element_activate(soops, tselem); + if (tselem->type == 0) { // the lib blocks /* editmode? */ if (te->idcode == ID_SCE) { @@ -1189,7 +1211,7 @@ static void do_outliner_item_activate_tree_element(bContext *C, tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false); } } - else { + else if (soops->flag & SO_SYNC_SELECT) { tree_element_type_active(C, scene, view_layer, @@ -1211,7 +1233,8 @@ void outliner_item_select(SpaceOutliner *soops, const bool toggle) { TreeStoreElem *tselem = TREESTORE(te); - const short new_flag = toggle ? (tselem->flag ^ TSE_SELECTED) : (tselem->flag | TSE_SELECTED); + const short new_flag = (toggle && (tselem->flag & TSE_ACTIVE)) ? (tselem->flag ^ TSE_SELECTED) : + (tselem->flag | TSE_SELECTED); if (extend == false) { outliner_flag_set(&soops->tree, TSE_SELECTED, false); @@ -1219,24 +1242,66 @@ void outliner_item_select(SpaceOutliner *soops, tselem->flag = new_flag; } -static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_children) +static void do_outliner_range_select_recursive(ListBase *lb, + TreeElement *active, + TreeElement *cursor, + bool *selecting) { - TreeStoreElem *tselem = TREESTORE(te); - if (toggle_children) { - tselem->flag &= ~TSE_CLOSED; + for (TreeElement *te = lb->first; te; te = te->next) { + if (*selecting) { + TREESTORE(te)->flag |= TSE_SELECTED; + } - const bool all_opened = !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1); - outliner_flag_set(&te->subtree, TSE_CLOSED, all_opened); - } - else { - tselem->flag ^= TSE_CLOSED; + /* Set state for selection */ + if (te == active || te == cursor) { + *selecting = !*selecting; + } + + if (*selecting) { + TREESTORE(te)->flag |= TSE_SELECTED; + } + + /* Don't look inside closed elements */ + if (!(TREESTORE(te)->flag & TSE_CLOSED)) { + do_outliner_range_select_recursive(&te->subtree, active, cursor, selecting); + } } } -static bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) +/* Select a range of items between cursor and active element */ +static void do_outliner_range_select(bContext *C, SpaceOutliner *soops, TreeElement *cursor) { - return ((te->flag & TE_ICONROW) == 0) && (view_co_x > te->xs) && - (view_co_x < te->xs + UI_UNIT_X); + TreeElement *active = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE); + outliner_flag_set(&soops->tree, TSE_ACTIVE_WALK, false); + + if (!active) { + outliner_item_select(soops, cursor, false, false); + outliner_item_do_activate_from_tree_element(C, cursor, TREESTORE(cursor), false, false); + return; + } + + TreeStoreElem *tselem = TREESTORE(active); + const bool active_selected = (tselem->flag & TSE_SELECTED); + + outliner_flag_set(&soops->tree, TSE_SELECTED | TSE_ACTIVE_WALK, false); + + /* Select active if under cursor */ + if (active == cursor) { + TREESTORE(cursor)->flag |= TSE_SELECTED; + return; + } + + /* If active is not selected, just select the element under the cursor */ + if (!active_selected || !outliner_is_element_visible(active)) { + outliner_item_select(soops, cursor, false, false); + outliner_item_do_activate_from_tree_element(C, cursor, TREESTORE(cursor), false, false); + return; + } + + outliner_flag_set(&soops->tree, TSE_SELECTED, false); + + bool selecting = false; + do_outliner_range_select_recursive(&soops->tree, active, cursor, &selecting); } static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops, @@ -1247,7 +1312,7 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops, } /** - * A version of #outliner_item_do_acticate_from_cursor that takes the tree element directly. + * A version of #outliner_item_do_activate_from_cursor that takes the tree element directly. * and doesn't depend on the pointer position. * * This allows us to simulate clicking on an item without dealing with the mouse cursor. @@ -1271,10 +1336,11 @@ void outliner_item_do_activate_from_tree_element( static int outliner_item_do_activate_from_cursor(bContext *C, const int mval[2], const bool extend, - const bool recursive, + const bool use_range, const bool deselect_all) { ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te; float view_mval[2]; @@ -1292,21 +1358,36 @@ static int outliner_item_do_activate_from_cursor(bContext *C, changed = true; } } - else if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) { - outliner_item_toggle_closed(te, extend); - changed = true; - rebuild_tree = true; + /* Don't allow toggle on scene collection */ + else if ((TREESTORE(te)->type != TSE_VIEW_COLLECTION_BASE) && + outliner_item_is_co_within_close_toggle(te, view_mval[0])) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } else { - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - /* the row may also contain children, if one is hovered we want this instead of current te */ - TreeElement *activate_te = outliner_find_item_at_x_in_row(soops, te, view_mval[0]); + + /* The row may also contain children, if one is hovered we want this instead of current te */ + bool merged_elements = false; + TreeElement *activate_te = outliner_find_item_at_x_in_row( + soops, te, view_mval[0], &merged_elements); + + /* If the selected icon was an aggregate of multiple elements, run the search popup */ + if (merged_elements) { + merged_element_search_menu_invoke(C, te, activate_te); + return OPERATOR_CANCELLED; + } + TreeStoreElem *activate_tselem = TREESTORE(activate_te); - outliner_item_select(soops, activate_te, extend, extend); - do_outliner_item_activate_tree_element( - C, scene, view_layer, soops, activate_te, activate_tselem, extend, recursive); + if (use_range) { + do_outliner_range_select(C, soops, activate_te); + } + else { + outliner_item_select(soops, activate_te, extend, extend); + do_outliner_item_activate_tree_element( + C, scene, view_layer, soops, activate_te, activate_tselem, extend, false); + } + changed = true; } @@ -1318,6 +1399,10 @@ static int outliner_item_do_activate_from_cursor(bContext *C, ED_region_tag_redraw_no_rebuild(ar); } ED_undo_push(C, "Outliner selection change"); + + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } } return OPERATOR_FINISHED; @@ -1327,9 +1412,9 @@ static int outliner_item_do_activate_from_cursor(bContext *C, static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool recursive = RNA_boolean_get(op->ptr, "recursive"); + const bool use_range = RNA_boolean_get(op->ptr, "extend_range"); const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - return outliner_item_do_activate_from_cursor(C, event->mval, extend, recursive, deselect_all); + return outliner_item_do_activate_from_cursor(C, event->mval, extend, use_range, deselect_all); } void OUTLINER_OT_item_activate(wmOperatorType *ot) @@ -1344,7 +1429,10 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) PropertyRNA *prop; RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation"); - RNA_def_boolean(ot->srna, "recursive", false, "Recursive", "Select Objects and their children"); + prop = RNA_def_boolean( + ot->srna, "extend_range", false, "Extend Range", "Select a range from active element"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect_all", false, @@ -1402,9 +1490,44 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); ED_region_tag_redraw(ar); + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } + return OPERATOR_FINISHED; } +/* Find if x coordinate is over an icon or name */ +static bool outliner_item_is_co_over_name_icons(TreeElement *te, float view_co_x) +{ + /* Special case: count area left of Scene Collection as empty space */ + bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ? + (view_co_x > te->xs + UI_UNIT_X) : + (view_co_x > te->xs); + + return outside_left && (view_co_x < te->xend); +} + +static int outliner_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceOutliner *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + float view_mval[2]; + const bool tweak = RNA_boolean_get(op->ptr, "tweak"); + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + /* Find element clicked on */ + TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + /* Pass through if click is over name or icons, or not tweak event */ + if (te && tweak && outliner_item_is_co_over_name_icons(te, view_mval[0])) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + return WM_gesture_box_invoke(C, op, event); +} + void OUTLINER_OT_select_box(wmOperatorType *ot) { /* identifiers */ @@ -1413,7 +1536,7 @@ void OUTLINER_OT_select_box(wmOperatorType *ot) ot->description = "Use box selection to select tree elements"; /* api callbacks */ - ot->invoke = WM_gesture_box_invoke; + ot->invoke = outliner_box_select_invoke; ot->exec = outliner_box_select_exec; ot->modal = WM_gesture_box_modal; ot->cancel = WM_gesture_box_cancel; @@ -1424,8 +1547,240 @@ void OUTLINER_OT_select_box(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ + PropertyRNA *prop; + + prop = RNA_def_boolean( + ot->srna, "tweak", false, "Tweak", "Tweak gesture from empty space for box selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_gesture_box(ot); WM_operator_properties_select_operation_simple(ot); } /* ****************************************************** */ + +/* **************** Walk Select Tool ****************** */ + +/* Given a tree element return the rightmost child that is visible in the outliner */ +static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *soops, TreeElement *te) +{ + while (te->subtree.last) { + if (TSELEM_OPEN(TREESTORE(te), soops)) { + te = te->subtree.last; + } + else { + break; + } + } + return te; +} + +/* Find previous visible element in the tree */ +static TreeElement *outliner_find_previous_element(SpaceOutliner *soops, TreeElement *walk_element) +{ + if (walk_element->prev) { + walk_element = outliner_find_rightmost_visible_child(soops, walk_element->prev); + } + else if (walk_element->parent) { + /* Use parent if at beginning of list */ + walk_element = walk_element->parent; + } + + return walk_element; +} + +/* Recursively search up the tree until a successor to a given element is found */ +static TreeElement *outliner_element_find_successor_in_parents(TreeElement *te) +{ + TreeElement *successor = te; + while (successor->parent) { + if (successor->parent->next) { + te = successor->parent->next; + break; + } + else { + successor = successor->parent; + } + } + + return te; +} + +/* Find next visible element in the tree */ +static TreeElement *outliner_find_next_element(SpaceOutliner *soops, TreeElement *walk_element) +{ + TreeStoreElem *tselem = TREESTORE(walk_element); + + if (TSELEM_OPEN(tselem, soops) && walk_element->subtree.first) { + walk_element = walk_element->subtree.first; + } + else if (walk_element->next) { + walk_element = walk_element->next; + } + else { + walk_element = outliner_element_find_successor_in_parents(walk_element); + } + + return walk_element; +} + +static TreeElement *do_outliner_select_walk(SpaceOutliner *soops, + TreeElement *walk_element, + const int direction, + const bool extend, + const bool toggle_all) +{ + TreeStoreElem *tselem = TREESTORE(walk_element); + + if (!extend) { + outliner_flag_set(&soops->tree, TSE_SELECTED, false); + } + tselem->flag &= ~TSE_ACTIVE_WALK; + + switch (direction) { + case OUTLINER_SELECT_WALK_UP: + walk_element = outliner_find_previous_element(soops, walk_element); + break; + case OUTLINER_SELECT_WALK_DOWN: + walk_element = outliner_find_next_element(soops, walk_element); + break; + case OUTLINER_SELECT_WALK_LEFT: + outliner_item_openclose(walk_element, false, toggle_all); + break; + case OUTLINER_SELECT_WALK_RIGHT: + outliner_item_openclose(walk_element, true, toggle_all); + break; + } + + TreeStoreElem *tselem_new = TREESTORE(walk_element); + + /* If new element is already selected, deselect the previous element */ + if (extend) { + tselem->flag = (tselem_new->flag & TSE_SELECTED) ? (tselem->flag & ~TSE_SELECTED) : + (tselem->flag | TSE_SELECTED); + } + + tselem_new->flag |= TSE_SELECTED | TSE_ACTIVE_WALK; + + return walk_element; +} + +/* Find walk select element, or set it if it does not exist. + * Changed is set to true if walk element is found, false if it was set */ +static TreeElement *find_walk_select_start_element(SpaceOutliner *soops, bool *changed) +{ + TreeElement *walk_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE_WALK); + + *changed = false; + + /* If no walk element exists, start from active */ + if (!walk_element) { + TreeElement *active_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE); + + /* If no active element exists, use the first element in the tree */ + if (!active_element) { + walk_element = soops->tree.first; + } + else { + walk_element = active_element; + } + + *changed = true; + } + + /* If walk element is not visible, set that element's first visible parent as walk element */ + if (!outliner_is_element_visible(walk_element)) { + TREESTORE(walk_element)->flag &= ~TSE_ACTIVE_WALK; + + while (!outliner_is_element_visible(walk_element)) { + walk_element = walk_element->parent; + } + *changed = true; + } + + return walk_element; +} + +/* Scroll the outliner when the walk element reaches the top or bottom boundary */ +static void outliner_walk_scroll(ARegion *ar, TreeElement *te) +{ + /* Account for the header height */ + int y_max = ar->v2d.cur.ymax - UI_UNIT_Y; + int y_min = ar->v2d.cur.ymin; + + /* Scroll if walked position is beyond the border */ + if (te->ys > y_max) { + outliner_scroll_view(ar, te->ys - y_max); + } + else if (te->ys < y_min) { + outliner_scroll_view(ar, -(y_min - te->ys)); + } +} + +static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + SpaceOutliner *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + + const short direction = RNA_enum_get(op->ptr, "direction"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool toggle_all = RNA_boolean_get(op->ptr, "toggle_all"); + + bool changed; + TreeElement *walk_element = find_walk_select_start_element(soops, &changed); + + /* If finding the starting walk select element did not move the element, proceed to walk */ + if (!changed) { + walk_element = do_outliner_select_walk(soops, walk_element, direction, extend, toggle_all); + } + else { + TREESTORE(walk_element)->flag |= TSE_SELECTED | TSE_ACTIVE_WALK; + } + + /* Scroll outliner to focus on walk element */ + outliner_walk_scroll(ar, walk_element); + + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } + ED_region_tag_redraw(ar); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_select_walk(wmOperatorType *ot) +{ + static const EnumPropertyItem direction_items[] = { + {OUTLINER_SELECT_WALK_UP, "UP", 0, "Up", ""}, + {OUTLINER_SELECT_WALK_DOWN, "DOWN", 0, "Down", ""}, + {OUTLINER_SELECT_WALK_LEFT, "LEFT", 0, "Left", ""}, + {OUTLINER_SELECT_WALK_RIGHT, "RIGHT", 0, "Right", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Walk Select"; + ot->idname = "OUTLINER_OT_select_walk"; + ot->description = "Use walk navigation to select tree elements"; + + /* api callbacks */ + ot->invoke = outliner_walk_select_invoke; + ot->poll = ED_operator_outliner_active; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_enum(ot->srna, + "direction", + direction_items, + 0, + "Walk Direction", + "Select element in this direction"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection on walk"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean( + ot->srna, "toggle_all", false, "Toggle All", "Toggle open/close hierarchy"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/* ****************************************************** */ diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c new file mode 100644 index 00000000000..a8aeb7ea4e1 --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_sync.c @@ -0,0 +1,548 @@ +/* + * 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) 2004 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spoutliner + */ + +#include <stdio.h> + +#include "DNA_armature_types.h" +#include "DNA_layer_types.h" +#include "DNA_outliner_types.h" +#include "DNA_screen_types.h" +#include "DNA_sequence_types.h" +#include "DNA_space_types.h" + +#include "BLI_compiler_compat.h" +#include "BLI_ghash.h" + +#include "BKE_armature.h" +#include "BKE_context.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_sequencer.h" + +#include "DEG_depsgraph.h" + +#include "ED_armature.h" +#include "ED_object.h" +#include "ED_outliner.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "outliner_intern.h" + +/* Functions for tagging outliner selection syncing is dirty from operators */ +void ED_outliner_select_sync_from_object_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_OBJECT; +} + +void ED_outliner_select_sync_from_edit_bone_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE; +} + +void ED_outliner_select_sync_from_pose_bone_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE; +} + +void ED_outliner_select_sync_from_sequence_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE; +} + +bool ED_outliner_select_sync_is_dirty(const bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + return wm->outliner_sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_ALL; +} + +/* Copy sync select dirty flag from window manager to all outliners to be synced lazily on draw */ +void ED_outliner_select_sync_flag_outliners(const bContext *C) +{ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_OUTLINER) { + SpaceOutliner *soutliner = (SpaceOutliner *)sl; + + soutliner->sync_select_dirty |= wm->outliner_sync_select_dirty; + } + } + } + } + + /* Clear global sync flag */ + wm->outliner_sync_select_dirty = 0; +} + +/** + * Outliner sync select dirty flags are not enough to determine which types to sync, + * outliner display mode also needs to be considered. This stores the types of data + * to sync to increase code clarity. + */ +typedef struct SyncSelectTypes { + bool object; + bool edit_bone; + bool pose_bone; + bool sequence; +} SyncSelectTypes; + +/** + * Set which types of data to sync when syncing selection from the outliner based on object + * interaction mode and outliner display mode + */ +static void outliner_sync_select_from_outliner_set_types(bContext *C, + SpaceOutliner *soops, + SyncSelectTypes *sync_types) +{ + Object *obact = CTX_data_active_object(C); + Object *obedit = CTX_data_edit_object(C); + + const bool sequence_view = soops->outlinevis == SO_SEQUENCE; + + sync_types->object = !sequence_view && (obact && obact->mode == OB_MODE_OBJECT); + sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE); + sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE); + sync_types->sequence = sequence_view; +} + +/** + * Current dirty flags and outliner display mode determine which type of syncing should occur. + * This is to ensure sync flag data is not lost on sync in the wrong display mode. + */ +static void outliner_sync_select_to_outliner_set_types(const bContext *C, + SpaceOutliner *soops, + SyncSelectTypes *sync_types) +{ + Object *obact = CTX_data_active_object(C); + Object *obedit = CTX_data_edit_object(C); + + const bool sequence_view = soops->outlinevis == SO_SEQUENCE; + + sync_types->object = !sequence_view && (obact && obact->mode == OB_MODE_OBJECT) && + (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_OBJECT); + sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE) && + (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE); + sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE) && + (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE); + sync_types->sequence = sequence_view && + (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE); +} + +/** + * Stores items selected from a sync from the outliner. Prevents syncing the selection + * state of the last instance of an object linked in multiple collections. + */ +typedef struct SelectedItems { + GSet *objects; + GSet *edit_bones; + GSet *pose_bones; +} SelectedItems; + +static void selected_items_init(SelectedItems *selected_items) +{ + selected_items->objects = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + selected_items->edit_bones = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + selected_items->pose_bones = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); +} + +static void selected_items_free(SelectedItems *selected_items) +{ + BLI_gset_free(selected_items->objects, NULL); + BLI_gset_free(selected_items->edit_bones, NULL); + BLI_gset_free(selected_items->pose_bones, NULL); +} + +/* Check if an instance of this object been selected by the sync */ +static bool is_object_selected(GSet *selected_objects, Base *base) +{ + return BLI_gset_haskey(selected_objects, base); +} + +/* Check if an instance of this edit bone been selected by the sync */ +static bool is_edit_bone_selected(GSet *selected_ebones, EditBone *ebone) +{ + return BLI_gset_haskey(selected_ebones, ebone); +} + +/* Check if an instance of this pose bone been selected by the sync */ +static bool is_pose_bone_selected(GSet *selected_pbones, bPoseChannel *pchan) +{ + return BLI_gset_haskey(selected_pbones, pchan); +} + +/* Add element's data to selected item set */ +static void add_selected_item(GSet *selected, void *data) +{ + BLI_gset_add(selected, data); +} + +static void outliner_select_sync_to_object(ViewLayer *view_layer, + TreeElement *te, + TreeStoreElem *tselem, + GSet *selected_objects) +{ + Object *ob = (Object *)tselem->id; + Base *base = (te->directdata) ? (Base *)te->directdata : + BKE_view_layer_base_find(view_layer, ob); + + if (base && (base->flag & BASE_SELECTABLE)) { + if (tselem->flag & TSE_SELECTED) { + ED_object_base_select(base, BA_SELECT); + + add_selected_item(selected_objects, base); + } + else if (!is_object_selected(selected_objects, base)) { + ED_object_base_select(base, BA_DESELECT); + } + } +} + +static void outliner_select_sync_to_edit_bone(ViewLayer *view_layer, + TreeElement *te, + TreeStoreElem *tselem, + GSet *selected_ebones) +{ + bArmature *arm = (bArmature *)tselem->id; + EditBone *ebone = (EditBone *)te->directdata; + + short bone_flag = ebone->flag; + + if (EBONE_SELECTABLE(arm, ebone)) { + if (tselem->flag & TSE_SELECTED) { + ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + + add_selected_item(selected_ebones, ebone); + } + else if (!is_edit_bone_selected(selected_ebones, ebone)) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + + /* Tag if selection changed */ + if (bone_flag != ebone->flag) { + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, obedit); + } +} + +static void outliner_select_sync_to_pose_bone(TreeElement *te, + TreeStoreElem *tselem, + GSet *selected_pbones) +{ + Object *ob = (Object *)tselem->id; + bArmature *arm = ob->data; + bPoseChannel *pchan = (bPoseChannel *)te->directdata; + + short bone_flag = pchan->bone->flag; + + if (PBONE_SELECTABLE(arm, pchan->bone)) { + if (tselem->flag & TSE_SELECTED) { + pchan->bone->flag |= BONE_SELECTED; + + add_selected_item(selected_pbones, pchan); + } + else if (!is_pose_bone_selected(selected_pbones, pchan)) { + pchan->bone->flag &= ~BONE_SELECTED; + } + } + + /* Tag if selection changed */ + if (bone_flag != pchan->bone->flag) { + DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, ob); + } +} + +static void outliner_select_sync_to_sequence(Scene *scene, TreeStoreElem *tselem) +{ + Sequence *seq = (Sequence *)tselem->id; + + if (tselem->flag & TSE_ACTIVE) { + BKE_sequencer_active_set(scene, seq); + } + + if (tselem->flag & TSE_SELECTED) { + seq->flag |= SELECT; + } + else { + seq->flag &= ~SELECT; + } +} + +/** Sync select and active flags from outliner to active view layer, bones, and sequencer. */ +static void outliner_sync_selection_from_outliner(Scene *scene, + ViewLayer *view_layer, + ListBase *tree, + const SyncSelectTypes *sync_types, + SelectedItems *selected_items) +{ + + for (TreeElement *te = tree->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tselem->type == 0 && te->idcode == ID_OB) { + if (sync_types->object) { + outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects); + } + } + else if (tselem->type == TSE_EBONE) { + if (sync_types->edit_bone) { + outliner_select_sync_to_edit_bone(view_layer, te, tselem, selected_items->edit_bones); + } + } + else if (tselem->type == TSE_POSE_CHANNEL) { + if (sync_types->pose_bone) { + outliner_select_sync_to_pose_bone(te, tselem, selected_items->pose_bones); + } + } + else if (tselem->type == TSE_SEQUENCE) { + if (sync_types->sequence) { + outliner_select_sync_to_sequence(scene, tselem); + } + } + + outliner_sync_selection_from_outliner( + scene, view_layer, &te->subtree, sync_types, selected_items); + } +} + +/* Set clean outliner and mark other outliners for syncing */ +void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *soops) +{ + /* Don't sync in certain outliner display modes */ + if (ELEM(soops->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS)) { + return; + } + + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + SyncSelectTypes sync_types; + outliner_sync_select_from_outliner_set_types(C, soops, &sync_types); + + /* To store elements that have been selected to prevent linked object sync errors */ + SelectedItems selected_items; + + selected_items_init(&selected_items); + + outliner_sync_selection_from_outliner( + scene, view_layer, &soops->tree, &sync_types, &selected_items); + + selected_items_free(&selected_items); + + /* Tag for updates */ + if (sync_types.object) { + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + } + if (sync_types.sequence) { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + } + + /* Clear outliner sync select dirty flag to prevent a sync to the outliner on draw */ + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_ALL; +} + +static void outliner_select_sync_from_object(ViewLayer *view_layer, + SpaceOutliner *soops, + Object *obact, + TreeElement *te, + TreeStoreElem *tselem) +{ + Object *ob = (Object *)tselem->id; + Base *base = (te->directdata) ? (Base *)te->directdata : + BKE_view_layer_base_find(view_layer, ob); + const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0); + + if (base && (ob == obact)) { + outliner_element_activate(soops, tselem); + } + + if (is_selected) { + tselem->flag |= TSE_SELECTED; + } + else { + tselem->flag &= ~TSE_SELECTED; + } +} + +static void outliner_select_sync_from_edit_bone(SpaceOutliner *soops, + EditBone *ebone_active, + TreeElement *te, + TreeStoreElem *tselem) +{ + EditBone *ebone = (EditBone *)te->directdata; + + if (ebone == ebone_active) { + outliner_element_activate(soops, tselem); + } + + if (ebone->flag & BONE_SELECTED) { + tselem->flag |= TSE_SELECTED; + } + else { + tselem->flag &= ~TSE_SELECTED; + } +} + +static void outliner_select_sync_from_pose_bone(SpaceOutliner *soops, + bPoseChannel *pchan_active, + TreeElement *te, + TreeStoreElem *tselem) +{ + bPoseChannel *pchan = (bPoseChannel *)te->directdata; + Bone *bone = pchan->bone; + + if (pchan == pchan_active) { + outliner_element_activate(soops, tselem); + } + + if (bone->flag & BONE_SELECTED) { + tselem->flag |= TSE_SELECTED; + } + else { + tselem->flag &= ~TSE_SELECTED; + } +} + +static void outliner_select_sync_from_sequence(SpaceOutliner *soops, + Sequence *sequence_active, + TreeStoreElem *tselem) +{ + Sequence *seq = (Sequence *)tselem->id; + + if (seq == sequence_active) { + outliner_element_activate(soops, tselem); + } + + if (seq->flag & SELECT) { + tselem->flag |= TSE_SELECTED; + } + else { + tselem->flag &= ~TSE_SELECTED; + } +} + +/** + * Contains active object, bones, and sequence for syncing to prevent getting active data + * repeatedly throughout syncing to the outliner. + */ +typedef struct SyncSelectActiveData { + Object *object; + EditBone *edit_bone; + bPoseChannel *pose_channel; + Sequence *sequence; +} SyncSelectActiveData; + +/** Sync select and active flags from active view layer, bones, and sequences to the outliner. */ +static void outliner_sync_selection_to_outliner(ViewLayer *view_layer, + SpaceOutliner *soops, + ListBase *tree, + SyncSelectActiveData *active_data, + const SyncSelectTypes *sync_types) +{ + for (TreeElement *te = tree->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tselem->type == 0 && te->idcode == ID_OB) { + if (sync_types->object) { + outliner_select_sync_from_object(view_layer, soops, active_data->object, te, tselem); + } + } + else if (tselem->type == TSE_EBONE) { + if (sync_types->edit_bone) { + outliner_select_sync_from_edit_bone(soops, active_data->edit_bone, te, tselem); + } + } + else if (tselem->type == TSE_POSE_CHANNEL) { + if (sync_types->pose_bone) { + outliner_select_sync_from_pose_bone(soops, active_data->pose_channel, te, tselem); + } + } + else if (tselem->type == TSE_SEQUENCE) { + if (sync_types->sequence) { + outliner_select_sync_from_sequence(soops, active_data->sequence, tselem); + } + } + else { + tselem->flag &= ~TSE_SELECTED; + } + + /* Sync subtree elements */ + outliner_sync_selection_to_outliner(view_layer, soops, &te->subtree, active_data, sync_types); + } +} + +/* Get active data from context */ +static void get_sync_select_active_data(const bContext *C, SyncSelectActiveData *active_data) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + active_data->object = OBACT(view_layer); + active_data->edit_bone = CTX_data_active_bone(C); + active_data->pose_channel = CTX_data_active_pose_bone(C); + active_data->sequence = BKE_sequencer_active_get(scene); +} + +/* If outliner is dirty sync selection from view layer and sequwncer */ +void outliner_sync_selection(const bContext *C, SpaceOutliner *soops) +{ + if (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_ALL) { + ViewLayer *view_layer = CTX_data_view_layer(C); + + /* Set which types of data to sync from sync dirty flag and outliner display mode */ + SyncSelectTypes sync_types; + outliner_sync_select_to_outliner_set_types(C, soops, &sync_types); + + /* Store active object, bones, and sequence */ + SyncSelectActiveData active_data; + get_sync_select_active_data(C, &active_data); + + outliner_sync_selection_to_outliner( + view_layer, soops, &soops->tree, &active_data, &sync_types); + + /* Keep any unsynced data in the dirty flag */ + if (sync_types.object) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT; + } + if (sync_types.edit_bone) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE; + } + if (sync_types.pose_bone) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE; + } + if (sync_types.sequence) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE; + } + } +} diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index f9905cc4fcd..49f6189c17c 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -63,6 +63,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_scene.h" #include "ED_screen.h" #include "ED_sequencer.h" @@ -478,6 +479,129 @@ void OUTLINER_OT_scene_operation(wmOperatorType *ot) } /* ******************************************** */ +/** + * Stores the parent and a child element of a merged icon-row icon for + * the merged select popup menu. The sub-tree of the parent is searched and + * the child is needed to only show elements of the same type in the popup. + */ +typedef struct MergedSearchData { + TreeElement *parent_element; + TreeElement *select_element; +} MergedSearchData; + +static void merged_element_search_cb_recursive( + const ListBase *tree, short tselem_type, short type, const char *str, uiSearchItems *items) +{ + char name[64]; + int iconid; + + for (TreeElement *te = tree->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tree_element_id_type_to_index(te) == type && tselem_type == tselem->type) { + if (BLI_strcasestr(te->name, str)) { + BLI_strncpy(name, te->name, 64); + + iconid = tree_element_get_icon(tselem, te).icon; + + /* Don't allow duplicate named items */ + if (UI_search_items_find_index(items, name) == -1) { + if (!UI_search_item_add(items, name, te, iconid)) { + break; + } + } + } + } + + merged_element_search_cb_recursive(&te->subtree, tselem_type, type, str, items); + } +} + +/* Get a list of elements that match the search string */ +static void merged_element_search_cb(const bContext *UNUSED(C), + void *data, + const char *str, + uiSearchItems *items) +{ + MergedSearchData *search_data = (MergedSearchData *)data; + TreeElement *parent = search_data->parent_element; + TreeElement *te = search_data->select_element; + + int type = tree_element_id_type_to_index(te); + + merged_element_search_cb_recursive(&parent->subtree, TREESTORE(te)->type, type, str, items); +} + +/* Activate an element from the merged element search menu */ +static void merged_element_search_call_cb(struct bContext *C, void *UNUSED(arg1), void *element) +{ + SpaceOutliner *soops = CTX_wm_space_outliner(C); + TreeElement *te = (TreeElement *)element; + + outliner_item_select(soops, te, false, false); + outliner_item_do_activate_from_tree_element(C, te, te->store_elem, false, false); + + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } +} + +/** Merged element search menu + * Created on activation of a merged or aggregated icon-row icon. + */ +static uiBlock *merged_element_search_menu(bContext *C, ARegion *ar, void *data) +{ + static char search[64] = ""; + uiBlock *block; + uiBut *but; + + /* Clear search on each menu creation */ + *search = '\0'; + + block = UI_block_begin(C, ar, __func__, UI_EMBOSS); + UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + + short menu_width = 10 * UI_UNIT_X; + but = uiDefSearchBut( + block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, ""); + UI_but_func_search_set( + but, NULL, merged_element_search_cb, data, false, merged_element_search_call_cb, NULL); + UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); + + /* Fake button to hold space for search items */ + uiDefBut(block, + UI_BTYPE_LABEL, + 0, + "", + 10, + 10 - UI_searchbox_size_y(), + menu_width, + UI_searchbox_size_y(), + NULL, + 0, + 0, + 0, + 0, + NULL); + + /* Center the menu on the cursor */ + UI_block_bounds_set_popup(block, 6, (const int[2]){-(menu_width / 2), 0}); + + return block; +} + +void merged_element_search_menu_invoke(bContext *C, + TreeElement *parent_te, + TreeElement *activate_te) +{ + MergedSearchData *select_data = MEM_callocN(sizeof(MergedSearchData), "merge_search_data"); + select_data->parent_element = parent_te; + select_data->select_element = activate_te; + + UI_popup_block_invoke(C, merged_element_search_menu, select_data, MEM_freeN); +} + static void object_select_cb(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index cc062467dbe..ec5510cdf84 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -297,7 +297,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops, ViewLayer *view_layer; for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) { - TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, te, TSE_R_LAYER, 0); + TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, ten, TSE_R_LAYER, 0); tenlay->name = view_layer->name; tenlay->directdata = view_layer; } @@ -314,7 +314,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops, ten = outliner_add_element(soops, lb, sce, te, TSE_SCENE_OBJECTS_BASE, 0); ten->name = IFACE_("Objects"); FOREACH_SCENE_OBJECT_BEGIN (sce, ob) { - outliner_add_element(soops, &ten->subtree, ob, NULL, 0, 0); + outliner_add_element(soops, &ten->subtree, ob, ten, 0, 0); } FOREACH_SCENE_OBJECT_END; outliner_make_object_parent_hierarchy(&ten->subtree); @@ -2008,6 +2008,9 @@ static int outliner_exclude_filter_get(SpaceOutliner *soops) case SO_FILTER_OB_VISIBLE: exclude_filter |= SO_FILTER_OB_STATE_VISIBLE; break; + case SO_FILTER_OB_INVISIBLE: + exclude_filter |= SO_FILTER_OB_STATE_INVISIBLE; + break; case SO_FILTER_OB_SELECTED: exclude_filter |= SO_FILTER_OB_STATE_SELECTED; break; @@ -2086,6 +2089,11 @@ static bool outliner_element_visible_get(ViewLayer *view_layer, return false; } } + else if (exclude_filter & SO_FILTER_OB_STATE_INVISIBLE) { + if ((base->flag & BASE_VISIBLE) != 0) { + return false; + } + } else if (exclude_filter & SO_FILTER_OB_STATE_SELECTED) { if ((base->flag & BASE_SELECTED) == 0) { return false; @@ -2339,7 +2347,8 @@ void outliner_build_tree( te = outliner_add_element(soops, &soops->tree, sce, NULL, 0, 0); tselem = TREESTORE(te); - if (sce == scene && show_opened) { + /* New scene elements open by default */ + if ((sce == scene && show_opened) || !tselem->used) { tselem->flag &= ~TSE_CLOSED; } diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index f57dce97b38..5dfdf6f129b 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -24,11 +24,15 @@ #include "BLI_utildefines.h" #include "DNA_action_types.h" +#include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "BKE_context.h" #include "BKE_outliner_treehash.h" +#include "BKE_layer.h" #include "ED_armature.h" +#include "ED_outliner.h" #include "UI_interface.h" #include "UI_view2d.h" @@ -62,6 +66,38 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, return NULL; } +static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement *parent_te, + float view_co_x, + bool *r_merged) +{ + TreeElement *child_te = parent_te->subtree.first; + + bool over_element = false; + + while (child_te) { + over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); + if ((child_te->flag & TE_ICONROW) && over_element) { + return child_te; + } + else if ((child_te->flag & TE_ICONROW_MERGED) && over_element) { + if (r_merged) { + *r_merged = true; + } + return child_te; + } + + TreeElement *te = outliner_find_item_at_x_in_row_recursive(child_te, view_co_x, r_merged); + if (te != child_te) { + return te; + } + + child_te = child_te->next; + } + + /* return parent if no child is hovered */ + return (TreeElement *)parent_te; +} + /** * Collapsed items can show their children as click-able icons. This function tries to find * such an icon that represents the child item at x-coordinate \a view_co_x (view-space). @@ -70,24 +106,14 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, */ TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *soops, const TreeElement *parent_te, - float view_co_x) + float view_co_x, + bool *r_merged) { - /* if parent_te is opened, it doesn't show childs in row */ + /* if parent_te is opened, it doesn't show children in row */ if (!TSELEM_OPEN(TREESTORE(parent_te), soops)) { - /* no recursion, items can only display their direct children in the row */ - for (TreeElement *child_te = parent_te->subtree.first; - /* don't look further if co_x is smaller than child position*/ - child_te && view_co_x >= child_te->xs; - - child_te = child_te->next) { - if ((child_te->flag & TE_ICONROW) && (view_co_x > child_te->xs) && - (view_co_x < child_te->xend)) { - return child_te; - } - } + return outliner_find_item_at_x_in_row_recursive(parent_te, view_co_x, r_merged); } - /* return parent if no child is hovered */ return (TreeElement *)parent_te; } @@ -300,3 +326,89 @@ float outliner_restrict_columns_width(const SpaceOutliner *soops) } return (num_columns * UI_UNIT_X + V2D_SCROLL_WIDTH); } + +/* Find first tree element in tree with matching treestore flag */ +TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag) +{ + for (TreeElement *te = lb->first; te; te = te->next) { + if ((TREESTORE(te)->flag & flag) == flag) { + return te; + } + TreeElement *active_element = outliner_find_element_with_flag(&te->subtree, flag); + if (active_element) { + return active_element; + } + } + return NULL; +} + +/* Find if element is visible in the outliner tree */ +bool outliner_is_element_visible(const TreeElement *te) +{ + TreeStoreElem *tselem; + + while (te->parent) { + tselem = TREESTORE(te->parent); + + if (tselem->flag & TSE_CLOSED) { + return false; + } + else { + te = te->parent; + } + } + + return true; +} + +/* Find if x coordinate is over element disclosure toggle */ +bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) +{ + return (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X); +} + +/* Scroll view vertically while keeping within total bounds */ +void outliner_scroll_view(ARegion *ar, int delta_y) +{ + int y_min = MIN2(ar->v2d.cur.ymin, ar->v2d.tot.ymin); + + ar->v2d.cur.ymax += delta_y; + ar->v2d.cur.ymin += delta_y; + + /* Adjust view if delta placed view outside total area */ + int offset; + if (ar->v2d.cur.ymax > -UI_UNIT_Y) { + offset = ar->v2d.cur.ymax; + ar->v2d.cur.ymax -= offset; + ar->v2d.cur.ymin -= offset; + } + else if (ar->v2d.cur.ymin < y_min) { + offset = y_min - ar->v2d.cur.ymin; + ar->v2d.cur.ymax += offset; + ar->v2d.cur.ymin += offset; + } +} + +/* Get base of object under cursor. Used for eyedropper tool */ +Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2]) +{ + ARegion *ar = CTX_wm_region(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + SpaceOutliner *soops = CTX_wm_space_outliner(C); + TreeElement *te; + Base *base = NULL; + float view_mval[2]; + + UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]); + + te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + if (te) { + TreeStoreElem *tselem = TREESTORE(te); + if (tselem->type == 0) { + Object *ob = (Object *)tselem->id; + base = (te->directdata) ? (Base *)te->directdata : BKE_view_layer_base_find(view_layer, ob); + } + } + + return base; +} diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 091efc56c09..79880c68120 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -131,6 +131,9 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), ED_region_tag_redraw(ar); break; } + if (wmn->action & NA_EDITED) { + ED_region_tag_redraw(ar); + } break; case NC_OBJECT: switch (wmn->data) { @@ -145,13 +148,8 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), ED_region_tag_redraw(ar); break; case ND_CONSTRAINT: - switch (wmn->action) { - case NA_ADDED: - case NA_REMOVED: - case NA_RENAME: - ED_region_tag_redraw(ar); - break; - } + /* all constraint actions now, for reordering */ + ED_region_tag_redraw(ar); break; case ND_MODIFIER: /* all modifier actions now */ @@ -304,6 +302,8 @@ static SpaceLink *outliner_new(const ScrArea *UNUSED(area), const Scene *UNUSED( soutliner->filter_id_type = ID_GR; soutliner->show_restrict_flags = SO_RESTRICT_ENABLE | SO_RESTRICT_HIDE; soutliner->outlinevis = SO_VIEW_LAYER; + soutliner->sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_ALL; + soutliner->flag |= SO_SYNC_SELECT; /* header */ ar = MEM_callocN(sizeof(ARegion), "header for outliner"); @@ -349,6 +349,9 @@ static SpaceLink *outliner_duplicate(SpaceLink *sl) soutlinern->treestore = NULL; soutlinern->treehash = NULL; + soutlinern->flag |= (soutliner->flag & SO_SYNC_SELECT); + soutlinern->sync_select_dirty = WM_OUTLINER_SYNC_SELECT_FROM_ALL; + return (SpaceLink *)soutlinern; } @@ -415,7 +418,7 @@ void ED_spacetype_outliner(void) /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region"); art->regionid = RGN_TYPE_WINDOW; - art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D; art->init = outliner_main_region_init; art->draw = outliner_main_region_draw; @@ -428,7 +431,7 @@ void ED_spacetype_outliner(void) art = MEM_callocN(sizeof(ARegionType), "spacetype outliner header region"); art->regionid = RGN_TYPE_HEADER; art->prefsizey = HEADERY; - art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER; art->init = outliner_header_region_init; art->draw = outliner_header_region_draw; diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index d57be0c85c3..84ded1dd2c7 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -20,8 +20,8 @@ set(INC ../../blenkernel ../../blenlib ../../blentranslation - ../../gpu ../../depsgraph + ../../gpu ../../imbuf ../../makesdna ../../makesrna diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 07350b5269e..b15acb12d00 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1045,9 +1045,8 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, bmain, depsgraph, scene, rectx, recty, proxy_size, false, &context); context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); - /* sequencer could start rendering, in this case we need to be sure it wouldn't be canceled - * by Esc pressed somewhere in the past - */ + /* Sequencer could start rendering, in this case we need to be sure it wouldn't be canceled + * by Escape pressed somewhere in the past. */ G.is_break = false; /* Rendering can change OGL context. Save & Restore framebuffer. */ diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 57f86059d9d..affb6d3fd88 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -41,6 +41,7 @@ /* for menu/popup icons etc etc*/ +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_sequencer.h" #include "ED_select_utils.h" @@ -49,6 +50,7 @@ /* own include */ #include "sequencer_intern.h" + static void *find_nearest_marker(int UNUSED(d1), int UNUSED(d2)) { return NULL; @@ -254,6 +256,8 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -293,6 +297,8 @@ static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -542,6 +548,8 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); /* allowing tweaks */ @@ -668,6 +676,8 @@ static int sequencer_select_more_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -699,6 +709,8 @@ static int sequencer_select_less_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -750,6 +762,8 @@ static int sequencer_select_linked_pick_invoke(bContext *C, wmOperator *op, cons selected = select_more_less_seq__internal(scene, 1, 1); } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -784,6 +798,8 @@ static int sequencer_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) selected = select_more_less_seq__internal(scene, true, true); } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -832,6 +848,8 @@ static int sequencer_select_handles_exec(bContext *C, wmOperator *op) } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -876,6 +894,8 @@ static int sequencer_select_active_side_exec(bContext *C, wmOperator *op) select_active_side( ed->seqbasep, RNA_enum_get(op->ptr, "side"), seq_act->machine, seq_act->startdisp); + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -934,6 +954,8 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -1311,6 +1333,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) } if (changed) { + ED_outliner_select_sync_from_sequence_tag(C); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 12b446c3f4c..9aa9d14cbc8 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -675,10 +675,9 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *ar) WM_gizmomap_draw(ar->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) { - rcti rect; - ED_region_visible_rect(ar, &rect); - int xoffset = rect.xmin + U.widget_unit; - int yoffset = rect.ymax; + const rcti *rect = ED_region_visible_rect(ar); + int xoffset = rect->xmin + U.widget_unit; + int yoffset = rect->ymax; ED_scene_draw_fps(scene, xoffset, &yoffset); } } diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 8bfb6c87625..c1a3c79b0d8 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -63,6 +63,8 @@ static SpaceLink *text_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scen stext->lheight = 12; stext->tabnumber = 4; stext->margin_column = 80; + stext->showsyntax = true; + stext->showlinenrs = true; /* header */ ar = MEM_callocN(sizeof(ARegion), "header for text"); diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 9dc8dfa93b6..e99bf680077 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -54,6 +54,7 @@ typedef struct TextDrawContext { int font_id; int cwidth; int lheight_dpi; + bool syntax_highlight; } TextDrawContext; static void text_draw_context_init(const SpaceText *st, TextDrawContext *tdc) @@ -61,6 +62,7 @@ static void text_draw_context_init(const SpaceText *st, TextDrawContext *tdc) tdc->font_id = blf_mono_font; tdc->cwidth = 0; tdc->lheight_dpi = st->lheight_dpi; + tdc->syntax_highlight = st->showsyntax && ED_text_is_syntax_highlight_supported(st->text); } static void text_font_begin(const TextDrawContext *tdc) @@ -418,7 +420,7 @@ static int text_draw_wrapped(const SpaceText *st, const char *format, int skip) { - const bool use_syntax = (st->showsyntax && format); + const bool use_syntax = (tdc->syntax_highlight && format); FlattenString fs; int basex, lines; int i, wrap, end, max, columns, padding; /* column */ @@ -514,7 +516,7 @@ static void text_draw(const SpaceText *st, int y, const char *format) { - const bool use_syntax = (st->showsyntax && format); + const bool use_syntax = (tdc->syntax_highlight && format); FlattenString fs; int columns, size, n, w = 0, padding, amount = 0; const char *in = NULL; @@ -1383,8 +1385,8 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi char ch; - // showsyntax must be on or else the format string will be null - if (!text->curl || !st->showsyntax) { + // syntax_highlight must be on or else the format string will be null + if (!text->curl || !tdc->syntax_highlight) { return; } @@ -1576,7 +1578,7 @@ void draw_text_main(SpaceText *st, ARegion *ar) tmp = text->lines.first; lineno = 0; for (i = 0; i < st->top && tmp; i++) { - if (st->showsyntax && !tmp->format) { + if (tdc.syntax_highlight && !tmp->format) { tft->format_line(st, tmp, false); } @@ -1631,7 +1633,7 @@ void draw_text_main(SpaceText *st, ARegion *ar) UI_FontThemeColor(tdc.font_id, TH_TEXT); for (i = 0; y > clip_min_y && i < st->viewlines && tmp; i++, tmp = tmp->next) { - if (st->showsyntax && !tmp->format) { + if (tdc.syntax_highlight && !tmp->format) { tft->format_line(st, tmp, false); } diff --git a/source/blender/editors/space_text/text_format.c b/source/blender/editors/space_text/text_format.c index 8c102dc009e..48ee30e450f 100644 --- a/source/blender/editors/space_text/text_format.c +++ b/source/blender/editors/space_text/text_format.c @@ -25,10 +25,13 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_string_utils.h" #include "DNA_text_types.h" #include "DNA_space_types.h" +#include "ED_text.h" + #include "text_format.h" /****************** flatten string **********************/ @@ -224,3 +227,38 @@ TextFormatType *ED_text_format_get(Text *text) return tft_lb.first; } } + +bool ED_text_is_syntax_highlight_supported(Text *text) +{ + if (text == NULL) { + return false; + } + + TextFormatType *tft; + + const char *text_ext = BLI_path_extension(text->id.name + 2); + if (text_ext == NULL) { + /* Extensionless datablocks are considered highlightable as Python. */ + return true; + } + text_ext++; /* skip the '.' */ + if (BLI_string_is_decimal(text_ext)) { + /* "Text.001" is treated as extensionless, and thus highlightable. */ + return true; + } + + /* Check all text formats in the static list */ + for (tft = tft_lb.first; tft; tft = tft->next) { + /* All formats should have an ext, but just in case */ + const char **ext; + for (ext = tft->ext; *ext; ext++) { + /* If extension matches text name, return the matching tft */ + if (BLI_strcasecmp(text_ext, *ext) == 0) { + return true; + } + } + } + + /* The filename has a non-numerical extension that we could not highlight. */ + return false; +} diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index a8af9c73bf2..e1550deb659 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -1217,36 +1217,34 @@ static int text_comment_exec(bContext *C, wmOperator *op) Text *text = CTX_data_edit_text(C); int type = RNA_enum_get(op->ptr, "type"); - if (txt_has_sel(text)) { - text_drawcache_tag_update(CTX_wm_space_text(C), 0); + text_drawcache_tag_update(CTX_wm_space_text(C), 0); - ED_text_undo_push_init(C); + ED_text_undo_push_init(C); + if (txt_has_sel(text)) { txt_order_cursors(text, false); + } - switch (type) { - case 1: + switch (type) { + case 1: + txt_comment(text); + break; + case -1: + txt_uncomment(text); + break; + default: + if (txt_uncomment(text) == false) { txt_comment(text); - break; - case -1: - txt_uncomment(text); - break; - default: - if (txt_uncomment(text) == false) { - txt_comment(text); - } - break; - } - - text_update_edited(text); + } + break; + } - text_update_cursor_moved(C); - WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text); + text_update_edited(text); - return OPERATOR_FINISHED; - } + text_update_cursor_moved(C); + WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text); - return OPERATOR_CANCELLED; + return OPERATOR_FINISHED; } void TEXT_OT_comment_toggle(wmOperatorType *ot) diff --git a/source/blender/editors/space_text/text_undo.c b/source/blender/editors/space_text/text_undo.c index a6393291f9a..4a628cf70e4 100644 --- a/source/blender/editors/space_text/text_undo.c +++ b/source/blender/editors/space_text/text_undo.c @@ -187,6 +187,7 @@ static bool text_undosys_step_encode(struct bContext *C, Text *text = us->text_ref.ptr; BLI_assert(text == CTX_data_edit_text(C)); + UNUSED_VARS_NDEBUG(C); us->step.data_size += text_undosys_step_encode_to_state(&us->states[1], text); diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 38e8d285c77..b412a72cce1 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -145,7 +145,7 @@ void ED_draw_object_facemap(Depsgraph *depsgraph, facemap_data = CustomData_get_layer(&me->pdata, CD_FACEMAP); - /* use gawain immediate mode fore now */ + /* Make a batch and free it each time for now. */ const int looptris_len = poly_to_tri_count(mpoly_len, mloop_len); const int vbo_len_capacity = looptris_len * 3; int vbo_len_used = 0; diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c index 0045094542f..f8f97848d14 100644 --- a/source/blender/editors/space_view3d/view3d_camera_control.c +++ b/source/blender/editors/space_view3d/view3d_camera_control.c @@ -87,8 +87,7 @@ typedef struct View3DCameraControl { float ofs_backup[3]; /* backup the views offset in case the user cancels flying in non camera mode */ - /* backup the views quat in case the user cancels flying in non camera mode. - * (quat for view, eul for camera) */ + /* backup the views quat in case the user cancels flying in non camera mode. */ float rot_backup[4]; /* remember if were ortho or not, only used for restoring the view if it was a ortho view */ char persp_backup; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 8844428b1bf..6c534ee1b98 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1382,8 +1382,7 @@ void view3d_draw_region_info(const bContext *C, ARegion *ar) ED_region_pixelspace(ar); /* local coordinate visible rect inside region, to accommodate overlapping ui */ - rcti rect; - ED_region_visible_rect(ar, &rect); + const rcti *rect = ED_region_visible_rect(ar); view3d_draw_border(C, ar); view3d_draw_grease_pencil(C); @@ -1399,14 +1398,14 @@ void view3d_draw_region_info(const bContext *C, ARegion *ar) /* The gizmo handles it's own drawing. */ break; case USER_MINI_AXIS_TYPE_MINIMAL: - draw_view_axis(rv3d, &rect); + draw_view_axis(rv3d, rect); case USER_MINI_AXIS_TYPE_NONE: break; } } - int xoffset = rect.xmin + U.widget_unit; - int yoffset = rect.ymax; + int xoffset = rect->xmin + U.widget_unit; + int yoffset = rect->ymax; if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0 && (v3d->overlay.flag & V3D_OVERLAY_HIDE_TEXT) == 0) { if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) { diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c index 040b257bb90..d5772e5052a 100644 --- a/source/blender/editors/space_view3d/view3d_draw_legacy.c +++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c @@ -162,6 +162,7 @@ static void validate_object_select_id( Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact); BLI_assert(ar->regiontype == RGN_TYPE_WINDOW); + UNUSED_VARS_NDEBUG(ar); if (obact_eval && (obact_eval->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT) || BKE_paint_select_face_test(obact_eval))) { @@ -185,7 +186,8 @@ static void validate_object_select_id( } if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE) != 0)) { - DRW_draw_select_id_object(depsgraph, view_layer, ar, v3d, obact, -1); + Base *base = BKE_view_layer_base_find(view_layer, obact); + DRW_select_buffer_context_create(&base, 1, -1); } /* TODO: Create a flag in `DRW_manager` because the drawing is no longer diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index ec7c1c0b3b9..b9adde6f60e 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -404,9 +404,7 @@ static void viewops_data_create(bContext *C, if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) { if (ED_view3d_persp_ensure(depsgraph, vod->v3d, vod->ar)) { /* If we're switching from camera view to the perspective one, - * need to tag viewport update, so camera vuew and borders - * are properly updated. - */ + * need to tag viewport update, so camera view and borders are properly updated. */ ED_region_tag_redraw(vod->ar); } } @@ -517,9 +515,6 @@ static void viewops_data_create(bContext *C, static void viewops_data_free(bContext *C, wmOperator *op) { ARegion *ar; -#if 0 - Paint *p = BKE_paint_get_active_from_context(C); -#endif if (op->customdata) { ViewOpsData *vod = op->customdata; ar = vod->ar; @@ -536,12 +531,9 @@ static void viewops_data_free(bContext *C, wmOperator *op) ar = CTX_wm_region(C); } -#if 0 - if (p && (p->flags & PAINT_FAST_NAVIGATE)) -#endif - { - ED_region_tag_redraw(ar); - } + /* Need to redraw because drawing code uses RV3D_NAVIGATING to draw + * faster while navigation operator runs. */ + ED_region_tag_redraw(ar); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index 3e2e113be3e..dbbc7f2a32e 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -146,42 +146,60 @@ typedef struct FlyInfo { struct Depsgraph *depsgraph; Scene *scene; - wmTimer *timer; /* needed for redraws */ + /** Needed for for updating that isn't triggered by input. */ + wmTimer *timer; short state; bool redraw; bool use_precision; - /* if the user presses shift they can look about - * without moving the direction there looking */ + /** If the user presses shift they can look about without moving the direction there looking. */ bool use_freelook; - bool anim_playing; /* needed for autokeyframing */ + /** + * Needed for auto-keyframing, when animation isn't playing, only keyframe on confirmation. + * + * Currently we can't cancel this operator usefully while recording on animation playback + * (this would need to un-key all previous frames). + */ + bool anim_playing; - int mval[2]; /* latest 2D mouse values */ - int center_mval[2]; /* center mouse values */ - float width, height; /* camera viewport dimensions */ + /** Latest 2D mouse values. */ + int mval[2]; + /** Center mouse values. */ + int center_mval[2]; + /** Camera viewport dimensions. */ + float width, height; #ifdef WITH_INPUT_NDOF - wmNDOFMotionData *ndof; /* latest 3D mouse values */ + /** Latest 3D mouse values. */ + wmNDOFMotionData *ndof; #endif /* fly state state */ - float speed; /* the speed the view is moving per redraw */ - short axis; /* Axis index to move along by default Z to move along the view */ - bool pan_view; /* when true, pan the view instead of rotating */ + /** The speed the view is moving per redraw. */ + float speed; + /** Axis index to move along by default Z to move along the view. */ + short axis; + /** When true, pan the view instead of rotating. */ + bool pan_view; eFlyPanState xlock, zlock; - float xlock_momentum, zlock_momentum; /* nicer dynamics */ - float grid; /* world scale 1.0 default */ + /** Nicer dynamics. */ + float xlock_momentum, zlock_momentum; + /** World scale 1.0 default. */ + float grid; /* compare between last state */ - double time_lastwheel; /* used to accelerate when using the mousewheel a lot */ - double time_lastdraw; /* time between draws */ + /** Used to accelerate when using the mousewheel a lot. */ + double time_lastwheel; + /** Time between draws. */ + double time_lastdraw; void *draw_handle_pixel; /* use for some lag */ - float dvec_prev[3]; /* old for some lag */ + /** Keep the previous value to smooth transitions (use lag). */ + float dvec_prev[3]; struct View3DCameraControl *v3d_camera_control; diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c index ad1a57eb71f..3c911e266a9 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c @@ -246,18 +246,17 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g copy_v3_v3(navgroup->gz_array[GZ_INDEX_ROTATE]->matrix_offset[i], rv3d->viewmat[i]); } - rcti rect_visible; - ED_region_visible_rect(ar, &rect_visible); + const rcti *rect_visible = ED_region_visible_rect(ar); - if ((navgroup->state.rect_visible.xmax == rect_visible.xmax) && - (navgroup->state.rect_visible.ymax == rect_visible.ymax) && + if ((navgroup->state.rect_visible.xmax == rect_visible->xmax) && + (navgroup->state.rect_visible.ymax == rect_visible->ymax) && (navgroup->state.rv3d.is_persp == rv3d->is_persp) && (navgroup->state.rv3d.is_camera == (rv3d->persp == RV3D_CAMOB)) && (navgroup->state.rv3d.viewlock == rv3d->viewlock)) { return; } - navgroup->state.rect_visible = rect_visible; + navgroup->state.rect_visible = *rect_visible; navgroup->state.rv3d.is_persp = rv3d->is_persp; navgroup->state.rv3d.is_camera = (rv3d->persp == RV3D_CAMOB); navgroup->state.rv3d.viewlock = rv3d->viewlock; @@ -268,8 +267,8 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g const float icon_offset = (icon_size * 0.52f) * GIZMO_OFFSET_FAC * UI_DPI_FAC; const float icon_offset_mini = icon_size * GIZMO_MINI_OFFSET_FAC * UI_DPI_FAC; const float co_rotate[2] = { - rect_visible.xmax - icon_offset, - rect_visible.ymax - icon_offset, + rect_visible->xmax - icon_offset, + rect_visible->ymax - icon_offset, }; float icon_offset_from_axis = 0.0f; @@ -286,8 +285,8 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g } const float co[2] = { - rect_visible.xmax - icon_offset_from_axis, - rect_visible.ymax - icon_offset_mini * 0.75f, + rect_visible->xmax - icon_offset_from_axis, + rect_visible->ymax - icon_offset_mini * 0.75f, }; wmGizmo *gz; diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index d20a854c022..37ea10773bf 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -88,6 +88,7 @@ #include "ED_particle.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_sculpt.h" @@ -197,43 +198,31 @@ static bool object_deselect_all_except(ViewLayer *view_layer, Base *b) * \{ */ struct EditSelectBuf_Cache { - Base **bases; - uint bases_len; BLI_bitmap *select_bitmap; }; -static void editselect_buf_cache_init(struct EditSelectBuf_Cache *esel, - ViewContext *vc, - short select_mode) +static void editselect_buf_cache_init(ViewContext *vc, short select_mode) { if (vc->obedit) { - esel->bases = BKE_view_layer_array_from_bases_in_edit_mode( - vc->view_layer, vc->v3d, &esel->bases_len); + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode( + vc->view_layer, vc->v3d, &bases_len); + + DRW_select_buffer_context_create(bases, bases_len, select_mode); + MEM_freeN(bases); } else { /* Use for paint modes, currently only a single object at a time. */ if (vc->obact) { - esel->bases = MEM_mallocN(sizeof(esel->bases), __func__); - esel->bases[0] = BKE_view_layer_base_find(vc->view_layer, vc->obact); - esel->bases_len = 1; + Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact); + DRW_select_buffer_context_create(&base, 1, select_mode); } - else { - esel->bases = NULL; - esel->bases_len = 0; - } - } - - DRW_draw_select_id(vc->depsgraph, vc->ar, vc->v3d, esel->bases, esel->bases_len, select_mode); - - for (int i = 0; i < esel->bases_len; i++) { - esel->bases[i]->object->runtime.select_id = i; } } static void editselect_buf_cache_free(struct EditSelectBuf_Cache *esel) { MEM_SAFE_FREE(esel->select_bitmap); - MEM_SAFE_FREE(esel->bases); } static void editselect_buf_cache_free_voidp(void *esel_voidp) @@ -250,7 +239,7 @@ static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *w wm_userdata->data = esel; wm_userdata->free_fn = editselect_buf_cache_free_voidp; wm_userdata->use_free = true; - editselect_buf_cache_init(esel, vc, select_mode); + editselect_buf_cache_init(vc, select_mode); } /** \} */ @@ -260,6 +249,7 @@ static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *w * \{ */ static bool edbm_backbuf_check_and_select_verts(struct EditSelectBuf_Cache *esel, + Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op) @@ -269,9 +259,12 @@ static bool edbm_backbuf_check_and_select_verts(struct EditSelectBuf_Cache *esel bool changed = false; const BLI_bitmap *select_bitmap = esel->select_bitmap; - uint index = DRW_select_buffer_context_offset_for_object_elem(ob->runtime.select_id, - SCE_SELECT_VERTEX); + uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_VERTEX); + if (index == 0) { + return false; + } + index -= 1; BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); @@ -288,6 +281,7 @@ static bool edbm_backbuf_check_and_select_verts(struct EditSelectBuf_Cache *esel } static bool edbm_backbuf_check_and_select_edges(struct EditSelectBuf_Cache *esel, + Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op) @@ -297,9 +291,12 @@ static bool edbm_backbuf_check_and_select_edges(struct EditSelectBuf_Cache *esel bool changed = false; const BLI_bitmap *select_bitmap = esel->select_bitmap; - uint index = DRW_select_buffer_context_offset_for_object_elem(ob->runtime.select_id, - SCE_SELECT_EDGE); + uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_EDGE); + if (index == 0) { + return false; + } + index -= 1; BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); @@ -316,6 +313,7 @@ static bool edbm_backbuf_check_and_select_edges(struct EditSelectBuf_Cache *esel } static bool edbm_backbuf_check_and_select_faces(struct EditSelectBuf_Cache *esel, + Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op) @@ -325,9 +323,12 @@ static bool edbm_backbuf_check_and_select_faces(struct EditSelectBuf_Cache *esel bool changed = false; const BLI_bitmap *select_bitmap = esel->select_bitmap; - uint index = DRW_select_buffer_context_offset_for_object_elem(ob->runtime.select_id, - SCE_SELECT_FACE); + uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_FACE); + if (index == 0) { + return false; + } + index -= 1; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); @@ -737,10 +738,12 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, { struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data; LassoSelectUserData *data = data_for_edge->data; - const bool is_visible = (data_for_edge->esel ? - BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, - data_for_edge->backbuf_offset + index) : - true); + bool is_visible = true; + if (data_for_edge->backbuf_offset) { + uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; + is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); + } + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) && @@ -761,10 +764,12 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, { struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data; LassoSelectUserData *data = data_for_edge->data; - const bool is_visible = (data_for_edge->esel ? - BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, - data_for_edge->backbuf_offset + index) : - true); + bool is_visible = true; + if (data_for_edge->backbuf_offset) { + uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; + is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); + } + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcords, data->moves, @@ -831,13 +836,15 @@ static bool do_lasso_select_mesh(ViewContext *vc, if (wm_userdata->data == NULL) { editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); esel = wm_userdata->data; - esel->select_bitmap = DRW_select_buffer_bitmap_from_poly(mcords, moves, &rect); + esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( + vc->depsgraph, vc->ar, vc->v3d, mcords, moves, &rect, NULL); } } if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { - data.is_changed |= edbm_backbuf_check_and_select_verts(esel, vc->obedit, vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_verts( + esel, vc->depsgraph, vc->obedit, vc->em, sel_op); } else { mesh_foreachScreenVert( @@ -850,7 +857,7 @@ static bool do_lasso_select_mesh(ViewContext *vc, .data = &data, .esel = use_zbuf ? esel : NULL, .backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem( - vc->obedit->runtime.select_id, SCE_SELECT_EDGE) : + vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) : 0, }; @@ -866,7 +873,8 @@ static bool do_lasso_select_mesh(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { - data.is_changed |= edbm_backbuf_check_and_select_faces(esel, vc->obedit, vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_faces( + esel, vc->depsgraph, vc->obedit, vc->em, sel_op); } else { mesh_foreachScreenFace( @@ -1141,7 +1149,8 @@ static bool do_lasso_select_paintvert(ViewContext *vc, if (wm_userdata->data == NULL) { editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX); esel = wm_userdata->data; - esel->select_bitmap = DRW_select_buffer_bitmap_from_poly(mcords, moves, &rect); + esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( + vc->depsgraph, vc->ar, vc->v3d, mcords, moves, &rect, NULL); } } @@ -1199,7 +1208,8 @@ static bool do_lasso_select_paintface(ViewContext *vc, if (esel == NULL) { editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE); esel = wm_userdata->data; - esel->select_bitmap = DRW_select_buffer_bitmap_from_poly(mcords, moves, &rect); + esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( + vc->depsgraph, vc->ar, vc->v3d, mcords, moves, &rect, NULL); } if (esel->select_bitmap) { @@ -1271,9 +1281,15 @@ static bool view3d_lasso_select( } else if (ob && (ob->mode & OB_MODE_POSE)) { changed_multi |= do_lasso_select_pose(vc, mcords, moves, sel_op); + if (changed_multi) { + ED_outliner_select_sync_from_pose_bone_tag(C); + } } else { changed_multi |= do_lasso_select_objects(vc, mcords, moves, sel_op); + if (changed_multi) { + ED_outliner_select_sync_from_object_tag(C); + } } } else { /* Edit Mode */ @@ -1294,6 +1310,9 @@ static bool view3d_lasso_select( break; case OB_ARMATURE: changed = do_lasso_select_armature(vc, mcords, moves, sel_op); + if (changed) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } break; case OB_MBALL: changed = do_lasso_select_meta(vc, mcords, moves, sel_op); @@ -1479,6 +1498,9 @@ static int object_select_menu_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } else { @@ -2341,6 +2363,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op) if (!retval && deselect_all) { retval = ED_armature_edit_deselect_all_visible_multi(C); } + if (retval) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } } else if (obedit->type == OB_LATTICE) { retval = ED_lattice_select_pick(C, location, extend, deselect, toggle); @@ -2401,6 +2426,15 @@ static int view3d_select_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); } } + + if (retval) { + if (obact && obact->mode & OB_MODE_POSE) { + ED_outliner_select_sync_from_pose_bone_tag(C); + } + else { + ED_outliner_select_sync_from_object_tag(C); + } + } } /* Pass-through allows tweaks @@ -2554,7 +2588,8 @@ static bool do_paintvert_box_select(ViewContext *vc, if (wm_userdata->data == NULL) { editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX); esel = wm_userdata->data; - esel->select_bitmap = DRW_select_buffer_bitmap_from_rect(rect, NULL); + esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( + vc->depsgraph, vc->ar, vc->v3d, rect, NULL); } if (esel->select_bitmap != NULL) { changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op); @@ -2608,7 +2643,8 @@ static bool do_paintface_box_select(ViewContext *vc, if (wm_userdata->data == NULL) { editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE); esel = wm_userdata->data; - esel->select_bitmap = DRW_select_buffer_bitmap_from_rect(rect, NULL); + esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( + vc->depsgraph, vc->ar, vc->v3d, rect, NULL); } if (esel->select_bitmap != NULL) { changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op); @@ -2731,10 +2767,12 @@ static void do_mesh_box_select__doSelectEdge_pass0( { struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData; BoxSelectUserData *data = data_for_edge->data; - const bool is_visible = (data_for_edge->esel ? - BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, - data_for_edge->backbuf_offset + index) : - true); + bool is_visible = true; + if (data_for_edge->backbuf_offset) { + uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; + is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); + } + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); @@ -2750,10 +2788,12 @@ static void do_mesh_box_select__doSelectEdge_pass1( { struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData; BoxSelectUserData *data = data_for_edge->data; - const bool is_visible = (data_for_edge->esel ? - BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, - data_for_edge->backbuf_offset + index) : - true); + bool is_visible = true; + if (data_for_edge->backbuf_offset) { + uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; + is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); + } + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); @@ -2805,13 +2845,15 @@ static bool do_mesh_box_select(ViewContext *vc, if (wm_userdata->data == NULL) { editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); esel = wm_userdata->data; - esel->select_bitmap = DRW_select_buffer_bitmap_from_rect(rect, NULL); + esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( + vc->depsgraph, vc->ar, vc->v3d, rect, NULL); } } if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { - data.is_changed |= edbm_backbuf_check_and_select_verts(esel, vc->obedit, vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_verts( + esel, vc->depsgraph, vc->obedit, vc->em, sel_op); } else { mesh_foreachScreenVert( @@ -2824,7 +2866,7 @@ static bool do_mesh_box_select(ViewContext *vc, .data = &data, .esel = use_zbuf ? esel : NULL, .backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem( - vc->obedit->runtime.select_id, SCE_SELECT_EDGE) : + vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) : 0, }; @@ -2840,7 +2882,8 @@ static bool do_mesh_box_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { - data.is_changed |= edbm_backbuf_check_and_select_faces(esel, vc->obedit, vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_faces( + esel, vc->depsgraph, vc->obedit, vc->em, sel_op); } else { mesh_foreachScreenFace( @@ -3212,6 +3255,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) if (changed) { DEG_id_tag_update(&vc.obedit->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); + ED_outliner_select_sync_from_edit_bone_tag(C); } break; case OB_LATTICE: @@ -3246,9 +3290,15 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } else if (vc.obact && vc.obact->mode & OB_MODE_POSE) { changed_multi = do_pose_box_select(C, &vc, &rect, sel_op); + if (changed_multi) { + ED_outliner_select_sync_from_pose_bone_tag(C); + } } else { /* object mode with none active */ changed_multi = do_object_box_select(C, &vc, &rect, sel_op); + if (changed_multi) { + ED_outliner_select_sync_from_object_tag(C); + } } } @@ -3392,14 +3442,17 @@ static bool mesh_circle_select(ViewContext *vc, struct EditSelectBuf_Cache *esel = wm_userdata->data; if (use_zbuf) { - esel->select_bitmap = DRW_select_buffer_bitmap_from_circle(mval, (int)(rad + 1.0f), NULL); + if (esel->select_bitmap == NULL) { + esel->select_bitmap = DRW_select_buffer_bitmap_from_circle( + vc->depsgraph, vc->ar, vc->v3d, mval, (int)(rad + 1.0f), NULL); + } } if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { if (esel->select_bitmap != NULL) { changed |= edbm_backbuf_check_and_select_verts( - esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } } else { @@ -3411,7 +3464,7 @@ static bool mesh_circle_select(ViewContext *vc, if (use_zbuf) { if (esel->select_bitmap != NULL) { changed |= edbm_backbuf_check_and_select_edges( - esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } } else { @@ -3424,7 +3477,7 @@ static bool mesh_circle_select(ViewContext *vc, if (use_zbuf) { if (esel->select_bitmap != NULL) { changed |= edbm_backbuf_check_and_select_faces( - esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } } else { @@ -3432,13 +3485,6 @@ static bool mesh_circle_select(ViewContext *vc, } } - if (use_zbuf) { - if (esel->select_bitmap != NULL) { - MEM_freeN(esel->select_bitmap); - esel->select_bitmap = NULL; - } - } - changed |= data.is_changed; if (changed) { @@ -3469,7 +3515,8 @@ static bool paint_facesel_circle_select(ViewContext *vc, { struct EditSelectBuf_Cache *esel = wm_userdata->data; - esel->select_bitmap = DRW_select_buffer_bitmap_from_circle(mval, (int)(rad + 1.0f), NULL); + esel->select_bitmap = DRW_select_buffer_bitmap_from_circle( + vc->depsgraph, vc->ar, vc->v3d, mval, (int)(rad + 1.0f), NULL); if (esel->select_bitmap != NULL) { changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op); MEM_freeN(esel->select_bitmap); @@ -3509,8 +3556,8 @@ static bool paint_vertsel_circle_select(ViewContext *vc, bool changed = false; if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - changed |= paintvert_deselect_all_visible( - ob, SEL_DESELECT, false); /* flush selection at the end */ + /* Flush selection at the end. */ + changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false); } const bool select = (sel_op != SEL_OP_SUB); @@ -3523,7 +3570,8 @@ static bool paint_vertsel_circle_select(ViewContext *vc, if (use_zbuf) { struct EditSelectBuf_Cache *esel = wm_userdata->data; - esel->select_bitmap = DRW_select_buffer_bitmap_from_circle(mval, (int)(rad + 1.0f), NULL); + esel->select_bitmap = DRW_select_buffer_bitmap_from_circle( + vc->depsgraph, vc->ar, vc->v3d, mval, (int)(rad + 1.0f), NULL); if (esel->select_bitmap != NULL) { changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op); MEM_freeN(esel->select_bitmap); @@ -3874,7 +3922,8 @@ static bool mball_circle_select(ViewContext *vc, /** Callbacks for circle selection in Editmode */ -static bool obedit_circle_select(ViewContext *vc, +static bool obedit_circle_select(bContext *C, + ViewContext *vc, wmGenericUserData *wm_userdata, const eSelectOp sel_op, const int mval[2], @@ -3895,6 +3944,9 @@ static bool obedit_circle_select(ViewContext *vc, break; case OB_ARMATURE: changed = armature_circle_select(vc, sel_op, mval, rad); + if (changed) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } break; case OB_MBALL: changed = mball_circle_select(vc, sel_op, mval, rad); @@ -3983,7 +4035,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) obedit = vc.obedit; if (obedit) { - obedit_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius); + obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, (float)radius); } else if (BKE_paint_select_face_test(obact)) { paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius); @@ -3993,6 +4045,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) } else if (obact->mode & OB_MODE_POSE) { pose_circle_select(&vc, sel_op, mval, (float)radius); + ED_outliner_select_sync_from_pose_bone_tag(C); } else { BLI_assert(0); @@ -4013,6 +4066,8 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) if (object_circle_select(&vc, sel_op, mval, (float)radius)) { DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); + + ED_outliner_select_sync_from_object_tag(C); } } @@ -4020,6 +4075,13 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) if (wm_userdata == &wm_userdata_buf) { WM_generic_user_data_free(wm_userdata); } + else { + struct EditSelectBuf_Cache *esel = wm_userdata->data; + if (esel && esel->select_bitmap) { + MEM_freeN(esel->select_bitmap); + esel->select_bitmap = NULL; + } + } return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 7f930f1d876..1af94e3ade5 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -622,8 +622,8 @@ bool ED_view3d_camera_autokey( /** * Call after modifying a locked view. * - * \note Not every view edit currently auto-keys (numpad for eg), - * this is complicated because of smoothview. + * \note Not every view edit currently auto-keys (num-pad for eg), + * this is complicated because of smooth-view. */ bool ED_view3d_camera_lock_autokey(View3D *v3d, RegionView3D *rv3d, diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 22c1aafd7c4..91c05f5cac6 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -191,71 +191,89 @@ typedef struct WalkInfo { struct Depsgraph *depsgraph; Scene *scene; - wmTimer *timer; /* needed for redraws */ + /** Needed for for updating that isn't triggered by input. */ + wmTimer *timer; short state; bool redraw; - bool anim_playing; /* needed for autokeyframing */ + /** + * Needed for auto-keyframing, when animation isn't playing, only keyframe on confirmation. + * + * Currently we can't cancel this operator usefully while recording on animation playback + * (this would need to un-key all previous frames). + */ + bool anim_playing; + + /** Previous 2D mouse values. */ + int prev_mval[2]; + /** Center mouse values. */ + int center_mval[2]; - int prev_mval[2]; /* previous 2D mouse values */ - int center_mval[2]; /* center mouse values */ int moffset[2]; #ifdef WITH_INPUT_NDOF - wmNDOFMotionData *ndof; /* latest 3D mouse values */ + /** Latest 3D mouse values. */ + wmNDOFMotionData *ndof; #endif /* walk state state */ - float base_speed; /* the base speed without run/slow down modifications */ - float speed; /* the speed the view is moving per redraw */ - float grid; /* world scale 1.0 default */ + /** The base speed without run/slow down modifications. */ + float base_speed; + /** The speed the view is moving per redraw. */ + float speed; + /** World scale 1.0 default. */ + float grid; /* compare between last state */ - double time_lastdraw; /* time between draws */ + /** Time between draws. */ + double time_lastdraw; void *draw_handle_pixel; /* use for some lag */ - float dvec_prev[3]; /* old for some lag */ + /** Keep the previous value to smooth transitions (use lag). */ + float dvec_prev[3]; - /* walk/fly */ + /** Walk/free movement. */ eWalkMethod navigation_mode; /* teleport */ WalkTeleport teleport; - /* look speed factor - user preferences */ + /** Look speed factor - user preferences. */ float mouse_speed; - /* speed adjustments */ + /** Speed adjustments. */ bool is_fast; bool is_slow; - /* mouse reverse */ + /** Mouse reverse. */ bool is_reversed; #ifdef USE_TABLET_SUPPORT - /* check if we had a cursor event before */ + /** Check if we had a cursor event before. */ bool is_cursor_first; - /* tablet devices (we can't relocate the cursor) */ + /** Tablet devices (we can't relocate the cursor). */ bool is_cursor_absolute; #endif - /* gravity system */ + /** Gravity system. */ eWalkGravityState gravity_state; float gravity; - /* height to use in walk mode */ + /** Height to use in walk mode. */ float view_height; - /* counting system to allow movement to continue if a direction (WASD) key is still pressed */ + /** Counting system to allow movement to continue if a direction (WASD) key is still pressed. */ int active_directions; float speed_jump; - float jump_height; /* maximum jump height */ - float speed_factor; /* to use for fast/slow speeds */ + /** Maximum jump height. */ + float jump_height; + /** To use for fast/slow speeds. */ + float speed_factor; struct SnapObjectContext *snap_context; diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 98203a7e316..6478928a6b6 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -2009,19 +2009,18 @@ static void drawTransformView(const struct bContext *C, ARegion *ar, void *arg) * to warn that autokeying is enabled */ static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar) { - rcti rect; const char *printable = IFACE_("Auto Keying On"); float printable_size[2]; int xco, yco; - ED_region_visible_rect(ar, &rect); + const rcti *rect = ED_region_visible_rect(ar); const int font_id = BLF_default(); BLF_width_and_height( font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; - yco = (rect.ymax - U.widget_unit); + xco = (rect->xmax - U.widget_unit) - (int)printable_size[0]; + yco = (rect->ymax - U.widget_unit); /* warning text (to clarify meaning of overlays) * - original color was red to match the icon, but that clashes badly with a less nasty border @@ -6602,7 +6601,7 @@ static void slide_origdata_interp_data_vert(SlideOrigData *sod, * and we do not want to mess up other shape keys */ BM_loop_interp_from_face(bm, l, f_copy, false, false); - /* make sure face-attributes are correct (e.g. MTexPoly) */ + /* make sure face-attributes are correct (e.g. #MLoopUV, #MLoopCol) */ BM_elem_attrs_copy_ex(sod->bm_origfaces, bm, f_copy, l->f, 0x0, CD_MASK_NORMAL); /* weight the loop */ diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index f78f5c5e623..6a6d3b78d38 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -326,7 +326,7 @@ static void planeProjection(const TransInfo *t, const float in[3], float out[3]) sub_v3_v3v3(vec, out, in); factor = dot_v3v3(vec, norm); - if (fabsf(factor) <= 0.001f) { + if (factor == 0.0f) { return; /* prevent divide by zero */ } factor = dot_v3v3(vec, vec) / factor; diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index ef9d23d1db8..2fb9e2b9591 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -52,6 +52,7 @@ #include "BLI_string.h" #include "BLI_bitmap.h" #include "BLI_rect.h" +#include "BLI_kdtree.h" #include "BKE_action.h" #include "BKE_animsys.h" @@ -235,8 +236,9 @@ static void sort_trans_data_selected_first(TransInfo *t) } } -/* distance calculated from not-selected vertex to nearest selected vertex - * warning; this is loops inside loop, has minor N^2 issues, but by sorting list it is OK */ +/** + * Distance calculated from not-selected vertex to nearest selected vertex. + */ static void set_prop_dist(TransInfo *t, const bool with_dist) { int a; @@ -255,54 +257,124 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) } } + /* Count number of selected. */ + int td_table_len = 0; FOREACH_TRANS_DATA_CONTAINER (t, tc) { - TransData *tob = tc->data; - for (a = 0; a < tc->data_len; a++, tob++) { + TransData *td = tc->data; + for (a = 0; a < tc->data_len; a++, td++) { + if (td->flag & TD_SELECTED) { + td_table_len++; + } + else { + /* By definition transform-data has selected items in beginning. */ + break; + } + } + } - tob->rdist = 0.0f; // init, it was mallocced + /* Pointers to selected's #TransData. + * Used to find #TransData from the index returned by #BLI_kdtree_find_nearest. */ + TransData **td_table = MEM_mallocN(sizeof(*td_table) * td_table_len, __func__); - if ((tob->flag & TD_SELECTED) == 0) { - TransData *td; - int i; - float dist_sq, vec[3]; + /* Create and fill kd-tree of selected's positions - in global or proj_vec space. */ + KDTree_3d *td_tree = BLI_kdtree_3d_new(td_table_len); - tob->rdist = -1.0f; // signal for next loop + int td_table_index = 0; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (a = 0; a < tc->data_len; a++, td++) { + if (td->flag & TD_SELECTED) { + /* Initialize, it was mallocced. */ + float vec[3]; + td->rdist = 0.0f; + + if (use_island) { + if (tc->use_local_mat) { + mul_v3_m4v3(vec, tc->mat, td->iloc); + } + else { + mul_v3_m3v3(vec, td->mtx, td->iloc); + } + } + else { + if (tc->use_local_mat) { + mul_v3_m4v3(vec, tc->mat, td->center); + } + else { + mul_v3_m3v3(vec, td->mtx, td->center); + } + } - for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { - if (td->flag & TD_SELECTED) { - if (use_island) { - sub_v3_v3v3(vec, tob->iloc, td->iloc); - } - else { - sub_v3_v3v3(vec, tob->center, td->center); - } - mul_m3_v3(tob->mtx, vec); + if (proj_vec) { + float vec_p[3]; + project_v3_v3v3(vec_p, vec, proj_vec); + sub_v3_v3(vec, vec_p); + } - if (proj_vec) { - float vec_p[3]; - project_v3_v3v3(vec_p, vec, proj_vec); - sub_v3_v3(vec, vec_p); - } + BLI_kdtree_3d_insert(td_tree, td_table_index, vec); + td_table[td_table_index++] = td; + } + else { + /* By definition transform-data has selected items in beginning. */ + break; + } + } + } + BLI_assert(td_table_index == td_table_len); - dist_sq = len_squared_v3(vec); - if ((tob->rdist == -1.0f) || (dist_sq < SQUARE(tob->rdist))) { - tob->rdist = sqrtf(dist_sq); - if (use_island) { - copy_v3_v3(tob->center, td->center); - copy_m3_m3(tob->axismtx, td->axismtx); - } - } + BLI_kdtree_3d_balance(td_tree); + + /* For each non-selected vertex, find distance to the nearest selected vertex. */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (a = 0; a < tc->data_len; a++, td++) { + if ((td->flag & TD_SELECTED) == 0) { + float vec[3]; + + if (use_island) { + if (tc->use_local_mat) { + mul_v3_m4v3(vec, tc->mat, td->iloc); } else { - break; /* by definition transdata has selected items in beginning */ + mul_v3_m3v3(vec, td->mtx, td->iloc); } } + else { + if (tc->use_local_mat) { + mul_v3_m4v3(vec, tc->mat, td->center); + } + else { + mul_v3_m3v3(vec, td->mtx, td->center); + } + } + + if (proj_vec) { + float vec_p[3]; + project_v3_v3v3(vec_p, vec, proj_vec); + sub_v3_v3(vec, vec_p); + } + + KDTreeNearest_3d nearest; + const int td_index = BLI_kdtree_3d_find_nearest(td_tree, vec, &nearest); + + td->rdist = -1.0f; + if (td_index != -1) { + td->rdist = nearest.dist; + if (use_island) { + copy_v3_v3(td->center, td_table[td_index]->center); + copy_m3_m3(td->axismtx, td_table[td_index]->axismtx); + } + } + if (with_dist) { - tob->dist = tob->rdist; + td->dist = td->rdist; } } } } + + BLI_kdtree_3d_free(td_tree); + MEM_freeN(td_table); } /* ************************** CONVERSIONS ************************* */ diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c index 6ebed88878f..e771fe43bd8 100644 --- a/source/blender/editors/transform/transform_input.c +++ b/source/blender/editors/transform/transform_input.c @@ -299,7 +299,7 @@ static void calcSpringFactor(MouseInput *mi) void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode) { - /* incase we allocate a new value */ + /* In case we allocate a new value. */ void *mi_data_prev = mi->data; mi->use_virtual_mval = true; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index d45a0588003..dbcc6c1b04a 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -1150,12 +1150,19 @@ static void TargetSnapMedian(TransInfo *t) add_v3_v3(v, td->center); } + if (i == 0) { + /* Is this possible? */ + continue; + } + + mul_v3_fl(v, 1.0 / i); + if (tc->use_local_mat) { mul_m4_v3(tc->mat, v); } add_v3_v3(t->tsnap.snapTarget, v); - i_accum += i; + i_accum++; } mul_v3_fl(t->tsnap.snapTarget, 1.0 / i_accum); diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index ed8178d1908..f485b440692 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -163,13 +163,13 @@ void ED_image_draw_cursor(ARegion *ar, const float cursor[2]) static void uvedit_get_batches(Object *ob, SpaceImage *sima, - const ToolSettings *ts, + const Scene *scene, GPUBatch **faces, GPUBatch **edges, GPUBatch **verts, GPUBatch **facedots) { - int drawfaces = draw_uvs_face_check(ts); + int drawfaces = draw_uvs_face_check(scene->toolsettings); const bool draw_stretch = (sima->flag & SI_DRAW_STRETCH) != 0; const bool draw_faces = (sima->flag & SI_NO_DRAWFACES) == 0; @@ -197,7 +197,7 @@ static void uvedit_get_batches(Object *ob, *faces = NULL; } - DRW_mesh_batch_cache_create_requested(ob, ob->data, ts, false, false); + DRW_mesh_batch_cache_create_requested(ob, ob->data, scene, false, false); } static void draw_uvs_shadow(SpaceImage *UNUSED(sima), @@ -212,7 +212,7 @@ static void draw_uvs_shadow(SpaceImage *UNUSED(sima), DRW_mesh_batch_cache_validate(me); GPUBatch *edges = DRW_mesh_batch_cache_get_uv_edges(me); - DRW_mesh_batch_cache_create_requested(eval_ob, me, scene->toolsettings, false, false); + DRW_mesh_batch_cache_create_requested(eval_ob, me, scene, false, false); if (edges) { GPU_batch_program_set_builtin(edges, GPU_SHADER_2D_UV_UNIFORM_COLOR); @@ -235,7 +235,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph) DRW_mesh_batch_cache_validate(me); GPUBatch *geom = DRW_mesh_batch_cache_get_uv_edges(me); - DRW_mesh_batch_cache_create_requested(eval_ob, me, scene->toolsettings, false, false); + DRW_mesh_batch_cache_create_requested(eval_ob, me, scene, false, false); GPU_batch_program_set_builtin(geom, GPU_SHADER_2D_UV_UNIFORM_COLOR); GPU_batch_uniform_4fv(geom, "color", col); @@ -300,7 +300,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph * } } - uvedit_get_batches(eval_ob, sima, ts, &faces, &edges, &verts, &facedots); + uvedit_get_batches(eval_ob, sima, scene, &faces, &edges, &verts, &facedots); bool interpedges; bool draw_stretch = (sima->flag & SI_DRAW_STRETCH) != 0; @@ -367,33 +367,33 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph * break; } case SI_UVDT_BLACK: - case SI_UVDT_WHITE: { - GPU_line_width(1.0f); - GPU_batch_program_set_builtin(edges, GPU_SHADER_2D_UNIFORM_COLOR); - if (sima->dt_uv == SI_UVDT_WHITE) { - GPU_batch_uniform_4f(edges, "color", 1.0f, 1.0f, 1.0f, 1.0f); - } - else { - GPU_batch_uniform_4f(edges, "color", 0.0f, 0.0f, 0.0f, 1.0f); - } - GPU_batch_draw(edges); - break; - } + case SI_UVDT_WHITE: case SI_UVDT_OUTLINE: { /* We could modify the vbo's data filling * instead of modifying the provoking vert. */ glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); - UI_GetThemeColor4fv(TH_WIRE_EDIT, col1); UI_GetThemeColor4fv(TH_EDGE_SELECT, col2); GPU_batch_program_set_builtin( edges, (interpedges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH : GPU_SHADER_2D_UV_EDGES); - /* Black Outline. */ - GPU_line_width(3.0f); - GPU_batch_uniform_4f(edges, "edgeColor", 0.0f, 0.0f, 0.0f, 1.0f); - GPU_batch_uniform_4f(edges, "selectColor", 0.0f, 0.0f, 0.0f, 1.0f); - GPU_batch_draw(edges); + + if (sima->dt_uv == SI_UVDT_OUTLINE) { + /* Black Outline. */ + GPU_line_width(3.0f); + GPU_batch_uniform_4f(edges, "edgeColor", 0.0f, 0.0f, 0.0f, 1.0f); + GPU_batch_uniform_4f(edges, "selectColor", 0.0f, 0.0f, 0.0f, 1.0f); + GPU_batch_draw(edges); + + UI_GetThemeColor4fv(TH_WIRE_EDIT, col1); + } + else if (sima->dt_uv == SI_UVDT_WHITE) { + copy_v4_fl4(col1, 1.0f, 1.0f, 1.0f, 1.0f); + } + else { + copy_v4_fl4(col1, 0.0f, 0.0f, 0.0f, 1.0f); + } + /* Inner Line. Use depth test to insure selection is drawn on top. */ GPU_depth_test(true); GPU_line_width(1.0f); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 883671949c8..dcf1d04ffb3 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -4519,7 +4519,9 @@ static int uv_select_overlap(bContext *C, const bool extend) const float(*t1)[2] = o_a->tri; const float(*t2)[2] = o_b->tri; float vi[2]; - bool result = + bool result = ( + /* Don't use 'isect_tri_tri_v2' here + * because it's important to ignore overlap at end-points. */ isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[0], t2[1], endpoint_bias, vi) == 1 || isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[1], t2[2], endpoint_bias, vi) == 1 || isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[2], t2[0], endpoint_bias, vi) == 1 || @@ -4529,7 +4531,7 @@ static int uv_select_overlap(bContext *C, const bool extend) isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[0], t2[1], endpoint_bias, vi) == 1 || isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[1], t2[2], endpoint_bias, vi) == 1 || isect_point_tri_v2(t1[0], t2[0], t2[1], t2[2]) != 0 || - isect_point_tri_v2(t2[0], t1[0], t1[1], t1[2]) != 0; + isect_point_tri_v2(t2[0], t1[0], t1[1], t1[2]) != 0); if (result) { uvedit_face_select_enable(scene, em_a, face_a, false, cd_loop_uv_offset_a); diff --git a/source/blender/freestyle/intern/scene_graph/FrsMaterial.h b/source/blender/freestyle/intern/scene_graph/FrsMaterial.h index c5a87c3baaf..80cd783f164 100644 --- a/source/blender/freestyle/intern/scene_graph/FrsMaterial.h +++ b/source/blender/freestyle/intern/scene_graph/FrsMaterial.h @@ -36,14 +36,14 @@ class FrsMaterial { /*! Default constructor */ inline FrsMaterial(); - /*! Builds a Material from its line, diffuse, ambiant, specular, emissive + /*! Builds a Material from its line, diffuse, ambient, specular, emissive * colors, a shininess coefficient and line color priority. * \param iLine: * A 4 element float-array containing the line color. * \param iDiffuse: * A 4 element float-array containing the diffuse color. * \param iAmbiant: - * A 4 element float-array containing the ambiant color. + * A 4 element float-array containing the ambient color. * \param iSpecular: * A 4 element float-array containing the specular color. * \param iEmission: @@ -159,31 +159,31 @@ class FrsMaterial { return Specular[3]; } - /*! Returns the ambiant color as a 4 float array */ + /*! Returns the ambient color as a 4 float array */ inline const float *ambient() const { return Ambient; } - /*! Returns the red component of the ambiant color */ + /*! Returns the red component of the ambient color */ inline const float ambientR() const { return Ambient[0]; } - /*! Returns the green component of the ambiant color */ + /*! Returns the green component of the ambient color */ inline const float ambientG() const { return Ambient[1]; } - /*! Returns the blue component of the ambiant color */ + /*! Returns the blue component of the ambient color */ inline const float ambientB() const { return Ambient[2]; } - /*! Returns the alpha component of the ambiant color */ + /*! Returns the alpha component of the ambient color */ inline const float ambientA() const { return Ambient[3]; @@ -267,7 +267,7 @@ class FrsMaterial { */ inline void setSpecular(const float r, const float g, const float b, const float a); - /*! Sets the ambiant color. + /*! Sets the ambient color. * \param r: * Red component * \param g: diff --git a/source/blender/freestyle/intern/scene_graph/IndexedFaceSet.h b/source/blender/freestyle/intern/scene_graph/IndexedFaceSet.h index d70794d9b78..f8dd25913c1 100644 --- a/source/blender/freestyle/intern/scene_graph/IndexedFaceSet.h +++ b/source/blender/freestyle/intern/scene_graph/IndexedFaceSet.h @@ -19,7 +19,7 @@ /** \file * \ingroup freestyle - * \brief A Set of indexed faces to represent a surfacic object + * \brief A Set of indexed faces to represent a surface object */ #include <memory.h> diff --git a/source/blender/freestyle/intern/scene_graph/NodeTransform.h b/source/blender/freestyle/intern/scene_graph/NodeTransform.h index ddae714f9d4..1118417657f 100644 --- a/source/blender/freestyle/intern/scene_graph/NodeTransform.h +++ b/source/blender/freestyle/intern/scene_graph/NodeTransform.h @@ -48,7 +48,7 @@ class NodeTransform : public NodeGroup { /*! multiplies the current matrix by the x, y, z translation matrix. */ void Translate(real x, real y, real z); - /*! multiplis the current matrix by a rotation matrix + /*! multiplies the current matrix by a rotation matrix * iAngle * The rotation angle * x, y, z @@ -56,13 +56,13 @@ class NodeTransform : public NodeGroup { */ void Rotate(real iAngle, real x, real y, real z); - /*! multiplys the current matrix by a scaling matrix. + /*! multiplies the current matrix by a scaling matrix. * x, y, z * The scaling coefficients with respect to the x,y,z axis */ void Scale(real x, real y, real z); - /*! Multiplys the current matrix by iMatrix */ + /*! Multiplies the current matrix by iMatrix */ void MultiplyMatrix(const Matrix44r &iMatrix); /*! Sets the current matrix to iMatrix */ diff --git a/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h index 4eb4b2616fc..d14a9836b95 100644 --- a/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h @@ -250,7 +250,7 @@ class GetSteerableViewMapDensityF1D : public UnaryFunction1D<double> { // GetViewMapGradientNormF1D /*! Returns the density of the viewmap for a given Interface1D. The density of each FEdge is - * evaluated in the proper steerable ViewMap depending on its oorientation. + * evaluated in the proper steerable ViewMap depending on its orientation. */ class GetViewMapGradientNormF1D : public UnaryFunction1D<double> { private: diff --git a/source/blender/freestyle/intern/stroke/Curve.h b/source/blender/freestyle/intern/stroke/Curve.h index 7eadfa830ce..8a233eef4ab 100644 --- a/source/blender/freestyle/intern/stroke/Curve.h +++ b/source/blender/freestyle/intern/stroke/Curve.h @@ -56,7 +56,7 @@ using namespace Geometry; /*! Class to represent a point of a curve. * A CurvePoint can be any point of a 1D curve (it doesn't have to be a vertex of the curve). * Any Interface1D is built upon ViewEdges, themselves built upon FEdges. Therefore, a curve is - * basically a polyline made of a list SVertex. Thus, a CurvePoint is built by lineraly + * basically a polyline made of a list SVertex. Thus, a CurvePoint is built by linearly * interpolating two SVertex. CurvePoint can be used as virtual points while querying 0D * information along a curve at a given resolution. */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c index a27fb27d518..2168e7f07ec 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -46,6 +46,7 @@ static void initData(GpencilModifierData *md) gpmd->step = 1; gpmd->factor = 0.0f; gpmd->length = 0.1f; + gpmd->distance = 0.1f; gpmd->layername[0] = '\0'; } @@ -94,7 +95,7 @@ static void deformStroke(GpencilModifierData *md, break; } case GP_SIMPLIFY_MERGE: { - BKE_gpencil_merge_distance_stroke(gpf, gps, mmd->length, true); + BKE_gpencil_merge_distance_stroke(gpf, gps, mmd->distance, true); break; } default: diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index 357e36a06b2..8ab72716f4d 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -102,16 +102,41 @@ static void deformStroke(GpencilModifierData *md, return; } - /* if normalize, set stroke thickness */ + /* Check to see if we normalize the whole stroke or only certain points along it. */ + bool gps_has_affected_points = false; + bool gps_has_unaffected_points = false; + if (mmd->flag & GP_THICK_NORMALIZE) { + for (int i = 0; i < gps->totpoints; i++) { + MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + const float weight = get_modifier_point_weight( + dvert, (mmd->flag & GP_THICK_INVERT_VGROUP) != 0, def_nr); + if (weight < 0.0f) { + gps_has_unaffected_points = true; + } + else { + gps_has_affected_points = true; + } + + /* If both checks are true, we have what we need so we can stop looking. */ + if (gps_has_affected_points && gps_has_unaffected_points) { + break; + } + } + } + + /* If we are normalizing and all points of the stroke are affected, it's safe to reset thickness + */ + if (mmd->flag & GP_THICK_NORMALIZE && gps_has_affected_points && !gps_has_unaffected_points) { gps->thickness = mmd->thickness; } + /* Without this check, modifier alters the thickness of strokes which have no points in scope */ for (int i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; float curvef = 1.0f; - /* verify vertex group */ + /* Verify point is part of vertex group. */ const float weight = get_modifier_point_weight( dvert, (mmd->flag & GP_THICK_INVERT_VGROUP) != 0, def_nr); if (weight < 0.0f) { @@ -119,11 +144,21 @@ static void deformStroke(GpencilModifierData *md, } if (mmd->flag & GP_THICK_NORMALIZE) { - pt->pressure = 1.0f; + if (gps_has_unaffected_points) { + /* Clamp value for very weird situations when stroke thickness can be zero. */ + CLAMP_MIN(gps->thickness, 0.001f); + /* Calculate pressure value to match the width of strokes with reset thickness and 1.0 + * pressure. */ + pt->pressure = (float)mmd->thickness / (float)gps->thickness; + } + else { + /* Reset point pressure values so only stroke thickness counts. */ + pt->pressure = 1.0f; + } } else { if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) { - /* normalize value to evaluate curve */ + /* Normalize value to evaluate curve. */ float value = (float)i / (gps->totpoints - 1); curvef = BKE_curvemapping_evaluateF(mmd->curve_thickness, 0, value); } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index c620644a5f8..fb7d3c1ace8 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -116,6 +116,7 @@ set(SRC intern/gpu_batch_private.h intern/gpu_codegen.h intern/gpu_context_private.h + intern/gpu_matrix_private.h intern/gpu_primitive_private.h intern/gpu_private.h intern/gpu_select_private.h diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 5b0cab220c0..175033f70d9 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -40,7 +40,7 @@ typedef enum { GPU_BATCH_READY_TO_DRAW, } GPUBatchPhase; -#define GPU_BATCH_VBO_MAX_LEN 4 +#define GPU_BATCH_VBO_MAX_LEN 6 #define GPU_BATCH_VAO_STATIC_LEN 3 #define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16 @@ -105,8 +105,9 @@ void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src); #define GPU_batch_create(prim, verts, elem) GPU_batch_create_ex(prim, verts, elem, 0) #define GPU_batch_init(batch, prim, verts, elem) GPU_batch_init_ex(batch, prim, verts, elem, 0) -void GPU_batch_clear( - GPUBatch *); /* Same as discard but does not free. (does not clal free callback) */ +/* Same as discard but does not free. (does not call free callback). */ +void GPU_batch_clear(GPUBatch *); + void GPU_batch_discard(GPUBatch *); /* verts & elem are not discarded */ void GPU_batch_vao_cache_clear(GPUBatch *); @@ -114,6 +115,7 @@ void GPU_batch_vao_cache_clear(GPUBatch *); void GPU_batch_callback_free_set(GPUBatch *, void (*callback)(GPUBatch *, void *), void *); void GPU_batch_instbuf_set(GPUBatch *, GPUVertBuf *, bool own_vbo); /* Instancing */ +void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo); int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo); diff --git a/source/blender/gpu/GPU_element.h b/source/blender/gpu/GPU_element.h index 4ac89d2658b..75caf4cbd6a 100644 --- a/source/blender/gpu/GPU_element.h +++ b/source/blender/gpu/GPU_element.h @@ -36,14 +36,19 @@ typedef enum { } GPUIndexBufType; typedef struct GPUIndexBuf { + uint index_start; uint index_len; + bool is_subrange; #if GPU_TRACK_INDEX_RANGE GPUIndexBufType index_type; uint32_t gl_index_type; uint base_index; #endif uint32_t ibo_id; /* 0 indicates not yet sent to VRAM */ - void *data; /* non-NULL indicates not yet sent to VRAM */ + union { + void *data; /* non-NULL indicates not yet sent to VRAM */ + struct GPUIndexBuf *src; /* if is_subrange is true, this is the source buffer. */ + }; } GPUIndexBuf; void GPU_indexbuf_use(GPUIndexBuf *); @@ -71,9 +76,21 @@ void GPU_indexbuf_add_line_verts(GPUIndexBufBuilder *, uint v1, uint v2); void GPU_indexbuf_add_tri_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3); void GPU_indexbuf_add_line_adj_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3, uint v4); +void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1); +void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2); +void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3); + +/* Skip primitive rendering at the given index. */ +void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem); +void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem); +void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem); + GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *); void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, GPUIndexBuf *); +/* Create a subrange of an existing indexbuffer. */ +GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *ibo, uint start, uint length); + void GPU_indexbuf_discard(GPUIndexBuf *); int GPU_indexbuf_primitive_len(GPUPrimType prim_type); diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index 61622c40ff0..a424f3180de 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -52,12 +52,12 @@ void GPU_matrix_translate_3f(float x, float y, float z); void GPU_matrix_translate_3fv(const float vec[3]); void GPU_matrix_scale_3f(float x, float y, float z); void GPU_matrix_scale_3fv(const float vec[3]); -void GPU_matrix_rotate_3f(float deg, - float x, - float y, - float z); /* axis of rotation should be a unit vector */ -void GPU_matrix_rotate_3fv(float deg, - const float axis[3]); /* axis of rotation should be a unit vector */ + +/* Axis of rotation should be a unit vector. */ +void GPU_matrix_rotate_3f(float deg, float x, float y, float z); +/* Axis of rotation should be a unit vector. */ +void GPU_matrix_rotate_3fv(float deg, const float axis[3]); + void GPU_matrix_rotate_axis(float deg, char axis); /* TODO: enum for axis? */ void GPU_matrix_look_at(float eyeX, diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 124f1f1ff8a..f4a94c7759a 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -396,7 +396,9 @@ void GPU_shader_free_builtin_shaders(void); /* Vertex attributes for shaders */ -#define GPU_MAX_ATTR 32 +/* Hardware limit is 16. Position attribute is always needed so we reduce to 15. + * This makes sure the GPUVertexFormat name buffer does not overflow. */ +#define GPU_MAX_ATTR 15 typedef struct GPUVertAttrLayers { struct { diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index 3e178e193dc..2d728422c42 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -87,9 +87,10 @@ void GPU_vertbuf_data_len_set(GPUVertBuf *, uint v_len); * should not be a problem. */ void GPU_vertbuf_attr_set(GPUVertBuf *, uint a_idx, uint v_idx, const void *data); -void GPU_vertbuf_attr_fill(GPUVertBuf *, - uint a_idx, - const void *data); /* tightly packed, non interleaved input data */ + +/* Tightly packed, non interleaved input data. */ +void GPU_vertbuf_attr_fill(GPUVertBuf *, uint a_idx, const void *data); + void GPU_vertbuf_attr_fill_stride(GPUVertBuf *, uint a_idx, uint stride, const void *data); /* For low level access only */ diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index 68608a98a79..8c22e3e1104 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -31,9 +31,11 @@ #include "BLI_assert.h" #define GPU_VERT_ATTR_MAX_LEN 16 -#define GPU_VERT_ATTR_MAX_NAMES 5 -#define GPU_VERT_ATTR_NAME_AVERAGE_LEN 11 -#define GPU_VERT_ATTR_NAMES_BUF_LEN ((GPU_VERT_ATTR_NAME_AVERAGE_LEN + 1) * GPU_VERT_ATTR_MAX_LEN) +#define GPU_VERT_ATTR_MAX_NAMES 6 +#define GPU_VERT_ATTR_NAMES_BUF_LEN 256 +#define GPU_VERT_FORMAT_MAX_NAMES 63 /* More than enough, actual max is ~30. */ +/* Computed as GPU_VERT_ATTR_NAMES_BUF_LEN / 30 (actual max format name). */ +#define GPU_MAX_SAFE_ATTRIB_NAME 12 typedef enum { GPU_COMP_I8, @@ -80,14 +82,16 @@ BLI_STATIC_ASSERT(GPU_VERT_ATTR_NAMES_BUF_LEN <= 256, typedef struct GPUVertFormat { /** 0 to 16 (GPU_VERT_ATTR_MAX_LEN). */ uint attr_len : 5; - /** Total count of active vertex attribute. */ - uint name_len : 5; + /** Total count of active vertex attribute names. (max GPU_VERT_FORMAT_MAX_NAMES) */ + uint name_len : 6; /** Stride in bytes, 1 to 1024. */ uint stride : 11; /** Has the format been packed. */ uint packed : 1; /** Current offset in names[]. */ uint name_offset : 8; + /** Store each attrib in one contiguous buffer region. */ + uint deinterleaved : 1; GPUVertAttr attrs[GPU_VERT_ATTR_MAX_LEN]; char names[GPU_VERT_ATTR_NAMES_BUF_LEN]; @@ -104,6 +108,8 @@ uint GPU_vertformat_attr_add( GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode); void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias); +void GPU_vertformat_deinterleave(GPUVertFormat *format); + int GPU_vertformat_attr_id_get(const GPUVertFormat *, const char *name); BLI_INLINE const char *GPU_vertformat_attr_name_get(const GPUVertFormat *format, @@ -113,6 +119,8 @@ BLI_INLINE const char *GPU_vertformat_attr_name_get(const GPUVertFormat *format, return format->names + attr->names[n_idx]; } +void GPU_vertformat_safe_attrib_name(const char *attrib_name, char *r_safe_name, uint max_len); + /* format conversion */ typedef struct GPUPackedNormal { @@ -122,7 +130,59 @@ typedef struct GPUPackedNormal { int w : 2; /* 0 by default, can manually set to { -2, -1, 0, 1 } */ } GPUPackedNormal; -GPUPackedNormal GPU_normal_convert_i10_v3(const float data[3]); -GPUPackedNormal GPU_normal_convert_i10_s3(const short data[3]); +/* OpenGL ES packs in a different order as desktop GL but component conversion is the same. + * Of the code here, only struct GPUPackedNormal needs to change. */ + +#define SIGNED_INT_10_MAX 511 +#define SIGNED_INT_10_MIN -512 + +BLI_INLINE int clampi(int x, int min_allowed, int max_allowed) +{ +#if TRUST_NO_ONE + assert(min_allowed <= max_allowed); +#endif + if (x < min_allowed) { + return min_allowed; + } + else if (x > max_allowed) { + return max_allowed; + } + else { + return x; + } +} + +BLI_INLINE int gpu_convert_normalized_f32_to_i10(float x) +{ + int qx = x * 511.0f; + return clampi(qx, SIGNED_INT_10_MIN, SIGNED_INT_10_MAX); +} + +BLI_INLINE int gpu_convert_i16_to_i10(short x) +{ + /* 16-bit signed --> 10-bit signed */ + /* TODO: round? */ + return x >> 6; +} + +BLI_INLINE GPUPackedNormal GPU_normal_convert_i10_v3(const float data[3]) +{ + GPUPackedNormal n = { + gpu_convert_normalized_f32_to_i10(data[0]), + gpu_convert_normalized_f32_to_i10(data[1]), + gpu_convert_normalized_f32_to_i10(data[2]), + }; + return n; +} + +BLI_INLINE GPUPackedNormal GPU_normal_convert_i10_s3(const short data[3]) +{ + GPUPackedNormal n = { + gpu_convert_i16_to_i10(data[0]), + gpu_convert_i16_to_i10(data[1]), + gpu_convert_i16_to_i10(data[2]), + }; + return n; +} #endif /* __GPU_VERTEX_FORMAT_H__ */ diff --git a/source/blender/gpu/intern/gpu_batch.c b/source/blender/gpu/intern/gpu_batch.c index 11b487f7be4..583551e3e58 100644 --- a/source/blender/gpu/intern/gpu_batch.c +++ b/source/blender/gpu/intern/gpu_batch.c @@ -182,6 +182,25 @@ void GPU_batch_instbuf_set(GPUBatch *batch, GPUVertBuf *inst, bool own_vbo) } } +void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo) +{ + BLI_assert(elem != NULL); + /* redo the bindings */ + GPU_batch_vao_cache_clear(batch); + + if (batch->elem != NULL && (batch->owns_flag & GPU_BATCH_OWNS_INDEX)) { + GPU_indexbuf_discard(batch->elem); + } + batch->elem = elem; + + if (own_ibo) { + batch->owns_flag |= GPU_BATCH_OWNS_INDEX; + } + else { + batch->owns_flag &= ~GPU_BATCH_OWNS_INDEX; + } +} + /* Returns the index of verts in the batch. */ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo) { @@ -362,13 +381,23 @@ static void create_bindings(GPUVertBuf *verts, const GPUVertFormat *format = &verts->format; const uint attr_len = format->attr_len; - const uint stride = format->stride; + uint stride = format->stride; + uint offset = 0; GPU_vertbuf_use(verts); for (uint a_idx = 0; a_idx < attr_len; ++a_idx) { const GPUVertAttr *a = &format->attrs[a_idx]; - const GLvoid *pointer = (const GLubyte *)0 + a->offset + v_first * stride; + + if (format->deinterleaved) { + offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * verts->vertex_len; + stride = a->sz; + } + else { + offset = a->offset; + } + + const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride; for (uint n_idx = 0; n_idx < a->name_len; ++n_idx) { const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); @@ -419,8 +448,11 @@ static void create_bindings(GPUVertBuf *verts, static void batch_update_program_bindings(GPUBatch *batch, uint v_first) { - for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN && batch->verts[v] != NULL; ++v) { - create_bindings(batch->verts[v], batch->interface, (batch->inst) ? 0 : v_first, false); + /* Reverse order so first vbos have more prevalence (in term of attrib override). */ + for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; --v) { + if (batch->verts[v] != NULL) { + create_bindings(batch->verts[v], batch->interface, (batch->inst) ? 0 : v_first, false); + } } if (batch->inst) { create_bindings(batch->inst, batch->interface, v_first, true); @@ -550,10 +582,10 @@ static void *elem_offset(const GPUIndexBuf *el, int v_first) { #if GPU_TRACK_INDEX_RANGE if (el->index_type == GPU_INDEX_U16) { - return (GLushort *)0 + v_first; + return (GLushort *)0 + v_first + el->index_start; } #endif - return (GLuint *)0 + v_first; + return (GLuint *)0 + v_first + el->index_start; } /* Use when drawing with GPU_batch_draw_advanced */ diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 51b73c93c86..0e15fdd000b 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -46,6 +46,7 @@ #include "GPU_shader.h" #include "GPU_texture.h" #include "GPU_uniformbuffer.h" +#include "GPU_vertex_format.h" #include "BLI_sys_types.h" /* for intptr_t support */ @@ -929,12 +930,15 @@ static char *code_generate_fragment(GPUMaterial *material, /* XXX This cannot go into gpu_shader_material.glsl because main() * would be parsed and generate error */ /* Old glsl mode compat. */ + /* TODO(fclem) This is only used by world shader now. get rid of it? */ BLI_dynstr_append(ds, "#ifndef NODETREE_EXEC\n"); BLI_dynstr_append(ds, "out vec4 fragColor;\n"); BLI_dynstr_append(ds, "void main()\n"); BLI_dynstr_append(ds, "{\n"); BLI_dynstr_append(ds, "\tClosure cl = nodetree_exec();\n"); - BLI_dynstr_append(ds, "\tfragColor = vec4(cl.radiance, cl.opacity);\n"); + BLI_dynstr_append(ds, + "\tfragColor = vec4(cl.radiance, " + "saturate(1.0 - avg(cl.transmittance)));\n"); BLI_dynstr_append(ds, "}\n"); BLI_dynstr_append(ds, "#endif\n\n"); @@ -1008,19 +1012,24 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u ds, "#define att%d %s\n", input->attr_id, attr_prefix_get(input->attr_type)); } else { - uint hash = BLI_ghashutil_strhash_p(input->attr_name); + char attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; + GPU_vertformat_safe_attrib_name( + input->attr_name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); BLI_dynstr_appendf(ds, - "DEFINE_ATTR(%s, %s%u);\n", + "DEFINE_ATTR(%s, %s%s);\n", GPU_DATATYPE_STR[input->type], attr_prefix_get(input->attr_type), - hash); - BLI_dynstr_appendf( - ds, "#define att%d %s%u\n", input->attr_id, attr_prefix_get(input->attr_type), hash); + attr_safe_name); + BLI_dynstr_appendf(ds, + "#define att%d %s%s\n", + input->attr_id, + attr_prefix_get(input->attr_type), + attr_safe_name); /* Auto attribute can be vertex color byte buffer. * We need to know and convert them to linear space in VS. */ if (input->attr_type == CD_AUTO_FROM_NAME) { - BLI_dynstr_appendf(ds, "uniform bool ba%u;\n", hash); - BLI_dynstr_appendf(ds, "#define att%d_is_srgb ba%u\n", input->attr_id, hash); + BLI_dynstr_appendf(ds, "uniform bool ba%s;\n", attr_safe_name); + BLI_dynstr_appendf(ds, "#define att%d_is_srgb ba%s\n", input->attr_id, attr_safe_name); } } BLI_dynstr_appendf(ds, diff --git a/source/blender/gpu/intern/gpu_context.cpp b/source/blender/gpu/intern/gpu_context.cpp index 97f9e52f944..17b86e3eec8 100644 --- a/source/blender/gpu/intern/gpu_context.cpp +++ b/source/blender/gpu/intern/gpu_context.cpp @@ -36,6 +36,7 @@ #include "gpu_batch_private.h" #include "gpu_context_private.h" +#include "gpu_matrix_private.h" #include <vector> #include <string.h> @@ -71,6 +72,7 @@ struct GPUContext { std::unordered_set<GPUFrameBuffer *> framebuffers; /* Framebuffers that have FBO from this context */ #endif + struct GPUMatrixState *matrix_state; std::vector<GLuint> orphaned_vertarray_ids; std::vector<GLuint> orphaned_framebuffer_ids; std::mutex orphans_mutex; /* todo: try spinlock instead */ @@ -101,9 +103,11 @@ static void orphans_add(GPUContext *ctx, std::vector<GLuint> *orphan_list, GLuin static void orphans_clear(GPUContext *ctx) { - BLI_assert(ctx); /* need at least an active context */ - BLI_assert(pthread_equal(pthread_self(), - ctx->thread)); /* context has been activated by another thread! */ + /* need at least an active context */ + BLI_assert(ctx); + + /* context has been activated by another thread! */ + BLI_assert(pthread_equal(pthread_self(), ctx->thread)); ctx->orphans_mutex.lock(); if (!ctx->orphaned_vertarray_ids.empty()) { @@ -139,6 +143,7 @@ GPUContext *GPU_context_create(GLuint default_framebuffer) GPUContext *ctx = new GPUContext; glGenVertexArrays(1, &ctx->default_vao); ctx->default_framebuffer = default_framebuffer; + ctx->matrix_state = GPU_matrix_state_create(); GPU_context_active_set(ctx); return ctx; } @@ -159,6 +164,7 @@ void GPU_context_discard(GPUContext *ctx) /* this removes the array entry */ GPU_batch_vao_cache_clear(*ctx->batches.begin()); } + GPU_matrix_state_discard(ctx->matrix_state); glDeleteVertexArrays(1, &ctx->default_vao); delete ctx; active_ctx = NULL; @@ -333,3 +339,9 @@ GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx) { return ctx->current_fbo; } + +struct GPUMatrixState *gpu_context_active_matrix_state_get() +{ + BLI_assert(active_ctx); + return active_ctx->matrix_state; +} diff --git a/source/blender/gpu/intern/gpu_context_private.h b/source/blender/gpu/intern/gpu_context_private.h index 6825b67d2c8..c9379e5433f 100644 --- a/source/blender/gpu/intern/gpu_context_private.h +++ b/source/blender/gpu/intern/gpu_context_private.h @@ -59,6 +59,8 @@ void gpu_context_remove_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb); void gpu_context_active_framebuffer_set(GPUContext *ctx, struct GPUFrameBuffer *fb); struct GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx); +struct GPUMatrixState *gpu_context_active_matrix_state_get(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_element.c b/source/blender/gpu/intern/gpu_element.c index 50e7df96503..6c9331b4903 100644 --- a/source/blender/gpu/intern/gpu_element.c +++ b/source/blender/gpu/intern/gpu_element.c @@ -162,6 +162,100 @@ void GPU_indexbuf_add_line_adj_verts( GPU_indexbuf_add_generic_vert(builder, v4); } +void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1) +{ + BLI_assert(builder->prim_type == GPU_PRIM_POINTS); + BLI_assert(elem < builder->max_index_len); + builder->data[elem++] = v1; + if (builder->index_len < elem) { + builder->index_len = elem; + } +} + +void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2) +{ + BLI_assert(builder->prim_type == GPU_PRIM_LINES); + BLI_assert(v1 != v2); + BLI_assert(v1 <= builder->max_allowed_index); + BLI_assert(v2 <= builder->max_allowed_index); + BLI_assert((elem + 1) * 2 <= builder->max_index_len); + uint idx = elem * 2; + builder->data[idx++] = v1; + builder->data[idx++] = v2; + if (builder->index_len < idx) { + builder->index_len = idx; + } +} + +void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3) +{ + BLI_assert(builder->prim_type == GPU_PRIM_TRIS); + BLI_assert(v1 != v2 && v2 != v3 && v3 != v1); + BLI_assert(v1 <= builder->max_allowed_index); + BLI_assert(v2 <= builder->max_allowed_index); + BLI_assert(v3 <= builder->max_allowed_index); + BLI_assert((elem + 1) * 3 <= builder->max_index_len); + uint idx = elem * 3; + builder->data[idx++] = v1; + builder->data[idx++] = v2; + builder->data[idx++] = v3; + if (builder->index_len < idx) { + builder->index_len = idx; + } +} + +void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem) +{ + BLI_assert(builder->prim_type == GPU_PRIM_POINTS); + BLI_assert(elem < builder->max_index_len); + builder->data[elem++] = RESTART_INDEX; + if (builder->index_len < elem) { + builder->index_len = elem; + } +} + +void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem) +{ + BLI_assert(builder->prim_type == GPU_PRIM_LINES); + BLI_assert((elem + 1) * 2 <= builder->max_index_len); + uint idx = elem * 2; + builder->data[idx++] = RESTART_INDEX; + builder->data[idx++] = RESTART_INDEX; + if (builder->index_len < idx) { + builder->index_len = idx; + } +} + +void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem) +{ + BLI_assert(builder->prim_type == GPU_PRIM_TRIS); + BLI_assert((elem + 1) * 3 <= builder->max_index_len); + uint idx = elem * 3; + builder->data[idx++] = RESTART_INDEX; + builder->data[idx++] = RESTART_INDEX; + builder->data[idx++] = RESTART_INDEX; + if (builder->index_len < idx) { + builder->index_len = idx; + } +} + +GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length) +{ + GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + BLI_assert(elem_src && !elem_src->is_subrange); + BLI_assert(start + length <= elem_src->index_len); +#if GPU_TRACK_INDEX_RANGE + elem->index_type = elem_src->index_type; + elem->gl_index_type = elem_src->gl_index_type; + elem->base_index = elem_src->base_index; +#endif + elem->is_subrange = true; + elem->src = elem_src; + elem->index_start = start; + elem->index_len = length; + return elem; +} + #if GPU_TRACK_INDEX_RANGE /* Everything remains 32 bit while building to keep things simple. * Find min/max after, then convert to smallest index type possible. */ @@ -271,6 +365,10 @@ static void indexbuf_upload_data(GPUIndexBuf *elem) void GPU_indexbuf_use(GPUIndexBuf *elem) { + if (elem->is_subrange) { + GPU_indexbuf_use(elem->src); + return; + } if (elem->ibo_id == 0) { elem->ibo_id = GPU_buf_alloc(); } @@ -285,7 +383,7 @@ void GPU_indexbuf_discard(GPUIndexBuf *elem) if (elem->ibo_id) { GPU_buf_free(elem->ibo_id); } - if (elem->data) { + if (!elem->is_subrange && elem->data) { MEM_freeN(elem->data); } MEM_freeN(elem); diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index 99dbff05453..5839b34cd19 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -309,6 +309,7 @@ void gpu_extensions_init(void) } else if ((strstr(renderer, "Mesa DRI R")) || (strstr(renderer, "Radeon") && strstr(vendor, "X.Org")) || + (strstr(renderer, "AMD") && strstr(vendor, "X.Org")) || (strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) || (strstr(renderer, "Gallium ") && strstr(renderer, " on AMD "))) { GG.device = GPU_DEVICE_ATI; diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.c index 6b5c4836e83..0e3019ad122 100644 --- a/source/blender/gpu/intern/gpu_immediate.c +++ b/source/blender/gpu/intern/gpu_immediate.c @@ -220,8 +220,10 @@ void immBegin(GPUPrimType prim_type, uint vertex_len) /* does the current buffer have enough room? */ const uint available_bytes = IMM_BUFFER_SIZE - imm.buffer_offset; /* ensure vertex data is aligned */ - const uint pre_padding = padding( - imm.buffer_offset, imm.vertex_format.stride); /* might waste a little space, but it's safe */ + + /* Might waste a little space, but it's safe. */ + const uint pre_padding = padding(imm.buffer_offset, imm.vertex_format.stride); + if ((bytes_needed + pre_padding) <= available_bytes) { imm.buffer_offset += pre_padding; } diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.c index 58ca800a92c..fb0dffb58d1 100644 --- a/source/blender/gpu/intern/gpu_matrix.c +++ b/source/blender/gpu/intern/gpu_matrix.c @@ -23,6 +23,9 @@ #include "GPU_shader_interface.h" +#include "gpu_context_private.h" +#include "gpu_matrix_private.h" + #define SUPPRESS_GENERIC_MATRIX_API #define USE_GPU_PY_MATRIX_API /* only so values are declared */ #include "GPU_matrix.h" @@ -32,6 +35,8 @@ #include "BLI_math_rotation.h" #include "BLI_math_vector.h" +#include "MEM_guardedalloc.h" + #define DEBUG_MATRIX_BIND 0 #define MATRIX_STACK_DEPTH 32 @@ -44,7 +49,7 @@ typedef struct MatrixStack { uint top; } MatrixStack; -typedef struct { +typedef struct GPUMatrixState { MatrixStack model_view_stack; MatrixStack projection_stack; @@ -56,8 +61,16 @@ typedef struct { * TODO: separate Model from View transform? Batches/objects have model, * camera/eye has view & projection */ -} MatrixState; +} GPUMatrixState; + +#define ModelViewStack gpu_context_active_matrix_state_get()->model_view_stack +#define ModelView ModelViewStack.stack[ModelViewStack.top] +#define ProjectionStack gpu_context_active_matrix_state_get()->projection_stack +#define Projection ProjectionStack.stack[ProjectionStack.top] + +GPUMatrixState *GPU_matrix_state_create(void) +{ #define MATRIX_4X4_IDENTITY \ { \ {1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, \ @@ -66,27 +79,36 @@ typedef struct { } \ } -static MatrixState state = { - .model_view_stack = {{MATRIX_4X4_IDENTITY}, 0}, - .projection_stack = {{MATRIX_4X4_IDENTITY}, 0}, - .dirty = true, -}; + GPUMatrixState *state = MEM_mallocN(sizeof(*state), __func__); + const MatrixStack identity_stack = {{MATRIX_4X4_IDENTITY}, 0}; + + state->model_view_stack = state->projection_stack = identity_stack; + state->dirty = true; #undef MATRIX_4X4_IDENTITY -#define ModelViewStack state.model_view_stack -#define ModelView ModelViewStack.stack[ModelViewStack.top] + return state; +} -#define ProjectionStack state.projection_stack -#define Projection ProjectionStack.stack[ProjectionStack.top] +void GPU_matrix_state_discard(GPUMatrixState *state) +{ + MEM_freeN(state); +} + +static void gpu_matrix_state_active_set_dirty(bool value) +{ + GPUMatrixState *state = gpu_context_active_matrix_state_get(); + state->dirty = value; +} void GPU_matrix_reset(void) { - state.model_view_stack.top = 0; - state.projection_stack.top = 0; + GPUMatrixState *state = gpu_context_active_matrix_state_get(); + state->model_view_stack.top = 0; + state->projection_stack.top = 0; unit_m4(ModelView); unit_m4(Projection); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } #ifdef WITH_GPU_SAFETY @@ -123,7 +145,7 @@ void GPU_matrix_pop(void) { BLI_assert(ModelViewStack.top > 0); ModelViewStack.top--; - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_push_projection(void) @@ -137,34 +159,34 @@ void GPU_matrix_pop_projection(void) { BLI_assert(ProjectionStack.top > 0); ProjectionStack.top--; - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_set(const float m[4][4]) { copy_m4_m4(ModelView, m); CHECKMAT(ModelView3D); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_identity_projection_set(void) { unit_m4(Projection); CHECKMAT(Projection3D); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_projection_set(const float m[4][4]) { copy_m4_m4(Projection, m); CHECKMAT(Projection3D); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_identity_set(void) { unit_m4(ModelView); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_translate_2f(float x, float y) @@ -194,7 +216,7 @@ void GPU_matrix_translate_3f(float x, float y, float z) m[3][2] = z; GPU_matrix_mul(m); #endif - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_translate_3fv(const float vec[3]) @@ -243,7 +265,7 @@ void GPU_matrix_mul(const float m[4][4]) { mul_m4_m4_post(ModelView, m); CHECKMAT(ModelView); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_rotate_2d(float deg) @@ -272,7 +294,7 @@ void GPU_matrix_rotate_axis(float deg, char axis) /* rotate_m4 works in place */ rotate_m4(ModelView, axis, DEG2RADF(deg)); CHECKMAT(ModelView); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } static void mat4_ortho_set( @@ -298,7 +320,7 @@ static void mat4_ortho_set( m[2][3] = 0.0f; m[3][3] = 1.0f; - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } static void mat4_frustum_set( @@ -324,7 +346,7 @@ static void mat4_frustum_set( m[2][3] = -1.0f; m[3][3] = 0.0f; - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3]) @@ -389,14 +411,14 @@ static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3 m[2][3] = 0.0f; m[3][3] = 1.0f; - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_ortho_set(float left, float right, float bottom, float top, float near, float far) { mat4_ortho_set(Projection, left, right, bottom, top, near, far); CHECKMAT(Projection); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top) @@ -404,7 +426,7 @@ void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top) Mat4 m; mat4_ortho_set(m, left, right, bottom, top, -1.0f, 1.0f); CHECKMAT(Projection2D); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_frustum_set( @@ -412,7 +434,7 @@ void GPU_matrix_frustum_set( { mat4_frustum_set(Projection, left, right, bottom, top, near, far); CHECKMAT(Projection); - state.dirty = true; + gpu_matrix_state_active_set_dirty(true); } void GPU_matrix_perspective_set(float fovy, float aspect, float near, float far) @@ -678,12 +700,13 @@ void GPU_matrix_bind(const GPUShaderInterface *shaderface) glUniformMatrix4fv(P_inv->location, 1, GL_FALSE, (const float *)m); } - state.dirty = false; + gpu_matrix_state_active_set_dirty(false); } bool GPU_matrix_dirty_get(void) { - return state.dirty; + GPUMatrixState *state = gpu_context_active_matrix_state_get(); + return state->dirty; } /* -------------------------------------------------------------------- */ @@ -695,12 +718,14 @@ BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mis int GPU_matrix_stack_level_get_model_view(void) { - return (int)state.model_view_stack.top; + GPUMatrixState *state = gpu_context_active_matrix_state_get(); + return (int)state->model_view_stack.top; } int GPU_matrix_stack_level_get_projection(void) { - return (int)state.projection_stack.top; + GPUMatrixState *state = gpu_context_active_matrix_state_get(); + return (int)state->projection_stack.top; } /** \} */ diff --git a/source/blender/gpu/intern/gpu_matrix_private.h b/source/blender/gpu/intern/gpu_matrix_private.h new file mode 100644 index 00000000000..862ef065481 --- /dev/null +++ b/source/blender/gpu/intern/gpu_matrix_private.h @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup gpu + */ + +#ifndef __GPU_MATRIX_PRIVATE_H__ +#define __GPU_MATRIX_PRIVATE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct GPUMatrixState *GPU_matrix_state_create(void); +void GPU_matrix_state_discard(struct GPUMatrixState *state); + +#ifdef __cplusplus +} +#endif + +#endif /* __GPU_MATRIX_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index dab17fcd72a..955b11036ef 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -1598,7 +1598,7 @@ void GPU_texture_generate_mipmap(GPUTexture *tex) if (GPU_texture_depth(tex)) { /* Some drivers have bugs when using glGenerateMipmap with depth textures (see T56789). - * In this case we just create a complete texture with mipmaps manually without downsampling. + * In this case we just create a complete texture with mipmaps manually without down-sampling. * You must initialize the texture levels using other methods like * GPU_framebuffer_recursive_downsample(). */ int levels = 1 + floor(log2(max_ii(tex->w, tex->h))); diff --git a/source/blender/gpu/intern/gpu_vertex_format.c b/source/blender/gpu/intern/gpu_vertex_format.c index e745c525df6..66e5e254734 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.c +++ b/source/blender/gpu/intern/gpu_vertex_format.c @@ -31,6 +31,8 @@ #include <string.h> #include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_ghash.h" #define PACK_DEBUG 0 @@ -149,9 +151,9 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, GPUVertFetchMode fetch_mode) { #if TRUST_NO_ONE - assert(format->name_len < GPU_VERT_ATTR_MAX_LEN); /* there's room for more */ - assert(format->attr_len < GPU_VERT_ATTR_MAX_LEN); /* there's room for more */ - assert(!format->packed); /* packed means frozen/locked */ + assert(format->name_len < GPU_VERT_FORMAT_MAX_NAMES); /* there's room for more */ + assert(format->attr_len < GPU_VERT_ATTR_MAX_LEN); /* there's room for more */ + assert(!format->packed); /* packed means frozen/locked */ assert((comp_len >= 1 && comp_len <= 4) || comp_len == 8 || comp_len == 12 || comp_len == 16); switch (comp_type) { @@ -163,8 +165,10 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, /* 10_10_10 format intended for normals (xyz) or colors (rgb) * extra component packed.w can be manually set to { -2, -1, 0, 1 } */ assert(comp_len == 3 || comp_len == 4); - assert(fetch_mode == - GPU_FETCH_INT_TO_FLOAT_UNIT); /* not strictly required, may relax later */ + + /* Not strictly required, may relax later. */ + assert(fetch_mode == GPU_FETCH_INT_TO_FLOAT_UNIT); + break; default: /* integer types can be kept as int or converted/normalized to float */ @@ -195,7 +199,7 @@ void GPU_vertformat_alias_add(GPUVertFormat *format, const char *alias) { GPUVertAttr *attr = &format->attrs[format->attr_len - 1]; #if TRUST_NO_ONE - assert(format->name_len < GPU_VERT_ATTR_MAX_LEN); /* there's room for more */ + assert(format->name_len < GPU_VERT_FORMAT_MAX_NAMES); /* there's room for more */ assert(attr->name_len < GPU_VERT_ATTR_MAX_NAMES); #endif format->name_len++; /* multiname support */ @@ -216,6 +220,79 @@ int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name) return -1; } +/* Encode 8 original bytes into 11 safe bytes. */ +static void safe_bytes(char out[11], const char data[8]) +{ + char safe_chars[63] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + + uint64_t in = *(uint64_t *)data; + for (int i = 0; i < 11; i++) { + /* Encoding in base63 */ + out[i] = safe_chars[in % 63lu]; + in /= 63lu; + } +} + +/* Warning: Always add a prefix to the result of this function as + * the generated string can start with a number and not be a valid attribute name. */ +void GPU_vertformat_safe_attrib_name(const char *attrib_name, + char *r_safe_name, + uint UNUSED(max_len)) +{ + char data[8] = {0}; + uint len = strlen(attrib_name); + + if (len > 8) { + /* Start with the first 4 chars of the name; */ + for (int i = 0; i < 4; i++) { + data[i] = attrib_name[i]; + } + /* We use a hash to identify each data layer based on its name. + * NOTE: This is still prone to hash collision but the risks are very low.*/ + /* Start hashing after the first 2 chars. */ + *(uint *)&data[4] = BLI_ghashutil_strhash_p_murmur(attrib_name + 4); + } + else { + /* Copy the whole name. Collision is barely possible + * (hash would have to be equal to the last 4 bytes). */ + for (int i = 0; i < 8 && attrib_name[i] != '\0'; i++) { + data[i] = attrib_name[i]; + } + } + /* Convert to safe bytes characters. */ + safe_bytes(r_safe_name, data); + /* End the string */ + r_safe_name[11] = '\0'; + + BLI_assert(GPU_MAX_SAFE_ATTRIB_NAME >= 12); +#if 0 /* For debugging */ + printf("%s > %lx > %s\n", attrib_name, *(uint64_t *)data, r_safe_name); +#endif +} + +/* Make attribute layout non-interleaved. + * Warning! This does not change data layout! + * Use direct buffer access to fill the data. + * This is for advanced usage. + * + * Deinterleaved data means all attrib data for each attrib + * is stored continuously like this : + * 000011112222 + * instead of : + * 012012012012 + * + * Note this is per attrib deinterleaving, NOT per component. + * */ +void GPU_vertformat_deinterleave(GPUVertFormat *format) +{ + /* Ideally we should change the stride and offset here. This would allow + * us to use GPU_vertbuf_attr_set / GPU_vertbuf_attr_fill. But since + * we use only 11 bits for attr->offset this limits the size of the + * buffer considerably. So instead we do the conversion when creating + * bindings in create_bindings(). */ + format->deinterleaved = true; +} + uint padding(uint offset, uint alignment) { const uint mod = offset % alignment; @@ -389,58 +466,3 @@ void GPU_vertformat_from_interface(GPUVertFormat *format, const GPUShaderInterfa } } } - -/* OpenGL ES packs in a different order as desktop GL but component conversion is the same. - * Of the code here, only struct GPUPackedNormal needs to change. */ - -#define SIGNED_INT_10_MAX 511 -#define SIGNED_INT_10_MIN -512 - -static int clampi(int x, int min_allowed, int max_allowed) -{ -#if TRUST_NO_ONE - assert(min_allowed <= max_allowed); -#endif - if (x < min_allowed) { - return min_allowed; - } - else if (x > max_allowed) { - return max_allowed; - } - else { - return x; - } -} - -static int quantize(float x) -{ - int qx = x * 511.0f; - return clampi(qx, SIGNED_INT_10_MIN, SIGNED_INT_10_MAX); -} - -static int convert_i16(short x) -{ - /* 16-bit signed --> 10-bit signed */ - /* TODO: round? */ - return x >> 6; -} - -GPUPackedNormal GPU_normal_convert_i10_v3(const float data[3]) -{ - GPUPackedNormal n = { - .x = quantize(data[0]), - .y = quantize(data[1]), - .z = quantize(data[2]), - }; - return n; -} - -GPUPackedNormal GPU_normal_convert_i10_s3(const short data[3]) -{ - GPUPackedNormal n = { - .x = convert_i16(data[0]), - .y = convert_i16(data[1]), - .z = convert_i16(data[2]), - }; - return n; -} diff --git a/source/blender/gpu/intern/gpu_vertex_format_private.h b/source/blender/gpu/intern/gpu_vertex_format_private.h index a850d17a1dd..13459101669 100644 --- a/source/blender/gpu/intern/gpu_vertex_format_private.h +++ b/source/blender/gpu/intern/gpu_vertex_format_private.h @@ -27,6 +27,7 @@ #define __GPU_VERTEX_FORMAT_PRIVATE_H__ void VertexFormat_pack(GPUVertFormat *format); +void VertexFormat_deinterleave(GPUVertFormat *format, uint vertex_len); uint padding(uint offset, uint alignment); uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len); diff --git a/source/blender/gpu/shaders/gpu_shader_2D_edituvs_stretch_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_edituvs_stretch_vert.glsl index 810784e2fbc..0ce5504dfa8 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_edituvs_stretch_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_edituvs_stretch_vert.glsl @@ -8,7 +8,7 @@ in vec2 pos; in float stretch; #else -in vec4 uv_adj; +in vec2 uv_angles; in float angle; #endif @@ -52,6 +52,11 @@ vec3 weight_to_rgb(float weight) #define M_PI 3.1415926535897932 +vec2 angle_to_v2(float angle) +{ + return vec2(cos(angle), sin(angle)); +} + /* Adapted from BLI_math_vector.h */ float angle_normalized_v2v2(vec2 v1, vec2 v2) { @@ -69,7 +74,9 @@ void main() gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0); #ifdef STRETCH_ANGLE - float uv_angle = angle_normalized_v2v2(uv_adj.xy, uv_adj.zw) / M_PI; + vec2 v1 = angle_to_v2(uv_angles.x * M_PI); + vec2 v2 = angle_to_v2(uv_angles.y * M_PI); + float uv_angle = angle_normalized_v2v2(v1, v2) / M_PI; float stretch = 1.0 - abs(uv_angle - angle); stretch = stretch; stretch = 1.0 - stretch * stretch; diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 00b8ce54eb3..1529279ca03 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -250,185 +250,156 @@ void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist) outview = normalize(co); } -void math_add(float val1, float val2, out float outval) +void math_add(float a, float b, out float result) { - outval = val1 + val2; + result = a + b; } -void math_subtract(float val1, float val2, out float outval) +void math_subtract(float a, float b, out float result) { - outval = val1 - val2; + result = a - b; } -void math_multiply(float val1, float val2, out float outval) +void math_multiply(float a, float b, out float result) { - outval = val1 * val2; + result = a * b; } -void math_divide(float val1, float val2, out float outval) +void math_divide(float a, float b, out float result) { - if (val2 == 0.0) { - outval = 0.0; + result = (b != 0.0) ? a / b : 0.0; +} + +void math_power(float a, float b, out float result) +{ + if (a >= 0.0) { + result = compatible_pow(a, b); } else { - outval = val1 / val2; + float fraction = mod(abs(b), 1.0); + if (fraction > 0.999 || fraction < 0.001) { + result = compatible_pow(a, floor(b + 0.5)); + } + else { + result = 0.0; + } } } -void math_sine(float val, out float outval) +void math_logarithm(float a, float b, out float result) { - outval = sin(val); + result = (a > 0.0 && b > 0.0) ? log2(a) / log2(b) : 0.0; } -void math_cosine(float val, out float outval) +void math_sqrt(float a, float b, out float result) { - outval = cos(val); + result = (a > 0.0) ? sqrt(a) : 0.0; } -void math_tangent(float val, out float outval) +void math_absolute(float a, float b, out float result) { - outval = tan(val); + result = abs(a); } -void math_asin(float val, out float outval) +void math_minimum(float a, float b, out float result) { - if (val <= 1.0 && val >= -1.0) { - outval = asin(val); - } - else { - outval = 0.0; - } + result = min(a, b); } -void math_acos(float val, out float outval) +void math_maximum(float a, float b, out float result) { - if (val <= 1.0 && val >= -1.0) { - outval = acos(val); - } - else { - outval = 0.0; - } + result = max(a, b); } -void math_atan(float val, out float outval) +void math_less_than(float a, float b, out float result) { - outval = atan(val); + result = (a < b) ? 1.0 : 0.0; } -void math_pow(float val1, float val2, out float outval) +void math_greater_than(float a, float b, out float result) { - if (val1 >= 0.0) { - outval = compatible_pow(val1, val2); - } - else { - float val2_mod_1 = mod(abs(val2), 1.0); + result = (a > b) ? 1.0 : 0.0; +} - if (val2_mod_1 > 0.999 || val2_mod_1 < 0.001) { - outval = compatible_pow(val1, floor(val2 + 0.5)); - } - else { - outval = 0.0; - } - } +void math_round(float a, float b, out float result) +{ + result = floor(a + 0.5); } -void math_log(float val1, float val2, out float outval) +void math_floor(float a, float b, out float result) { - if (val1 > 0.0 && val2 > 0.0) { - outval = log2(val1) / log2(val2); - } - else { - outval = 0.0; - } + result = floor(a); } -void math_max(float val1, float val2, out float outval) +void math_ceil(float a, float b, out float result) { - outval = max(val1, val2); + result = ceil(a); } -void math_min(float val1, float val2, out float outval) +void math_fraction(float a, float b, out float result) { - outval = min(val1, val2); + result = a - floor(a); } -void math_round(float val, out float outval) +/* Change sign to match C convention. mod in GLSL will take absolute for negative numbers. + * See https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/mod.xhtml + */ +void math_modulo(float a, float b, out float result) { - outval = floor(val + 0.5); + result = (b != 0.0) ? sign(a) * mod(abs(a), b) : 0.0; } -void math_less_than(float val1, float val2, out float outval) +void math_sine(float a, float b, out float result) { - if (val1 < val2) { - outval = 1.0; - } - else { - outval = 0.0; - } + result = sin(a); } -void math_greater_than(float val1, float val2, out float outval) +void math_cosine(float a, float b, out float result) { - if (val1 > val2) { - outval = 1.0; - } - else { - outval = 0.0; - } + result = cos(a); } -void math_modulo(float val1, float val2, out float outval) +void math_tangent(float a, float b, out float result) { - if (val2 == 0.0) { - outval = 0.0; - } - else { - /* change sign to match C convention, mod in GLSL will take absolute for negative numbers, - * see https://www.opengl.org/sdk/docs/man/html/mod.xhtml */ - outval = sign(val1) * mod(abs(val1), val2); - } + result = tan(a); } -void math_abs(float val1, out float outval) +void math_arcsine(float a, float b, out float result) { - outval = abs(val1); + result = (a <= 1.0 && a >= -1.0) ? asin(a) : 0.0; } -void math_atan2(float val1, float val2, out float outval) +void math_arccosine(float a, float b, out float result) { - outval = atan(val1, val2); + result = (a <= 1.0 && a >= -1.0) ? acos(a) : 0.0; } -void math_floor(float val, out float outval) +void math_arctangent(float a, float b, out float result) { - outval = floor(val); + result = atan(a); } -void math_ceil(float val, out float outval) +void math_arctan2(float a, float b, out float result) { - outval = ceil(val); + result = atan(a, b); } -void math_fract(float val, out float outval) +void squeeze(float val, float width, float center, out float outval) { - outval = val - floor(val); + outval = 1.0 / (1.0 + pow(2.71828183, -((val - center) * width))); } -void math_sqrt(float val, out float outval) +void map_range( + float value, float fromMin, float fromMax, float toMin, float toMax, out float result) { - if (val > 0.0) { - outval = sqrt(val); + if (fromMax != fromMin) { + result = toMin + ((value - fromMin) / (fromMax - fromMin)) * (toMax - toMin); } else { - outval = 0.0; + result = 0.0; } } -void squeeze(float val, float width, float center, out float outval) -{ - outval = 1.0 / (1.0 + pow(2.71828183, -((val - center) * width))); -} - void vec_math_add(vec3 v1, vec3 v2, out vec3 outvec, out float outval) { outvec = v1 + v2; @@ -960,9 +931,9 @@ void clamp_vec3(vec3 vec, vec3 min, vec3 max, out vec3 out_vec) out_vec = clamp(vec, min, max); } -void clamp_val(float value, float min, float max, out float out_value) +void clamp_value(float value, float min, float max, out float result) { - out_value = clamp(value, min, max); + result = clamp(value, min, max); } void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 outcol) @@ -1228,10 +1199,9 @@ vec3 principled_sheen(float NV, vec3 basecol_tint, float sheen_tint) void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result) { N = normalize(N); - vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; - result.ssr_normal = normal_encode(vN, viewCameraVec); eevee_closure_diffuse(N, color.rgb, 1.0, result.radiance); + closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result); result.radiance *= color.rgb; } @@ -1243,9 +1213,7 @@ void node_bsdf_glossy(vec4 color, float roughness, vec3 N, float ssr_id, out Clo vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; result.radiance = out_spec * color.rgb; - result.ssr_data = vec4(ssr_spec * color.rgb, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); + closure_load_ssr_data(ssr_spec * color.rgb, roughness, N, viewCameraVec, int(ssr_id), result); } void node_bsdf_anisotropic(vec4 color, @@ -1274,9 +1242,8 @@ void node_bsdf_glass( vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; result.radiance = mix(out_refr, out_spec, fresnel); - result.ssr_data = vec4(ssr_spec * color.rgb * fresnel, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); + closure_load_ssr_data( + ssr_spec * color.rgb * fresnel, roughness, N, viewCameraVec, int(ssr_id), result); } void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out Closure result) @@ -1341,7 +1308,7 @@ void node_bsdf_principled(vec4 base_color, vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface); - float sss_scalef = dot(sss_scale, vec3(1.0 / 3.0)) * subsurface; + float sss_scalef = avg(sss_scale) * subsurface; eevee_closure_principled(N, mixed_ss_base_color, f0, @@ -1365,28 +1332,34 @@ void node_bsdf_principled(vec4 base_color, vec3(1.0); /* Simulate 2 transmission event */ out_refr *= refr_color * (1.0 - fresnel) * transmission; - vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; result.radiance = out_spec + out_refr; result.radiance += out_diff * out_sheen; /* Coarse approx. */ + + closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result); + + vec3 sss_radiance = (out_diff + out_trans) * alpha; # ifndef USE_SSS - result.radiance += (out_diff + out_trans) * mixed_ss_base_color * (1.0 - transmission); -# endif - result.ssr_data = vec4(ssr_spec, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); -# ifdef USE_SSS - result.sss_data.a = sss_scalef; - result.sss_data.rgb = out_diff + out_trans; + result.radiance += sss_radiance * mixed_ss_base_color * (1.0 - transmission); +# else # ifdef USE_SSS_ALBEDO - result.sss_albedo.rgb = mixed_ss_base_color; + vec3 sss_albedo = mixed_ss_base_color; # else - result.sss_data.rgb *= mixed_ss_base_color; + sss_radiance *= mixed_ss_base_color; # endif - result.sss_data.rgb *= (1.0 - transmission); -# endif + sss_radiance *= (1.0 - transmission); + closure_load_sss_data(sss_scalef, + sss_radiance, +# ifdef USE_SSS_ALBEDO + sss_albedo, +# endif + int(sss_id), + result); +# endif /* USE_SSS */ + result.radiance += emission.rgb; - result.opacity = alpha; + result.radiance *= alpha; + result.transmittance = vec3(1.0 - alpha); } void node_bsdf_principled_dielectric(vec4 base_color, @@ -1432,14 +1405,12 @@ void node_bsdf_principled_dielectric(vec4 base_color, eevee_closure_default( N, diffuse, f0, vec3(1.0), int(ssr_id), roughness, 1.0, out_diff, out_spec, ssr_spec); - vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; result.radiance = out_spec + out_diff * (diffuse + out_sheen); - result.ssr_data = vec4(ssr_spec, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); + closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result); result.radiance += emission.rgb; - result.opacity = alpha; + result.radiance *= alpha; + result.transmittance = vec3(1.0 - alpha); } void node_bsdf_principled_metallic(vec4 base_color, @@ -1477,14 +1448,12 @@ void node_bsdf_principled_metallic(vec4 base_color, eevee_closure_glossy(N, base_color.rgb, f90, int(ssr_id), roughness, 1.0, out_spec, ssr_spec); - vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; result.radiance = out_spec; - result.ssr_data = vec4(ssr_spec, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); + closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result); result.radiance += emission.rgb; - result.opacity = alpha; + result.radiance *= alpha; + result.transmittance = vec3(1.0 - alpha); } void node_bsdf_principled_clearcoat(vec4 base_color, @@ -1532,14 +1501,12 @@ void node_bsdf_principled_clearcoat(vec4 base_color, out_spec, ssr_spec); - vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; result.radiance = out_spec; - result.ssr_data = vec4(ssr_spec, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); + closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result); result.radiance += emission.rgb; - result.opacity = alpha; + result.radiance *= alpha; + result.transmittance = vec3(1.0 - alpha); } void node_bsdf_principled_subsurface(vec4 base_color, @@ -1580,7 +1547,7 @@ void node_bsdf_principled_subsurface(vec4 base_color, subsurface_color = subsurface_color * (1.0 - metallic); vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface); - float sss_scalef = dot(sss_scale, vec3(1.0 / 3.0)) * subsurface; + float sss_scalef = avg(sss_scale) * subsurface; float NV = dot(N, cameraVec); vec3 out_sheen = sheen * principled_sheen(NV, ctint, sheen_tint); @@ -1600,26 +1567,33 @@ void node_bsdf_principled_subsurface(vec4 base_color, out_spec, ssr_spec); - vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; result.radiance = out_spec; - result.ssr_data = vec4(ssr_spec, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); -# ifdef USE_SSS - result.sss_data.a = sss_scalef; - result.sss_data.rgb = out_diff + out_trans; + closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result); + + vec3 sss_radiance = (out_diff + out_trans) * alpha; +# ifndef USE_SSS + result.radiance += sss_radiance * mixed_ss_base_color * (1.0 - transmission); +# else # ifdef USE_SSS_ALBEDO - result.sss_albedo.rgb = mixed_ss_base_color; + vec3 sss_albedo = mixed_ss_base_color; # else - result.sss_data.rgb *= mixed_ss_base_color; + sss_radiance *= mixed_ss_base_color; # endif -# else - result.radiance += (out_diff + out_trans) * mixed_ss_base_color; -# endif + sss_radiance *= (1.0 - transmission); + closure_load_sss_data(sss_scalef, + sss_radiance, +# ifdef USE_SSS_ALBEDO + sss_albedo, +# endif + int(sss_id), + result); +# endif /* USE_SSS */ + result.radiance += out_diff * out_sheen; result.radiance += emission.rgb; - result.opacity = alpha; + result.radiance *= alpha; + result.transmittance = vec3(1.0 - alpha); } void node_bsdf_principled_glass(vec4 base_color, @@ -1669,14 +1643,12 @@ void node_bsdf_principled_glass(vec4 base_color, out_spec *= spec_col; ssr_spec *= spec_col * fresnel; - vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; result.radiance = mix(out_refr, out_spec, fresnel); - result.ssr_data = vec4(ssr_spec, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); + closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result); result.radiance += emission.rgb; - result.opacity = alpha; + result.radiance *= alpha; + result.transmittance = vec3(1.0 - alpha); } void node_bsdf_translucent(vec4 color, vec3 N, out Closure result) @@ -1686,11 +1658,9 @@ void node_bsdf_translucent(vec4 color, vec3 N, out Closure result) void node_bsdf_transparent(vec4 color, out Closure result) { - /* this isn't right */ result = CLOSURE_DEFAULT; result.radiance = vec3(0.0); - result.opacity = clamp(1.0 - dot(color.rgb, vec3(0.3333334)), 0.0, 1.0); - result.ssr_id = TRANSPARENT_CLOSURE_FLAG; + result.transmittance = abs(color.rgb); } void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out Closure result) @@ -1712,19 +1682,25 @@ void node_subsurface_scattering(vec4 color, vec3 out_diff, out_trans; vec3 vN = mat3(ViewMatrix) * N; result = CLOSURE_DEFAULT; - result.ssr_data = vec4(0.0); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = -1; - result.sss_data.a = scale; + closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result); + eevee_closure_subsurface(N, color.rgb, 1.0, scale, out_diff, out_trans); - result.sss_data.rgb = out_diff + out_trans; + + vec3 sss_radiance = out_diff + out_trans; # ifdef USE_SSS_ALBEDO /* Not perfect for texture_blur not exactly equal to 0.0 or 1.0. */ - result.sss_albedo.rgb = mix(color.rgb, vec3(1.0), texture_blur); - result.sss_data.rgb *= mix(vec3(1.0), color.rgb, texture_blur); + vec3 sss_albedo = mix(color.rgb, vec3(1.0), texture_blur); + sss_radiance *= mix(vec3(1.0), color.rgb, texture_blur); # else - result.sss_data.rgb *= color.rgb; + sss_radiance *= color.rgb; # endif + closure_load_sss_data(scale, + sss_radiance, +# ifdef USE_SSS_ALBEDO + sss_albedo, +# endif + int(sss_id), + result); # else node_bsdf_diffuse(color, 0.0, N, result); # endif @@ -1740,7 +1716,6 @@ void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Cl result = CLOSURE_DEFAULT; result.ssr_normal = normal_encode(vN, viewCameraVec); result.radiance = out_refr * color.rgb; - result.ssr_id = REFRACT_CLOSURE_FLAG; } void node_ambient_occlusion( @@ -1841,7 +1816,7 @@ void node_background(vec4 color, float strength, out Closure result) color *= strength; result = CLOSURE_DEFAULT; result.radiance = color.rgb; - result.opacity = color.a; + result.transmittance = vec3(0.0); #else result = CLOSURE_DEFAULT; #endif @@ -1949,6 +1924,15 @@ void node_volume_principled(vec4 color, #endif } +void node_holdout(out Closure result) +{ + result = CLOSURE_DEFAULT; +#ifndef VOLUMETRICS + result.holdout = 1.0; + result.flag = CLOSURE_HOLDOUT_FLAG; +#endif +} + /* closures */ void node_mix_shader(float fac, Closure shader1, Closure shader2, out Closure shader) @@ -2023,7 +2007,7 @@ void node_attribute_volume_density(sampler3D tex, out vec4 outcol, out vec3 outv #endif outvec = texture(tex, cos).aaa; outcol = vec4(outvec, 1.0); - outf = dot(vec3(1.0 / 3.0), outvec); + outf = avg(outvec); } uniform vec3 volumeColor = vec3(1.0); @@ -2044,7 +2028,7 @@ void node_attribute_volume_color(sampler3D tex, out vec4 outcol, out vec3 outvec outvec = value.rgb * volumeColor; outcol = vec4(outvec, 1.0); - outf = dot(vec3(1.0 / 3.0), outvec); + outf = avg(outvec); } void node_attribute_volume_flame(sampler3D tex, out vec4 outcol, out vec3 outvec, out float outf) @@ -2078,7 +2062,7 @@ void node_attribute(vec3 attr, out vec4 outcol, out vec3 outvec, out float outf) { outcol = vec4(attr, 1.0); outvec = attr; - outf = dot(vec3(1.0 / 3.0), attr); + outf = avg(attr); } void node_uvmap(vec3 attr_uv, out vec3 outvec) @@ -3491,7 +3475,7 @@ void node_output_world(Closure surface, Closure volume, out Closure result) { #ifndef VOLUMETRICS result.radiance = surface.radiance * backgroundAlpha; - result.opacity = backgroundAlpha; + result.transmittance = vec3(1.0 - backgroundAlpha); #else result = volume; #endif /* VOLUMETRICS */ @@ -3538,6 +3522,8 @@ void node_eevee_specular(vec4 diffuse, float ssr_id, out Closure result) { + normal = normalize(normal); + vec3 out_diff, out_spec, ssr_spec; eevee_closure_default_clearcoat(normal, diffuse.rgb, @@ -3553,19 +3539,19 @@ void node_eevee_specular(vec4 diffuse, out_spec, ssr_spec); - vec3 vN = normalize(mat3(ViewMatrix) * normal); + float alpha = 1.0 - transp; result = CLOSURE_DEFAULT; result.radiance = out_diff * diffuse.rgb + out_spec + emissive.rgb; - result.opacity = 1.0 - transp; - result.ssr_data = vec4(ssr_spec, roughness); - result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = int(ssr_id); + result.radiance *= alpha; + result.transmittance = vec3(transp); + + closure_load_ssr_data(ssr_spec * alpha, roughness, normal, viewCameraVec, int(ssr_id), result); } void node_shader_to_rgba(Closure cl, out vec4 outcol, out float outalpha) { vec4 spec_accum = vec4(0.0); - if (ssrToggle && cl.ssr_id == outputSsrId) { + if (ssrToggle && FLAG_TEST(cl.flag, CLOSURE_SSR_FLAG)) { vec3 V = cameraVec; vec3 vN = normal_decode(cl.ssr_normal, viewCameraVec); vec3 N = transform_direction(ViewMatrixInverse, vN); @@ -3574,7 +3560,7 @@ void node_shader_to_rgba(Closure cl, out vec4 outcol, out float outalpha) fallback_cubemap(N, V, worldPosition, viewPosition, roughness, roughnessSquared, spec_accum); } - outalpha = cl.opacity; + outalpha = avg(cl.transmittance); outcol = vec4((spec_accum.rgb * cl.ssr_data.rgb) + cl.radiance, 1.0); # ifdef USE_SSS diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 791e939db7f..571cac41399 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -46,8 +46,7 @@ * * IMB needs: * - \ref DNA module - * The listbase types are used for handling the memory - * management. + * The #ListBase types are used for handling the memory management. * - \ref blenlib module * blenlib handles guarded memory management in blender-style. * BLI_winstuff.h makes a few windows specific behaviors diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 1f180d0d9c1..49e9c4c54d2 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -110,8 +110,8 @@ void bicubic_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in return; } - pixel_from_buffer( - out, &outI, &outF, xout, yout); /* gcc warns these could be uninitialized, but its ok */ + /* gcc warns these could be uninitialized, but its ok. */ + pixel_from_buffer(out, &outI, &outF, xout, yout); bicubic_interpolation_color(in, outI, outF, u, v); } @@ -220,8 +220,8 @@ void bilinear_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, i return; } - pixel_from_buffer( - out, &outI, &outF, xout, yout); /* gcc warns these could be uninitialized, but its ok */ + /* gcc warns these could be uninitialized, but its ok. */ + pixel_from_buffer(out, &outI, &outF, xout, yout); bilinear_interpolation_color(in, outI, outF, u, v); } @@ -332,8 +332,8 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float x, float y, int xout, in return; } - pixel_from_buffer( - out, &outI, &outF, xout, yout); /* gcc warns these could be uninitialized, but its ok */ + /* gcc warns these could be uninitialized, but its ok. */ + pixel_from_buffer(out, &outI, &outF, xout, yout); nearest_interpolation_color(in, outI, outF, x, y); } diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index c9883d5df86..f18eb2f7303 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -1215,9 +1215,9 @@ void IMB_exr_read_channels(void *handle) /* check if exr was saved with previous versions of blender which flipped images */ const StringAttribute *ta = data->ifile->header(0).findTypedAttribute<StringAttribute>( "BlenderMultiChannel"); - short flip = (ta && STREQLEN(ta->value().c_str(), - "Blender V2.43", - 13)); /* 'previous multilayer attribute, flipped */ + + /* 'previous multilayer attribute, flipped. */ + short flip = (ta && STREQLEN(ta->value().c_str(), "Blender V2.43", 13)); exr_printf( "\nIMB_exr_read_channels\n%s %-6s %-22s " diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c index 28557277d72..46ddee25b0f 100644 --- a/source/blender/imbuf/intern/scaling.c +++ b/source/blender/imbuf/intern/scaling.c @@ -1629,8 +1629,8 @@ bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy) return false; } - /* scaleup / scaledown functions below change ibuf->x and ibuf->y - * so we first scale the Z-buffer (if any) */ + /* Scale-up / scale-down functions below change ibuf->x and ibuf->y + * so we first scale the Z-buffer (if any). */ scalefast_Z_ImBuf(ibuf, newx, newy); /* try to scale common cases in a fast way */ diff --git a/source/blender/makesdna/DNA_documentation.h b/source/blender/makesdna/DNA_documentation.h index be11b3d9040..0251625292c 100644 --- a/source/blender/makesdna/DNA_documentation.h +++ b/source/blender/makesdna/DNA_documentation.h @@ -27,7 +27,7 @@ * blender file. There is an executable that scans all files, looking * for struct-s to serialize (hence sdna: Struct \ref DNA). From this * information, it builds a file with numbers that encode the format, - * the names of variables, and the plce to look for them. + * the names of variables, and the place to look for them. * * \section dnaissues Known issues with DNA * diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 82628065014..83ee8be7f68 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -502,8 +502,11 @@ typedef struct SimplifyGpencilModifierData { short step; /** Custom index for passes. */ int layer_pass; - /* Sample length */ + /** Sample length */ float length; + /** Merge distance */ + float distance; + char _pad[4]; } SimplifyGpencilModifierData; typedef enum eSimplifyGpencil_Flag { diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index bf72ce5e598..9682fa1d13b 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -651,6 +651,8 @@ typedef enum eGP_DrawMode { ((gpd) && (gpd->flag & \ (GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE))) #define GPENCIL_PAINT_MODE(gpd) ((gpd) && (gpd->flag & (GP_DATA_STROKE_PAINTMODE))) +#define GPENCIL_SCULPT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_SCULPTMODE)) +#define GPENCIL_WEIGHT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) #define GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd) \ ((gpd) && (gpd->flag & (GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE))) #define GPENCIL_NONE_EDIT_MODE(gpd) \ diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 152ecb85991..1d1ccef8846 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -306,12 +306,12 @@ typedef struct Material { /* blend_method */ enum { - MA_BM_SOLID, - MA_BM_ADD, - MA_BM_MULTIPLY, - MA_BM_CLIP, - MA_BM_HASHED, - MA_BM_BLEND, + MA_BM_SOLID = 0, + // MA_BM_ADD = 1, /* deprecated */ + // MA_BM_MULTIPLY = 2, /* deprecated */ + MA_BM_CLIP = 3, + MA_BM_HASHED = 4, + MA_BM_BLEND = 5, }; /* blend_flag */ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 8a9a69ac178..caa7c98335a 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -193,9 +193,10 @@ typedef struct Mesh { short totcol; + float remesh_voxel_size; + char _pad1[4]; /** Deprecated multiresolution modeling data, only keep for loading old files. */ struct Multires *mr DNA_DEPRECATED; - void *_pad1; Mesh_Runtime runtime; } Mesh; @@ -250,6 +251,8 @@ enum { ME_FLAG_UNUSED_8 = 1 << 8, /* cleared */ ME_DS_EXPAND = 1 << 9, ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10, + ME_REMESH_SMOOTH_NORMALS = 1 << 11, + ME_REMESH_REPROJECT_PAINT_MASK = 1 << 12, }; /* me->cd_flag */ diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 88eef05f8be..af350cfee48 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -188,10 +188,6 @@ typedef struct MVertTri { unsigned int tri[3]; } MVertTri; -// typedef struct MTexPoly { -// void *_pad; -//} MTexPoly; - typedef struct MLoopUV { float uv[2]; int flag; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 75c0721f72a..81b93ce6541 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -184,6 +184,8 @@ typedef enum eNodeSocketFlag { /** socket hidden automatically, to distinguish from manually hidden */ SOCK_AUTO_HIDDEN__DEPRECATED = (1 << 8), SOCK_NO_INTERNAL_LINK = (1 << 9), + /** Draw socket in a more compact form. */ + SOCK_COMPACT = (1 << 10), } eNodeSocketFlag; /* limit data in bNode to what we want to see saved? */ @@ -1010,6 +1012,11 @@ typedef struct NodeCryptomatte { char _pad[4]; } NodeCryptomatte; +typedef struct NodeDenoise { + char hdr; + char _pad[7]; +} NodeDenoise; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1160,31 +1167,31 @@ typedef struct NodeCryptomatte { /* math node clamp */ #define SHD_MATH_CLAMP 1 -/* Math node operation/ */ +/* Math node operations. */ enum { NODE_MATH_ADD = 0, - NODE_MATH_SUB = 1, - NODE_MATH_MUL = 2, + NODE_MATH_SUBTRACT = 1, + NODE_MATH_MULTIPLY = 2, NODE_MATH_DIVIDE = 3, - NODE_MATH_SIN = 4, - NODE_MATH_COS = 5, - NODE_MATH_TAN = 6, - NODE_MATH_ASIN = 7, - NODE_MATH_ACOS = 8, - NODE_MATH_ATAN = 9, - NODE_MATH_POW = 10, - NODE_MATH_LOG = 11, - NODE_MATH_MIN = 12, - NODE_MATH_MAX = 13, + NODE_MATH_SINE = 4, + NODE_MATH_COSINE = 5, + NODE_MATH_TANGENT = 6, + NODE_MATH_ARCSINE = 7, + NODE_MATH_ARCCOSINE = 8, + NODE_MATH_ARCTANGENT = 9, + NODE_MATH_POWER = 10, + NODE_MATH_LOGARITHM = 11, + NODE_MATH_MINIMUM = 12, + NODE_MATH_MAXIMUM = 13, NODE_MATH_ROUND = 14, - NODE_MATH_LESS = 15, - NODE_MATH_GREATER = 16, - NODE_MATH_MOD = 17, - NODE_MATH_ABS = 18, - NODE_MATH_ATAN2 = 19, + NODE_MATH_LESS_THAN = 15, + NODE_MATH_GREATER_THAN = 16, + NODE_MATH_MODULO = 17, + NODE_MATH_ABSOLUTE = 18, + NODE_MATH_ARCTAN2 = 19, NODE_MATH_FLOOR = 20, NODE_MATH_CEIL = 21, - NODE_MATH_FRACT = 22, + NODE_MATH_FRACTION = 22, NODE_MATH_SQRT = 23, }; diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h index 2207e08558d..34a1b6d3e0b 100644 --- a/source/blender/makesdna/DNA_object_force_types.h +++ b/source/blender/makesdna/DNA_object_force_types.h @@ -271,7 +271,7 @@ typedef struct PointCache { char name[64]; char prev_name[64]; - char info[64]; + char info[128]; /** File path, 1024 = FILE_MAX. */ char path[1024]; @@ -497,6 +497,8 @@ typedef struct SoftBody { #define PTCACHE_FAKE_SMOKE (1 << 12) #define PTCACHE_IGNORE_CLEAR (1 << 13) +#define PTCACHE_FLAG_INFO_DIRTY (1 << 14) + /* PTCACHE_OUTDATED + PTCACHE_FRAMES_SKIPPED */ #define PTCACHE_REDO_NEEDED 258 diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h index 1462d955f81..9776063f220 100644 --- a/source/blender/makesdna/DNA_outliner_types.h +++ b/source/blender/makesdna/DNA_outliner_types.h @@ -60,6 +60,10 @@ enum { TSE_DRAG_INTO = (1 << 6), TSE_DRAG_BEFORE = (1 << 7), TSE_DRAG_AFTER = (1 << 8), + /* Needed because outliner-only elements can be active */ + TSE_ACTIVE = (1 << 9), + /* Needed because walk selection should not activate */ + TSE_ACTIVE_WALK = (1 << 10), TSE_DRAG_ANY = (TSE_DRAG_INTO | TSE_DRAG_BEFORE | TSE_DRAG_AFTER), }; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f50b345e402..45159af306c 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1125,7 +1125,7 @@ typedef struct GP_Sculpt_Settings { /* GP_Sculpt_Settings.flag */ typedef enum eGP_Sculpt_SettingsFlag { /* only affect selected points */ - GP_SCULPT_SETT_FLAG_SELECT_MASK = (1 << 0), + GP_SCULPT_SETT_FLAG_DEPRECATED = (1 << 0), /* apply brush to position */ GP_SCULPT_SETT_FLAG_APPLY_POSITION = (1 << 1), /* apply brush to strength */ @@ -1142,6 +1142,16 @@ typedef enum eGP_Sculpt_SettingsFlag { GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE = (1 << 7), } eGP_Sculpt_SettingsFlag; +/* GP_Sculpt_Settings.gpencil_selectmode_sculpt */ +typedef enum eGP_Sculpt_SelectMaskFlag { + /* only affect selected points */ + GP_SCULPT_MASK_SELECTMODE_POINT = (1 << 0), + /* only affect selected strokes */ + GP_SCULPT_MASK_SELECTMODE_STROKE = (1 << 1), + /* only affect selected segmenst */ + GP_SCULPT_MASK_SELECTMODE_SEGMENT = (1 << 2), +} eGP_Sculpt_SelectMaskFlag; + /* Settings for GP Interpolation Operators */ typedef struct GP_Interpolate_Settings { /** #eGP_Interpolate_SettingsFlag. */ @@ -1412,8 +1422,10 @@ typedef struct ToolSettings { /** Default stroke thickness for annotation strokes. */ short annotate_thickness; - /** Stroke selection mode. */ - short gpencil_selectmode; + /** Stroke selection mode for Edit. */ + char gpencil_selectmode_edit; + /** Stroke selection mode for Sculpt. */ + char gpencil_selectmode_sculpt; /* Grease Pencil Sculpt */ struct GP_Sculpt_Settings gp_sculpt; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index e37d72ff8af..fd421cd8304 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -375,6 +375,13 @@ typedef struct ScrArea { typedef struct ARegion_Runtime { /* Panel category to use between 'layout' and 'draw'. */ const char *category; + + /** + * The visible part of the region, use with region overlap not to draw + * on top of the overlapping regions. + * + * Lazy initialize, zero'd when unset, relative to #ARegion.winrct x/y min. */ + rcti visible_rect; } ARegion_Runtime; typedef struct ARegion { diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 41fc2244cce..030116ea5cc 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -244,7 +244,12 @@ typedef struct SpaceOutliner { char search_string[64]; struct TreeStoreElem search_tse; - short flag, outlinevis, storeflag, search_flags; + short flag, outlinevis, storeflag; + char search_flags; + + /** Selection syncing flag (#WM_OUTLINER_SYNC_SELECT_FROM_OBJECT and similar flags). */ + char sync_select_dirty; + int filter; char filter_state; char show_restrict_flags; @@ -263,6 +268,7 @@ typedef enum eSpaceOutliner_Flag { SO_FLAG_UNUSED_1 = (1 << 2), /* cleared */ SO_HIDE_KEYINGSETINFO = (1 << 3), SO_SKIP_SORT_ALPHA = (1 << 4), + SO_SYNC_SELECT = (1 << 5), } eSpaceOutliner_Flag; /* SpaceOutliner.filter */ @@ -281,13 +287,14 @@ typedef enum eSpaceOutliner_Filter { SO_FILTER_NO_OB_CAMERA = (1 << 10), SO_FILTER_NO_OB_OTHERS = (1 << 11), - SO_FILTER_UNUSED_12 = (1 << 12), /* cleared */ - SO_FILTER_OB_STATE_VISIBLE = (1 << 13), /* Not set via DNA. */ - SO_FILTER_OB_STATE_SELECTED = (1 << 14), /* Not set via DNA. */ - SO_FILTER_OB_STATE_ACTIVE = (1 << 15), /* Not set via DNA. */ - SO_FILTER_NO_COLLECTION = (1 << 16), + SO_FILTER_UNUSED_12 = (1 << 12), /* cleared */ + SO_FILTER_OB_STATE_VISIBLE = (1 << 13), /* Not set via DNA. */ + SO_FILTER_OB_STATE_INVISIBLE = (1 << 14), /* Not set via DNA. */ + SO_FILTER_OB_STATE_SELECTED = (1 << 15), /* Not set via DNA. */ + SO_FILTER_OB_STATE_ACTIVE = (1 << 16), /* Not set via DNA. */ + SO_FILTER_NO_COLLECTION = (1 << 17), - SO_FILTER_ID_TYPE = (1 << 17), + SO_FILTER_ID_TYPE = (1 << 18), } eSpaceOutliner_Filter; #define SO_FILTER_OB_TYPE \ @@ -295,7 +302,8 @@ typedef enum eSpaceOutliner_Filter { SO_FILTER_NO_OB_LAMP | SO_FILTER_NO_OB_CAMERA | SO_FILTER_NO_OB_OTHERS) #define SO_FILTER_OB_STATE \ - (SO_FILTER_OB_STATE_VISIBLE | SO_FILTER_OB_STATE_SELECTED | SO_FILTER_OB_STATE_ACTIVE) + (SO_FILTER_OB_STATE_VISIBLE | SO_FILTER_OB_STATE_INVISIBLE | SO_FILTER_OB_STATE_SELECTED | \ + SO_FILTER_OB_STATE_ACTIVE) #define SO_FILTER_ANY \ (SO_FILTER_NO_OB_CONTENT | SO_FILTER_NO_CHILDREN | SO_FILTER_OB_TYPE | SO_FILTER_OB_STATE | \ @@ -305,8 +313,9 @@ typedef enum eSpaceOutliner_Filter { typedef enum eSpaceOutliner_StateFilter { SO_FILTER_OB_ALL = 0, SO_FILTER_OB_VISIBLE = 1, - SO_FILTER_OB_SELECTED = 2, - SO_FILTER_OB_ACTIVE = 3, + SO_FILTER_OB_INVISIBLE = 2, + SO_FILTER_OB_SELECTED = 3, + SO_FILTER_OB_ACTIVE = 4, } eSpaceOutliner_StateFilter; /* SpaceOutliner.show_restrict_flags */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index b8914c7a74f..3cb96ce8bf8 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -868,6 +868,7 @@ typedef enum eUserPref_Flag { USER_NONEGFRAMES = (1 << 24), USER_TXT_TABSTOSPACES_DISABLE = (1 << 25), USER_TOOLTIPS_PYTHON = (1 << 26), + USER_ADDONS_ENABLED_ONLY = (1 << 27), } eUserPref_Flag; typedef enum eUserPref_PrefFlag { diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 8dcae41aaa2..cacc79608ad 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -131,12 +131,15 @@ typedef struct wmWindowManager { ListBase windows; /** Set on file read. */ - int initialized; + short initialized; /** Indicator whether data was saved. */ short file_saved; /** Operator stack depth to avoid nested undo pushes. */ short op_undo_depth; + /** Set after selection to notify outliner to sync. Stores type of selection */ + short outliner_sync_select_dirty; + /** Operator registry. */ ListBase operators; @@ -186,6 +189,18 @@ enum { WM_KEYCONFIG_IS_INITIALIZED = (1 << 1), }; +/* wmWindowManager.outliner_sync_select_dirty */ +enum { + WM_OUTLINER_SYNC_SELECT_FROM_OBJECT = (1 << 0), + WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE = (1 << 1), + WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE = (1 << 2), + WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE = (1 << 3), +}; + +#define WM_OUTLINER_SYNC_SELECT_FROM_ALL \ + (WM_OUTLINER_SYNC_SELECT_FROM_OBJECT | WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE | \ + WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE | WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE) + #define WM_KEYCONFIG_STR_DEFAULT "blender" /* IME is win32 only! */ diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 5252c8f3350..86ba306fc6a 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -339,7 +339,7 @@ static bool init_structDNA(SDNA *sdna, bool do_endian_swap, const char **r_error data = (int *)sdna->data; - /* clear pointers incase of error */ + /* Clear pointers in case of error. */ sdna->names = NULL; sdna->types = NULL; sdna->structs = NULL; diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 30e24917b83..b0738b617f7 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -953,8 +953,10 @@ bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *v bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop); bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info); bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index); -bool RNA_property_editable_flag(PointerRNA *ptr, - PropertyRNA *prop); /* without lib check, only checks the flag */ + +/* without lib check, only checks the flag */ +bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop); + bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop); bool RNA_property_animated(PointerRNA *ptr, PropertyRNA *prop); bool RNA_property_overridable_get(PointerRNA *ptr, PropertyRNA *prop); diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 39889f77a96..38631d1acf2 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -105,7 +105,7 @@ typedef enum PropertyUnit { #define RNA_STACK_ARRAY 32 /** - * \note Also update enums in bpy_props.c when adding items here. + * \note Also update enums in bpy_props.c and rna_rna.c when adding items here. * Watch it: these values are written to files as part of node socket button subtypes! */ typedef enum PropertySubType { diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 57cdbbadeb8..7996750a1b8 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -642,8 +642,7 @@ static void rna_IDMaterials_append_id(ID *id, Main *bmain, Material *ma) WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, id); } -static Material *rna_IDMaterials_pop_id( - ID *id, Main *bmain, ReportList *reports, int index_i, bool remove_material_slot) +static Material *rna_IDMaterials_pop_id(ID *id, Main *bmain, ReportList *reports, int index_i) { Material *ma; short *totcol = give_totcolp_id(id); @@ -657,7 +656,7 @@ static Material *rna_IDMaterials_pop_id( return NULL; } - ma = BKE_material_pop_id(bmain, id, index_i, remove_material_slot); + ma = BKE_material_pop_id(bmain, id, index_i); if (*totcol == totcol_orig) { BKE_report(reports, RPT_ERROR, "No material to removed"); @@ -671,9 +670,9 @@ static Material *rna_IDMaterials_pop_id( return ma; } -static void rna_IDMaterials_clear_id(ID *id, Main *bmain, bool remove_material_slot) +static void rna_IDMaterials_clear_id(ID *id, Main *bmain) { - BKE_material_clear_id(bmain, id, remove_material_slot); + BKE_material_clear_id(bmain, id); DEG_id_tag_update(id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_OBJECT | ND_DRAW, id); @@ -1116,16 +1115,12 @@ static void rna_def_ID_materials(BlenderRNA *brna) RNA_def_function_ui_description(func, "Remove a material from the data-block"); parm = RNA_def_int( func, "index", -1, -MAXMAT, MAXMAT, "", "Index of material to remove", 0, MAXMAT); - RNA_def_boolean( - func, "update_data", 0, "", "Update data by re-adjusting the material slots assigned"); parm = RNA_def_pointer(func, "material", "Material", "", "Material to remove"); RNA_def_function_return(func, parm); func = RNA_def_function(srna, "clear", "rna_IDMaterials_clear_id"); RNA_def_function_flag(func, FUNC_USE_MAIN); RNA_def_function_ui_description(func, "Remove all materials from the data-block"); - RNA_def_boolean( - func, "update_data", 0, "", "Update data by re-adjusting the material slots assigned"); } static void rna_def_image_preview(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 731f549b497..b78a3304cc7 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1136,12 +1136,34 @@ PropertyType RNA_property_type(PropertyRNA *prop) PropertySubType RNA_property_subtype(PropertyRNA *prop) { - return rna_ensure_property(prop)->subtype; + PropertyRNA *rna_prop = rna_ensure_property(prop); + + /* For custom properties, find and parse the 'subtype' metadata field. */ + if (prop->magic != RNA_MAGIC) { + IDProperty *idprop = (IDProperty *)prop; + + /* Restrict to arrays only for now for performance reasons. */ + if (idprop->type == IDP_ARRAY && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) { + IDProperty *idp_ui = rna_idproperty_ui(prop); + + if (idp_ui) { + IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "subtype", IDP_STRING); + + if (item) { + int result = PROP_NONE; + RNA_enum_value_from_id(rna_enum_property_subtype_items, IDP_String(item), &result); + return (PropertySubType)result; + } + } + } + } + + return rna_prop->subtype; } PropertyUnit RNA_property_unit(PropertyRNA *prop) { - return RNA_SUBTYPE_UNIT(rna_ensure_property(prop)->subtype); + return RNA_SUBTYPE_UNIT(RNA_property_subtype(prop)); } int RNA_property_flag(PropertyRNA *prop) @@ -1212,7 +1234,7 @@ char RNA_property_array_item_char(PropertyRNA *prop, int index) const char *vectoritem = "XYZW"; const char *quatitem = "WXYZ"; const char *coloritem = "RGBA"; - PropertySubType subtype = rna_ensure_property(prop)->subtype; + PropertySubType subtype = RNA_property_subtype(prop); BLI_assert(index >= 0); @@ -1240,6 +1262,7 @@ char RNA_property_array_item_char(PropertyRNA *prop, int index) int RNA_property_array_item_index(PropertyRNA *prop, char name) { + /* Don't use custom property subtypes in RNA path lookup. */ PropertySubType subtype = rna_ensure_property(prop)->subtype; /* get index based on string name/alias */ @@ -2522,20 +2545,33 @@ void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value) } } -static void rna_property_boolean_get_default_array_values(BoolPropertyRNA *bprop, bool *values) +static void rna_property_boolean_fill_default_array_values( + const bool *defarr, int defarr_length, bool defvalue, int out_length, bool *r_values) { - unsigned int length = bprop->property.totarraylength; - - if (bprop->defaultarray) { - memcpy(values, bprop->defaultarray, sizeof(bool) * length); + if (defarr && defarr_length > 0) { + defarr_length = MIN2(defarr_length, out_length); + memcpy(r_values, defarr, sizeof(bool) * defarr_length); } else { - for (unsigned int i = 0; i < length; i++) { - values[i] = bprop->defaultvalue; - } + defarr_length = 0; + } + + for (int i = defarr_length; i < out_length; i++) { + r_values[i] = defvalue; } } +static void rna_property_boolean_get_default_array_values(PointerRNA *ptr, + BoolPropertyRNA *bprop, + bool *r_values) +{ + int length = bprop->property.totarraylength; + int out_length = RNA_property_array_length(ptr, (PropertyRNA *)bprop); + + rna_property_boolean_fill_default_array_values( + bprop->defaultarray, length, bprop->defaultvalue, out_length, r_values); +} + void RNA_property_boolean_get_array(PointerRNA *ptr, PropertyRNA *prop, bool *values) { BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop; @@ -2565,7 +2601,7 @@ void RNA_property_boolean_get_array(PointerRNA *ptr, PropertyRNA *prop, bool *va bprop->getarray_ex(ptr, prop, values); } else { - rna_property_boolean_get_default_array_values(bprop, values); + rna_property_boolean_get_default_array_values(ptr, bprop, values); } } @@ -2684,9 +2720,7 @@ bool RNA_property_boolean_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop return bprop->defaultvalue; } -void RNA_property_boolean_get_default_array(PointerRNA *UNUSED(ptr), - PropertyRNA *prop, - bool *values) +void RNA_property_boolean_get_default_array(PointerRNA *ptr, PropertyRNA *prop, bool *values) { BoolPropertyRNA *bprop = (BoolPropertyRNA *)rna_ensure_property(prop); @@ -2697,7 +2731,7 @@ void RNA_property_boolean_get_default_array(PointerRNA *UNUSED(ptr), values[0] = bprop->defaultvalue; } else { - rna_property_boolean_get_default_array_values(bprop, values); + rna_property_boolean_get_default_array_values(ptr, bprop, values); } } @@ -2709,7 +2743,7 @@ bool RNA_property_boolean_get_default_index(PointerRNA *ptr, PropertyRNA *prop, BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN); BLI_assert(RNA_property_array_check(prop) != false); BLI_assert(index >= 0); - BLI_assert(index < prop->totarraylength); + BLI_assert(index < len); if (len <= RNA_MAX_ARRAY_LENGTH) { RNA_property_boolean_get_default_array(ptr, prop, tmp); @@ -2785,20 +2819,33 @@ void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value) } } -static void rna_property_int_get_default_array_values(IntPropertyRNA *iprop, int *values) +static void rna_property_int_fill_default_array_values( + const int *defarr, int defarr_length, int defvalue, int out_length, int *r_values) { - unsigned int length = iprop->property.totarraylength; - - if (iprop->defaultarray) { - memcpy(values, iprop->defaultarray, sizeof(int) * length); + if (defarr && defarr_length > 0) { + defarr_length = MIN2(defarr_length, out_length); + memcpy(r_values, defarr, sizeof(int) * defarr_length); } else { - for (unsigned int i = 0; i < length; i++) { - values[i] = iprop->defaultvalue; - } + defarr_length = 0; + } + + for (int i = defarr_length; i < out_length; i++) { + r_values[i] = defvalue; } } +static void rna_property_int_get_default_array_values(PointerRNA *ptr, + IntPropertyRNA *iprop, + int *r_values) +{ + int length = iprop->property.totarraylength; + int out_length = RNA_property_array_length(ptr, (PropertyRNA *)iprop); + + rna_property_int_fill_default_array_values( + iprop->defaultarray, length, iprop->defaultvalue, out_length, r_values); +} + void RNA_property_int_get_array(PointerRNA *ptr, PropertyRNA *prop, int *values) { IntPropertyRNA *iprop = (IntPropertyRNA *)prop; @@ -2827,7 +2874,7 @@ void RNA_property_int_get_array(PointerRNA *ptr, PropertyRNA *prop, int *values) iprop->getarray_ex(ptr, prop, values); } else { - rna_property_int_get_default_array_values(iprop, values); + rna_property_int_get_default_array_values(ptr, iprop, values); } } @@ -2999,18 +3046,34 @@ bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value) } } -void RNA_property_int_get_default_array(PointerRNA *UNUSED(ptr), PropertyRNA *prop, int *values) +void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int *values) { IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop); BLI_assert(RNA_property_type(prop) == PROP_INT); BLI_assert(RNA_property_array_check(prop) != false); - if (prop->arraydimension == 0) { + if (prop->magic != RNA_MAGIC) { + int length = rna_ensure_property_array_length(ptr, prop); + + IDProperty *idp_ui = rna_idproperty_ui(prop); + IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL; + + int defval = (item && item->type == IDP_INT) ? IDP_Int(item) : iprop->defaultvalue; + + if (item && item->type == IDP_ARRAY && item->subtype == IDP_INT) { + rna_property_int_fill_default_array_values( + IDP_Array(item), item->len, defval, length, values); + } + else { + rna_property_int_fill_default_array_values(NULL, 0, defval, length, values); + } + } + else if (prop->arraydimension == 0) { values[0] = iprop->defaultvalue; } else { - rna_property_int_get_default_array_values(iprop, values); + rna_property_int_get_default_array_values(ptr, iprop, values); } } @@ -3022,7 +3085,7 @@ int RNA_property_int_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int i BLI_assert(RNA_property_type(prop) == PROP_INT); BLI_assert(RNA_property_array_check(prop) != false); BLI_assert(index >= 0); - BLI_assert(index < prop->totarraylength); + BLI_assert(index < len); if (len <= RNA_MAX_ARRAY_LENGTH) { RNA_property_int_get_default_array(ptr, prop, tmp); @@ -3109,20 +3172,33 @@ void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value) } } -static void rna_property_float_get_default_array_values(FloatPropertyRNA *fprop, float *values) +static void rna_property_float_fill_default_array_values( + const float *defarr, int defarr_length, float defvalue, int out_length, float *r_values) { - unsigned int length = fprop->property.totarraylength; - - if (fprop->defaultarray) { - memcpy(values, fprop->defaultarray, sizeof(float) * length); + if (defarr && defarr_length > 0) { + defarr_length = MIN2(defarr_length, out_length); + memcpy(r_values, defarr, sizeof(float) * defarr_length); } else { - for (unsigned int i = 0; i < length; i++) { - values[i] = fprop->defaultvalue; - } + defarr_length = 0; + } + + for (int i = defarr_length; i < out_length; i++) { + r_values[i] = defvalue; } } +static void rna_property_float_get_default_array_values(PointerRNA *ptr, + FloatPropertyRNA *fprop, + float *r_values) +{ + int length = fprop->property.totarraylength; + int out_length = RNA_property_array_length(ptr, (PropertyRNA *)fprop); + + rna_property_float_fill_default_array_values( + fprop->defaultarray, length, fprop->defaultvalue, out_length, r_values); +} + void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values) { FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop; @@ -3157,7 +3233,7 @@ void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *val fprop->getarray_ex(ptr, prop, values); } else { - rna_property_float_get_default_array_values(fprop, values); + rna_property_float_get_default_array_values(ptr, fprop, values); } } @@ -3343,20 +3419,40 @@ bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float va } } -void RNA_property_float_get_default_array(PointerRNA *UNUSED(ptr), - PropertyRNA *prop, - float *values) +void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values) { FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop); BLI_assert(RNA_property_type(prop) == PROP_FLOAT); BLI_assert(RNA_property_array_check(prop) != false); - if (prop->arraydimension == 0) { + if (prop->magic != RNA_MAGIC) { + int length = rna_ensure_property_array_length(ptr, prop); + + IDProperty *idp_ui = rna_idproperty_ui(prop); + IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL; + + float defval = (item && item->type == IDP_DOUBLE) ? IDP_Double(item) : fprop->defaultvalue; + + if (item && item->type == IDP_ARRAY && item->subtype == IDP_DOUBLE) { + double *defarr = IDP_Array(item); + for (int i = 0; i < length; i++) { + values[i] = (i < item->len) ? (float)defarr[i] : defval; + } + } + else if (item && item->type == IDP_ARRAY && item->subtype == IDP_FLOAT) { + rna_property_float_fill_default_array_values( + IDP_Array(item), item->len, defval, length, values); + } + else { + rna_property_float_fill_default_array_values(NULL, 0, defval, length, values); + } + } + else if (prop->arraydimension == 0) { values[0] = fprop->defaultvalue; } else { - rna_property_float_get_default_array_values(fprop, values); + rna_property_float_get_default_array_values(ptr, fprop, values); } } @@ -3368,7 +3464,7 @@ float RNA_property_float_get_default_index(PointerRNA *ptr, PropertyRNA *prop, i BLI_assert(RNA_property_type(prop) == PROP_FLOAT); BLI_assert(RNA_property_array_check(prop) != false); BLI_assert(index >= 0); - BLI_assert(index < prop->totarraylength); + BLI_assert(index < len); if (len <= RNA_MAX_ARRAY_LENGTH) { RNA_property_float_get_default_array(ptr, prop, tmp); @@ -6381,8 +6477,8 @@ char *RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, in PropertyRNA *prop = RNA_struct_find_property(ptr, name); if (prop) { - return RNA_property_string_get_alloc( - ptr, prop, fixedbuf, fixedlen, NULL); /* TODO, pass length */ + /* TODO, pass length */ + return RNA_property_string_get_alloc(ptr, prop, fixedbuf, fixedlen, NULL); } else { printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); @@ -7083,6 +7179,7 @@ ParameterList *RNA_parameter_list_create(ParameterList *parms, FunctionRNA *func) { PropertyRNA *parm; + PointerRNA null_ptr = PointerRNA_NULL; void *data; int alloc_size = 0, size; @@ -7122,7 +7219,8 @@ ParameterList *RNA_parameter_list_create(ParameterList *parms, switch (parm->type) { case PROP_BOOLEAN: if (parm->arraydimension) { - rna_property_boolean_get_default_array_values((BoolPropertyRNA *)parm, data); + rna_property_boolean_get_default_array_values( + &null_ptr, (BoolPropertyRNA *)parm, data); } else { memcpy(data, &((BoolPropertyRNA *)parm)->defaultvalue, size); @@ -7130,7 +7228,7 @@ ParameterList *RNA_parameter_list_create(ParameterList *parms, break; case PROP_INT: if (parm->arraydimension) { - rna_property_int_get_default_array_values((IntPropertyRNA *)parm, data); + rna_property_int_get_default_array_values(&null_ptr, (IntPropertyRNA *)parm, data); } else { memcpy(data, &((IntPropertyRNA *)parm)->defaultvalue, size); @@ -7138,7 +7236,7 @@ ParameterList *RNA_parameter_list_create(ParameterList *parms, break; case PROP_FLOAT: if (parm->arraydimension) { - rna_property_float_get_default_array_values((FloatPropertyRNA *)parm, data); + rna_property_float_get_default_array_values(&null_ptr, (FloatPropertyRNA *)parm, data); } else { memcpy(data, &((FloatPropertyRNA *)parm)->defaultvalue, size); @@ -8762,6 +8860,80 @@ static void rna_property_override_apply_ex(Main *bmain, } continue; } + + /* Note: will have to think about putting that logic into its own function maybe? + * Would be nice to have it in a single place... */ + PointerRNA private_ptr_item_local, private_ptr_item_override, private_ptr_item_storage; + if (opop->subitem_local_name != NULL || opop->subitem_reference_name != NULL || + opop->subitem_local_index != -1 || opop->subitem_reference_index != -1) { + RNA_POINTER_INVALIDATE(&private_ptr_item_local); + RNA_POINTER_INVALIDATE(&private_ptr_item_override); + RNA_POINTER_INVALIDATE(&private_ptr_item_storage); + if (opop->subitem_local_name != NULL) { + RNA_property_collection_lookup_string( + ptr_local, prop_local, opop->subitem_local_name, &private_ptr_item_local); + if (opop->subitem_reference_name != NULL) { + RNA_property_collection_lookup_string(ptr_override, + prop_override, + opop->subitem_reference_name, + &private_ptr_item_override); + } + else { + RNA_property_collection_lookup_string( + ptr_override, prop_override, opop->subitem_local_name, &private_ptr_item_override); + } + } + else if (opop->subitem_reference_name != NULL) { + RNA_property_collection_lookup_string( + ptr_local, prop_local, opop->subitem_reference_name, &private_ptr_item_local); + RNA_property_collection_lookup_string( + ptr_override, prop_override, opop->subitem_reference_name, &private_ptr_item_override); + } + else if (opop->subitem_local_index != -1) { + RNA_property_collection_lookup_int( + ptr_local, prop_local, opop->subitem_local_index, &private_ptr_item_local); + if (opop->subitem_reference_index != -1) { + RNA_property_collection_lookup_int(ptr_override, + prop_override, + opop->subitem_reference_index, + &private_ptr_item_override); + } + else { + RNA_property_collection_lookup_int( + ptr_override, prop_override, opop->subitem_local_index, &private_ptr_item_override); + } + } + else if (opop->subitem_reference_index != -1) { + RNA_property_collection_lookup_int( + ptr_local, prop_local, opop->subitem_reference_index, &private_ptr_item_local); + RNA_property_collection_lookup_int(ptr_override, + prop_override, + opop->subitem_reference_index, + &private_ptr_item_override); + } + if (prop_storage != NULL) { + if (opop->subitem_local_name != NULL) { + RNA_property_collection_lookup_string( + ptr_storage, prop_storage, opop->subitem_local_name, &private_ptr_item_storage); + } + else if (opop->subitem_reference_name != NULL) { + RNA_property_collection_lookup_string( + ptr_storage, prop_storage, opop->subitem_reference_name, &private_ptr_item_storage); + } + else if (opop->subitem_local_index != -1) { + RNA_property_collection_lookup_int( + ptr_storage, prop_storage, opop->subitem_local_index, &private_ptr_item_storage); + } + else if (opop->subitem_reference_index != -1) { + RNA_property_collection_lookup_int( + ptr_storage, prop_storage, opop->subitem_reference_index, &private_ptr_item_storage); + } + } + ptr_item_local = &private_ptr_item_local; + ptr_item_override = &private_ptr_item_override; + ptr_item_storage = &private_ptr_item_storage; + } + if (!rna_property_override_operation_apply(bmain, ptr_local, ptr_override, diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 6737363bae4..59e5c584811 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -922,13 +922,13 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) prop = RNA_def_property(srna, "bbone_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "xwidth"); - RNA_def_property_range(prop, 0.0f, 1000.0f); + RNA_def_property_ui_range(prop, 0.0f, 1000.0f, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text(prop, "B-Bone Display X Width", "B-Bone X size"); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); prop = RNA_def_property(srna, "bbone_z", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "zwidth"); - RNA_def_property_range(prop, 0.0f, 1000.0f); + RNA_def_property_ui_range(prop, 0.0f, 1000.0f, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text(prop, "B-Bone Display Z Width", "B-Bone Z size"); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c index 5a37c4c0e6f..4700df5352f 100644 --- a/source/blender/makesrna/intern/rna_collection.c +++ b/source/blender/makesrna/intern/rna_collection.c @@ -130,9 +130,9 @@ static bool rna_Collection_objects_override_apply(Main *bmain, PointerRNA *UNUSED(ptr_item_storage), IDOverrideLibraryPropertyOperation *opop) { - (void)opop; BLI_assert(opop->operation == IDOVERRIDE_LIBRARY_OP_REPLACE && "Unsupported RNA override operation on collections' objects"); + UNUSED_VARS_NDEBUG(opop); Collection *coll_dst = ptr_dst->id.data; @@ -233,9 +233,9 @@ static bool rna_Collection_children_override_apply(Main *bmain, PointerRNA *UNUSED(ptr_item_storage), IDOverrideLibraryPropertyOperation *opop) { - (void)opop; BLI_assert(opop->operation == IDOVERRIDE_LIBRARY_OP_REPLACE && "Unsupported RNA override operation on collections' objects"); + UNUSED_VARS_NDEBUG(opop); Collection *coll_dst = ptr_dst->id.data; @@ -403,6 +403,7 @@ void RNA_def_collections(BlenderRNA *brna) RNA_def_property_struct_type(prop, "Object"); RNA_def_property_ui_text( prop, "All Objects", "Objects that are in this collection and its child collections"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_collection_funcs(prop, "rna_Collection_all_objects_begin", "rna_iterator_listbase_next", diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 6e57d16df27..68a5a01dab8 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -1009,7 +1009,7 @@ static void rna_def_constraint_armature_deform(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "Armature Constraint", "Applies transformations done by the Armature modifier"); RNA_def_struct_sdna_from(srna, "bArmatureConstraint", "data"); - RNA_def_struct_ui_icon(srna, ICON_MOD_ARMATURE); + RNA_def_struct_ui_icon(srna, ICON_CON_ARMATURE); prop = RNA_def_property(srna, "targets", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "targets", NULL); @@ -3026,6 +3026,7 @@ void RNA_def_constraint(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Disable", "Enable/Disable Constraint"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1); prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 310a335ba4c..644d0b712a2 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1215,8 +1215,8 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna) RNA_def_property_ui_text(prop, "Body Text", "Content of this text object"); RNA_def_property_string_funcs( prop, "rna_Curve_body_get", "rna_Curve_body_length", "rna_Curve_body_set"); - RNA_def_property_string_maxlength(prop, - 8192); /* note that originally str did not have a limit! */ + /* note that originally str did not have a limit! */ + RNA_def_property_string_maxlength(prop, 8192); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "body_format", PROP_COLLECTION, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 2cb7c62b028..03ab9da2eff 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -724,8 +724,10 @@ void RNA_struct_free_extension(StructRNA *srna, ExtensionRNA *ext) #ifdef RNA_RUNTIME ext->free(ext->data); /* decref's the PyObject that the srna owns */ RNA_struct_blender_type_set(srna, NULL); /* this gets accessed again - XXX fixme */ - RNA_struct_py_type_set( - srna, NULL); /* NULL the srna's value so RNA_struct_free wont complain of a leak */ + + /* NULL the srna's value so RNA_struct_free wont complain of a leak */ + RNA_struct_py_type_set(srna, NULL); + #else (void)srna; (void)ext; diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index dfe9e018e94..ea343406c81 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -611,22 +611,22 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) "FIXED", ICON_IPO_CONSTANT, "Fixed", - "Delete alternative vertices in the stroke, except extremes"}, + "Delete alternating vertices in the stroke, except extremes"}, {GP_SIMPLIFY_ADAPTIVE, "ADAPTIVE", ICON_IPO_EASE_IN_OUT, "Adaptive", - "Use a RDP algorithm to simplify"}, + "Use a RDP algorithm to simplify the stroke"}, {GP_SIMPLIFY_SAMPLE, "SAMPLE", ICON_IPO_EASE_IN_OUT, "Sample", - "Sample a curve using a fixed length"}, + "Resample the stroke with segments of the specified length"}, {GP_SIMPLIFY_MERGE, "MERGE", ICON_IPO_EASE_IN_OUT, "Merge", - "Sample a curve using doing merge of vertex"}, + "Simplify the stroke by merging vertices closer than a given distance"}, {0, NULL, 0, NULL, NULL}, }; @@ -689,9 +689,16 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) /* Sample */ prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "length"); - RNA_def_property_range(prop, 0, 10); + RNA_def_property_range(prop, 0, 10.0f); RNA_def_property_ui_text(prop, "Length", "Length of each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Distance */ + prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "distance"); + RNA_def_property_range(prop, 0, 100.0f); + RNA_def_property_ui_text(prop, "Distance", "Distance between points"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } static void rna_def_modifier_gpencilthick(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 6378ee15279..8bfc4bf6313 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -719,16 +719,6 @@ void RNA_def_material(BlenderRNA *brna) static EnumPropertyItem prop_eevee_blend_items[] = { {MA_BM_SOLID, "OPAQUE", 0, "Opaque", "Render surface without transparency"}, - {MA_BM_ADD, - "ADD", - 0, - "Additive", - "Render surface and blend the result with additive blending"}, - {MA_BM_MULTIPLY, - "MULTIPLY", - 0, - "Multiply", - "Render surface and blend the result with multiplicative blending"}, {MA_BM_CLIP, "CLIP", 0, diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 03173bcb3da..966dc071641 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -2998,6 +2998,30 @@ static void rna_def_mesh(BlenderRNA *brna) rna_def_paint_mask(brna, prop); /* End paint mask */ + /* Remesh */ + prop = RNA_def_property(srna, "remesh_voxel_size", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "remesh_voxel_size"); + RNA_def_property_float_default(prop, 0.1f); + RNA_def_property_range(prop, 0.00001f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0001f, FLT_MAX, 0.01, 4); + RNA_def_property_ui_text(prop, + "Voxel size", + "Size of the voxel in object space used for volume evaluation. Lower " + "values preserve finer details"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + + prop = RNA_def_property(srna, "remesh_smooth_normals", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_SMOOTH_NORMALS); + RNA_def_property_ui_text(prop, "Smooth normals", "Smooth the normals of the remesher result"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + + prop = RNA_def_property(srna, "remesh_preserve_paint_mask", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_PAINT_MASK); + RNA_def_property_boolean_default(prop, false); + RNA_def_property_ui_text(prop, "Preserve Paint Mask", "Keep the current mask on the new mesh"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + /* End remesh */ + prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_AUTOSMOOTH); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b28403bf28c..a0c00db1a1e 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -100,34 +100,38 @@ static const EnumPropertyItem node_chunksize_items[] = { #endif const EnumPropertyItem rna_enum_node_math_items[] = { - {NODE_MATH_ADD, "ADD", 0, "Add", ""}, - {NODE_MATH_SUB, "SUBTRACT", 0, "Subtract", ""}, - {NODE_MATH_MUL, "MULTIPLY", 0, "Multiply", ""}, - {NODE_MATH_DIVIDE, "DIVIDE", 0, "Divide", ""}, + {NODE_MATH_ADD, "ADD", 0, "Add", "A + B"}, + {NODE_MATH_SUBTRACT, "SUBTRACT", 0, "Subtract", "A - B"}, + {NODE_MATH_MULTIPLY, "MULTIPLY", 0, "Multiply", "A * B"}, + {NODE_MATH_DIVIDE, "DIVIDE", 0, "Divide", "A / B"}, {0, "", ICON_NONE, NULL, NULL}, - {NODE_MATH_POW, "POWER", 0, "Power", ""}, - {NODE_MATH_LOG, "LOGARITHM", 0, "Logarithm", ""}, - {NODE_MATH_SQRT, "SQRT", 0, "Square Root", ""}, - {NODE_MATH_ABS, "ABSOLUTE", 0, "Absolute", ""}, + {NODE_MATH_POWER, "POWER", 0, "Power", "A power B"}, + {NODE_MATH_LOGARITHM, "LOGARITHM", 0, "Logarithm", "Logarithm A base B"}, + {NODE_MATH_SQRT, "SQRT", 0, "Square Root", "Square root of A"}, + {NODE_MATH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Magnitude of A"}, {0, "", ICON_NONE, NULL, NULL}, - {NODE_MATH_MIN, "MINIMUM", 0, "Minimum", ""}, - {NODE_MATH_MAX, "MAXIMUM", 0, "Maximum", ""}, - {NODE_MATH_LESS, "LESS_THAN", 0, "Less Than", ""}, - {NODE_MATH_GREATER, "GREATER_THAN", 0, "Greater Than", ""}, + {NODE_MATH_MINIMUM, "MINIMUM", 0, "Minimum", "The minimum from A and B"}, + {NODE_MATH_MAXIMUM, "MAXIMUM", 0, "Maximum", "The maximum from A and B"}, + {NODE_MATH_LESS_THAN, "LESS_THAN", 0, "Less Than", "1 if A < B else 0"}, + {NODE_MATH_GREATER_THAN, "GREATER_THAN", 0, "Greater Than", "1 if A > B else 0"}, {0, "", ICON_NONE, NULL, NULL}, - {NODE_MATH_ROUND, "ROUND", 0, "Round", ""}, - {NODE_MATH_FLOOR, "FLOOR", 0, "Floor", ""}, - {NODE_MATH_CEIL, "CEIL", 0, "Ceil", ""}, - {NODE_MATH_FRACT, "FRACT", 0, "Fract", ""}, - {NODE_MATH_MOD, "MODULO", 0, "Modulo", ""}, + {NODE_MATH_ROUND, + "ROUND", + 0, + "Round", + "Round A to the nearest integer. Round upward if the fraction part is 0.5"}, + {NODE_MATH_FLOOR, "FLOOR", 0, "Floor", "The largest integer smaller than or equal A"}, + {NODE_MATH_CEIL, "CEIL", 0, "Ceil", "The smallest integer greater than or equal A"}, + {NODE_MATH_FRACTION, "FRACT", 0, "Fraction", "The fraction part of A"}, + {NODE_MATH_MODULO, "MODULO", 0, "Modulo", "A mod B"}, {0, "", ICON_NONE, NULL, NULL}, - {NODE_MATH_SIN, "SINE", 0, "Sine", ""}, - {NODE_MATH_COS, "COSINE", 0, "Cosine", ""}, - {NODE_MATH_TAN, "TANGENT", 0, "Tangent", ""}, - {NODE_MATH_ASIN, "ARCSINE", 0, "Arcsine", ""}, - {NODE_MATH_ACOS, "ARCCOSINE", 0, "Arccosine", ""}, - {NODE_MATH_ATAN, "ARCTANGENT", 0, "Arctangent", ""}, - {NODE_MATH_ATAN2, "ARCTAN2", 0, "Arctan2", ""}, + {NODE_MATH_SINE, "SINE", 0, "Sine", "sin(A)"}, + {NODE_MATH_COSINE, "COSINE", 0, "Cosine", "cos(A)"}, + {NODE_MATH_TANGENT, "TANGENT", 0, "Tangent", "tan(A)"}, + {NODE_MATH_ARCSINE, "ARCSINE", 0, "Arcsine", "arcsin(A)"}, + {NODE_MATH_ARCCOSINE, "ARCCOSINE", 0, "Arccosine", "arccos(A)"}, + {NODE_MATH_ARCTANGENT, "ARCTANGENT", 0, "Arctangent", "arctan(A)"}, + {NODE_MATH_ARCTAN2, "ARCTAN2", 0, "Arctan2", "The signed angle arctan(A / B)"}, {0, NULL, 0, NULL, NULL}, }; @@ -3834,6 +3838,16 @@ static void def_frame(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, NULL); } +static void def_map_range(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "clamp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "custom1", 1); + RNA_def_property_ui_text(prop, "Clamp", "Clamp the result to the target range [To Min, To Max]"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_math(StructRNA *srna) { PropertyRNA *prop; @@ -3842,7 +3856,7 @@ static void def_math(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, rna_enum_node_math_items); RNA_def_property_ui_text(prop, "Operation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "custom2", SHD_MATH_CLAMP); @@ -7590,6 +7604,18 @@ static void def_cmp_cryptomatte(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeCryptomatte_update_remove"); } +static void def_cmp_denoise(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeDenoise", "storage"); + + prop = RNA_def_property(srna, "use_hdr", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "hdr", 0); + RNA_def_property_ui_text(prop, "HDR", "Process HDR images"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -- Texture Nodes --------------------------------------------------------- */ static void def_tex_output(StructRNA *srna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 6069e78d874..bc433691cb4 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -546,6 +546,46 @@ static void rna_Object_parent_set(PointerRNA *ptr, } } +bool rna_Object_parent_override_apply(Main *UNUSED(bmain), + PointerRNA *ptr_dst, + PointerRNA *ptr_src, + PointerRNA *ptr_storage, + PropertyRNA *prop_dst, + PropertyRNA *prop_src, + PropertyRNA *UNUSED(prop_storage), + const int len_dst, + const int len_src, + const int len_storage, + PointerRNA *UNUSED(ptr_item_dst), + PointerRNA *UNUSED(ptr_item_src), + PointerRNA *UNUSED(ptr_item_storage), + IDOverrideLibraryPropertyOperation *opop) +{ + BLI_assert(len_dst == len_src && (!ptr_storage || len_dst == len_storage) && len_dst == 0); + BLI_assert(opop->operation == IDOVERRIDE_LIBRARY_OP_REPLACE && + "Unsupported RNA override operation on animdata pointer"); + UNUSED_VARS_NDEBUG(ptr_storage, len_dst, len_src, len_storage, opop); + + /* We need a special handling here because setting parent resets pinvert parent matrix, + * which is evil in our case. */ + Object *ob = (Object *)ptr_dst->data; + Object *parent_dst = RNA_property_pointer_get(ptr_dst, prop_dst).data; + Object *parent_src = RNA_property_pointer_get(ptr_src, prop_src).data; + + if (parent_src == parent_dst) { + return false; + } + + if (parent_src == NULL) { + /* The only case where we do want default behavior (with matrix reset). */ + ED_object_parent(ob, parent_src, ob->partype, ob->parsubstr); + } + else { + ob->parent = parent_src; + } + return true; +} + static void rna_Object_parent_type_set(PointerRNA *ptr, int value) { Object *ob = (Object *)ptr->data; @@ -1392,8 +1432,8 @@ bool rna_Object_constraints_override_apply(Main *UNUSED(bmain), Object *ob_src = (Object *)ptr_src->id.data; /* Remember that insertion operations are defined and stored in correct order, which means that - * even if we insert several items in a row, we alays insert first one, then second one, etc. - * So we should always find 'anchor' constraint in both _src *and* _dst> */ + * even if we insert several items in a row, we always insert first one, then second one, etc. + * So we should always find 'anchor' constraint in both _src *and* _dst. */ bConstraint *con_anchor = NULL; if (opop->subitem_local_name && opop->subitem_local_name[0]) { con_anchor = BLI_findstring( @@ -1480,8 +1520,8 @@ bool rna_Object_modifiers_override_apply(Main *UNUSED(bmain), Object *ob_src = (Object *)ptr_src->id.data; /* Remember that insertion operations are defined and stored in correct order, which means that - * even if we insert several items in a row, we alays insert first one, then second one, etc. - * So we should always find 'anchor' constraint in both _src *and* _dst> */ + * even if we insert several items in a row, we always insert first one, then second one, etc. + * So we should always find 'anchor' constraint in both _src *and* _dst. */ ModifierData *mod_anchor = NULL; if (opop->subitem_local_name && opop->subitem_local_name[0]) { mod_anchor = BLI_findstring( @@ -2389,6 +2429,7 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_pointer_funcs(prop, NULL, "rna_Object_parent_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_override_funcs(prop, NULL, NULL, "rna_Object_parent_override_apply"); RNA_def_property_ui_text(prop, "Parent", "Parent Object"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_dependency_update"); diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index ab6cffe615d..4dd46281a42 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -140,7 +140,7 @@ static void rna_Cache_change(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR if (pid.type == PTCACHE_TYPE_SMOKE_DOMAIN) { cache->step = 1; } - BKE_ptcache_update_info(&pid); + cache->flag |= PTCACHE_FLAG_INFO_DIRTY; } } @@ -292,6 +292,24 @@ static void rna_PointCache_frame_step_range( } } +int rna_Cache_info_length(PointerRNA *ptr) +{ + PointCache *cache = (PointCache *)ptr->data; + Object *ob = (Object *)ptr->id.data; + + if (!ob) { + return 0; + } + + PTCacheID pid = BKE_ptcache_id_find(ob, NULL, cache); + + if (cache->flag & PTCACHE_FLAG_INFO_DIRTY) { + BKE_ptcache_update_info(&pid); + } + + return (int)strlen(cache->info); +} + static char *rna_CollisionSettings_path(PointerRNA *UNUSED(ptr)) { /* both methods work ok, but return the shorter path */ @@ -870,6 +888,11 @@ static void rna_def_pointcache_common(StructRNA *srna) prop = RNA_def_property(srna, "info", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "info"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + /* Note that we do not actually need a getter here, `rna_Cache_info_length` will update the info + * string just as well. */ + RNA_def_property_string_funcs(prop, NULL, "rna_Cache_info_length", NULL); + RNA_def_property_string_maxlength( + prop, sizeof(((PointCache *)0)->info) / sizeof(*(((PointCache *)0)->info))); RNA_def_property_ui_text(prop, "Cache Info", "Info on current cache status"); prop = RNA_def_property(srna, "use_external", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 4c50e0c19ae..86ce0ade96b 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -449,8 +449,10 @@ static int rna_ParticleSystem_tessfaceidx_on_emitter(ParticleSystem *particlesys } part = particlesystem->part; - totpart = particlesystem->totcached; - totchild = particlesystem->totchildcache; + /* Note: only hair, keyed and baked particles may have cached items... */ + totpart = particlesystem->totcached != 0 ? particlesystem->totcached : particlesystem->totpart; + totchild = particlesystem->totchildcache != 0 ? particlesystem->totchildcache : + particlesystem->totchild; /* can happen for disconnected/global hair */ if (part->type == PART_HAIR && !particlesystem->childcache) { diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index f17e9ce28fe..cb075884915 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -1241,6 +1241,10 @@ static int rna_property_override_diff_propptr(Main *bmain, const bool no_prop_name, IDOverrideLibrary *override, const char *rna_path, + const char *rna_itemname_a, + const char *rna_itemname_b, + const int rna_itemindex_a, + const int rna_itemindex_b, const int flags, bool *r_override_changed) { @@ -1282,9 +1286,18 @@ static int rna_property_override_diff_propptr(Main *bmain, IDOverrideLibraryProperty *op = BKE_override_library_property_get( override, rna_path, &created); - if (op != NULL && created) { /* If not yet overridden... */ - BKE_override_library_property_operation_get( - op, IDOVERRIDE_LIBRARY_OP_REPLACE, NULL, NULL, -1, -1, true, NULL, NULL); + /* If not yet overridden, or if we are handling sub-items (inside a collection)... */ + if (op != NULL && (created || rna_itemname_a != NULL || rna_itemname_b != NULL || + rna_itemindex_a != -1 || rna_itemindex_b != -1)) { + BKE_override_library_property_operation_get(op, + IDOVERRIDE_LIBRARY_OP_REPLACE, + rna_itemname_b, + rna_itemname_a, + rna_itemindex_b, + rna_itemindex_a, + true, + NULL, + &created); if (r_override_changed) { *r_override_changed = created; } @@ -1294,12 +1307,56 @@ static int rna_property_override_diff_propptr(Main *bmain, return comp; } else { + /* In case we got some array/collection like items identifiers, now is the time to generate a + * proper rna path from those. */ +# define RNA_PATH_BUFFSIZE 8192 + + char extended_rna_path_buffer[RNA_PATH_BUFFSIZE]; + char *extended_rna_path = extended_rna_path_buffer; + +# define RNA_PATH_PRINTF(_str, ...) \ + if (BLI_snprintf(extended_rna_path_buffer, RNA_PATH_BUFFSIZE, (_str), __VA_ARGS__) >= \ + RNA_PATH_BUFFSIZE - 1) { \ + extended_rna_path = BLI_sprintfN((_str), __VA_ARGS__); \ + } \ + (void)0 +# define RNA_PATH_FREE() \ + if (extended_rna_path != extended_rna_path_buffer && extended_rna_path != rna_path) \ + MEM_freeN(extended_rna_path) + + /* There may be a propname defined in some cases, while no actual name set + * (e.g. happens with point cache), in that case too we want to fall back to index. + * Note that we do not need the RNA path for insertion operations. */ + if (rna_path) { + if ((rna_itemname_a != NULL && rna_itemname_a[0] != '\0') && + (rna_itemname_b != NULL && rna_itemname_b[0] != '\0')) { + BLI_assert(STREQ(rna_itemname_a, rna_itemname_b)); + char esc_item_name[RNA_PATH_BUFFSIZE]; + BLI_strescape(esc_item_name, rna_itemname_a, RNA_PATH_BUFFSIZE); + RNA_PATH_PRINTF("%s[\"%s\"]", rna_path, esc_item_name); + } + else if (rna_itemindex_a != -1) { /* Based on index... */ + BLI_assert(rna_itemindex_a == rna_itemindex_b); + RNA_PATH_PRINTF("%s[%d]", rna_path, rna_itemindex_a); + } + else { + extended_rna_path = (char *)rna_path; + } + } + eRNAOverrideMatchResult report_flags = 0; const bool match = RNA_struct_override_matches( - bmain, propptr_a, propptr_b, rna_path, override, flags, &report_flags); + bmain, propptr_a, propptr_b, extended_rna_path, override, flags, &report_flags); if (r_override_changed && (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) != 0) { *r_override_changed = true; } + + RNA_PATH_FREE(); + +# undef RNA_PATH_BUFFSIZE +# undef RNA_PATH_PRINTF +# undef RNA_PATH_FREE + return !match; } } @@ -1616,6 +1673,10 @@ int rna_property_override_diff_default(Main *bmain, no_prop_name, override, rna_path, + NULL, + NULL, + -1, + -1, flags, r_override_changed); } @@ -1636,21 +1697,6 @@ int rna_property_override_diff_default(Main *bmain, int idx_a = 0; int idx_b = 0; -# define RNA_PATH_BUFFSIZE 8192 - - char extended_rna_path_buffer[RNA_PATH_BUFFSIZE]; - char *extended_rna_path = extended_rna_path_buffer; - -# define RNA_PATH_PRINTF(_str, ...) \ - if (BLI_snprintf(extended_rna_path_buffer, RNA_PATH_BUFFSIZE, (_str), __VA_ARGS__) >= \ - RNA_PATH_BUFFSIZE - 1) { \ - extended_rna_path = BLI_sprintfN((_str), __VA_ARGS__); \ - } \ - (void)0 -# define RNA_PATH_FREE() \ - if (extended_rna_path != extended_rna_path_buffer) \ - MEM_freeN(extended_rna_path) - CollectionPropertyIterator iter_a, iter_b; RNA_property_collection_begin(ptr_a, prop_a, &iter_a); RNA_property_collection_begin(ptr_b, prop_b, &iter_b); @@ -1730,41 +1776,15 @@ int rna_property_override_diff_default(Main *bmain, # endif if (!(is_id || is_valid_for_diffing || is_valid_for_insertion)) { - /* Differences we cannot handle, we can break here - * (we do not support replacing ID pointers in collections e.g.). */ + /* Differences we cannot handle, we can break here. */ equals = false; abort = true; break; } - /* There may be a propname defined in some cases, while no actual name set - * (e.g. happens with point cache), in that case too we want to fall back to index. - * Note that we do not need the RNA path for insertion operations. */ - if (is_id || is_valid_for_diffing) { - if ((propname_a != NULL && propname_a[0] != '\0') && - (propname_b != NULL && propname_b[0] != '\0')) { - if (rna_path) { - /* In case of name, either it is valid for diffing, and _a and _b are identical, - * or it is valid for insertion, and we need to use _a. */ - char esc_item_name[RNA_PATH_BUFFSIZE]; - BLI_strescape(esc_item_name, propname_a, RNA_PATH_BUFFSIZE); - RNA_PATH_PRINTF("%s[\"%s\"]", rna_path, esc_item_name); - } - } - else { /* Based on index... */ - if (rna_path) { - /* In case of indices, we need _a one for insertion, - * but _b ones for in-depth diffing. - * Insertion always happen once all 'replace' operations have been done, - * otherwise local and reference paths for those would have to be different! */ - RNA_PATH_PRINTF("%s[%d]", rna_path, is_valid_for_insertion ? idx_a : idx_b); - } - } - } - - /* Collections do not support replacement of their data - * (since they do not support removing), only in *some* cases, insertion. - * We also assume then that _a data is the one where things are inserted. */ + /* Collections do not support replacement of their data (except for collections of ID + * pointers), since they do not support removing, only in *some* cases, insertion. We + * also assume then that _a data is the one where things are inserted. */ if (is_valid_for_insertion && use_insertion) { bool created; IDOverrideLibraryProperty *op = BKE_override_library_property_get( @@ -1812,7 +1832,11 @@ int rna_property_override_diff_default(Main *bmain, no_ownership, no_prop_name, override, - extended_rna_path, + rna_path, + propname_a, + propname_b, + idx_a, + idx_b, flags, r_override_changed); equals = equals && eq; @@ -1839,7 +1863,6 @@ int rna_property_override_diff_default(Main *bmain, propname_b = buff_b; } propname_b[0] = '\0'; - RNA_PATH_FREE(); if (!do_create && !equals) { abort = true; /* Early out in case we do not want to loop over whole collection. */ @@ -1864,10 +1887,6 @@ int rna_property_override_diff_default(Main *bmain, RNA_property_collection_next(&iter_b); idx_b++; } - -# undef RNA_PATH_BUFFSIZE -# undef RNA_PATH_PRINTF -# undef RNA_PATH_FREE } /* Not same number of items in both collections. */ @@ -2824,8 +2843,10 @@ static void rna_def_number_property(StructRNA *srna, PropertyType type) prop = RNA_def_property(srna, "default_array", type, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_array( - prop, RNA_MAX_ARRAY_DIMENSION); /* no fixed default length, important its not 0 though */ + + /* no fixed default length, important its not 0 though. */ + RNA_def_property_array(prop, RNA_MAX_ARRAY_DIMENSION); + RNA_def_property_flag(prop, PROP_DYNAMIC); RNA_def_property_dynamic_array_funcs( prop, "rna_NumberProperty_default_array_get_length"); /* same for all types */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 531ff27798d..e74e67fb83e 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -739,6 +739,36 @@ static void rna_GPencilInterpolateSettings_type_set(PointerRNA *ptr, int value) } } +static void rna_Gpencil_mask_point_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *)ptr->data; + + ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_STROKE; + ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_SEGMENT; +} + +static void rna_Gpencil_mask_stroke_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *)ptr->data; + + ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_POINT; + ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_SEGMENT; +} + +static void rna_Gpencil_mask_segment_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *)ptr->data; + + ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_POINT; + ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_STROKE; +} + /* Read-only Iterator of all the scene objects. */ static void rna_Scene_objects_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -821,8 +851,8 @@ static void rna_Scene_fps_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(p { DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_FPS | ID_RECALC_SEQUENCER_STRIPS); /* NOTE: Tag via dependency graph will take care of all the updates ion the evaluated domain, - * however, changes in FPS actually modifies an original stip length, so this we take care about - * here. */ + * however, changes in FPS actually modifies an original skip length, + * so this we take care about here. */ BKE_sequencer_refresh_sound_length(bmain, scene); } @@ -3058,13 +3088,39 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop, "Only Endpoints", "Only use the first and last parts of the stroke for snapping"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Grease Pencil - Select mode */ - prop = RNA_def_property(srna, "gpencil_selectmode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "gpencil_selectmode"); + /* Grease Pencil - Select mode Edit */ + prop = RNA_def_property(srna, "gpencil_selectmode_edit", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "gpencil_selectmode_edit"); RNA_def_property_enum_items(prop, gpencil_selectmode_items); RNA_def_property_ui_text(prop, "Select Mode", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + /* Grease Pencil - Select mode Sculpt */ + prop = RNA_def_property(srna, "use_gpencil_select_mask_point", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "gpencil_selectmode_sculpt", GP_SCULPT_MASK_SELECTMODE_POINT); + RNA_def_property_ui_text(prop, "Selection Mask", "Only sculpt selected stroke points"); + RNA_def_property_ui_icon(prop, ICON_GP_SELECT_POINTS, 0); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_mask_point_update"); + + prop = RNA_def_property(srna, "use_gpencil_select_mask_stroke", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "gpencil_selectmode_sculpt", GP_SCULPT_MASK_SELECTMODE_STROKE); + RNA_def_property_ui_text(prop, "Selection Mask", "Only sculpt selected stroke"); + RNA_def_property_ui_icon(prop, ICON_GP_SELECT_STROKES, 0); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_mask_stroke_update"); + + prop = RNA_def_property(srna, "use_gpencil_select_mask_segment", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "gpencil_selectmode_sculpt", GP_SCULPT_MASK_SELECTMODE_SEGMENT); + RNA_def_property_ui_text( + prop, "Selection Mask", "Only sculpt selected stroke points between other strokes"); + RNA_def_property_ui_icon(prop, ICON_GP_SELECT_BETWEEN_STROKES, 0); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_mask_segment_update"); + /* Annotations - 2D Views Stroke Placement */ prop = RNA_def_property(srna, "annotation_stroke_placement_view2d", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v2d_align"); @@ -3467,7 +3523,7 @@ static void rna_def_statvis(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "overhang_min"); RNA_def_property_float_default(prop, 0.5f); RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f)); - RNA_def_property_ui_range(prop, 0.0f, DEG2RADF(180.0f), 0.001, 3); + RNA_def_property_ui_range(prop, 0.0f, DEG2RADF(180.0f), 10, 3); RNA_def_property_ui_text(prop, "Overhang Min", "Minimum angle to display"); RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, 0, "rna_EditMesh_update"); diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index 853017e6daf..c868c79e968 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -225,12 +225,15 @@ static const EnumPropertyItem *rna_Area_ui_type_itemf(bContext *C, static int rna_Area_ui_type_get(PointerRNA *ptr) { - int value = rna_Area_type_get(ptr) << 16; ScrArea *sa = ptr->data; + const int area_type = rna_Area_type_get(ptr); + const bool area_changing = sa->butspacetype != SPACE_EMPTY; + int value = area_type << 16; + /* sa->type can be NULL (when not yet initialized), try to do it now. */ /* Copied from `ED_area_initialize()`.*/ - if (sa->type == NULL) { - sa->type = BKE_spacetype_from_id(sa->spacetype); + if (sa->type == NULL || area_changing) { + sa->type = BKE_spacetype_from_id(area_type); if (sa->type == NULL) { sa->spacetype = SPACE_VIEW3D; sa->type = BKE_spacetype_from_id(sa->spacetype); @@ -238,7 +241,7 @@ static int rna_Area_ui_type_get(PointerRNA *ptr) BLI_assert(sa->type != NULL); } if (sa->type->space_subtype_item_extend != NULL) { - value |= sa->type->space_subtype_get(sa); + value |= area_changing ? sa->butspacetype_subtype : sa->type->space_subtype_get(sa); } return value; } diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 84f2cb7c1be..fab3585797a 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1420,13 +1420,6 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Guide", ""); - prop = RNA_def_property(srna, "use_select_mask", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_SELECT_MASK); - RNA_def_property_ui_text(prop, "Selection Mask", "Only sculpt selected stroke points"); - RNA_def_property_ui_icon(prop, ICON_GP_ONLY_SELECTED, 0); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - prop = RNA_def_property(srna, "use_edit_position", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_APPLY_POSITION); RNA_def_property_ui_text(prop, "Affect Position", "The brush affects the position of the point"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 912e9599f14..a4f0715ca7b 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -32,6 +32,8 @@ #include "BKE_studiolight.h" #include "BKE_sequencer.h" +#include "ED_text.h" + #include "BLI_math.h" #include "DNA_action_types.h" @@ -1504,6 +1506,11 @@ static void rna_SpaceTextEditor_text_set(PointerRNA *ptr, WM_main_add_notifier(NC_TEXT | NA_SELECTED, st->text); } +static bool rna_SpaceTextEditor_text_is_syntax_highlight_supported(struct SpaceText *space) +{ + return ED_text_is_syntax_highlight_supported(space->text); +} + static void rna_SpaceTextEditor_updateEdited(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) @@ -2770,6 +2777,7 @@ static void rna_def_space_outliner(BlenderRNA *brna) static const EnumPropertyItem filter_state_items[] = { {SO_FILTER_OB_ALL, "ALL", 0, "All", "Show all objects in the view layer"}, {SO_FILTER_OB_VISIBLE, "VISIBLE", 0, "Visible", "Show visible objects"}, + {SO_FILTER_OB_INVISIBLE, "INVISIBLE", 0, "Invisible", "Show invisible objects"}, {SO_FILTER_OB_SELECTED, "SELECTED", 0, "Selected", "Show selected objects"}, {SO_FILTER_OB_ACTIVE, "ACTIVE", 0, "Active", "Show only the active object"}, {0, NULL, 0, NULL, NULL}, @@ -2808,6 +2816,12 @@ static void rna_def_space_outliner(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Sort Alphabetically", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL); + prop = RNA_def_property(srna, "use_sync_select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SO_SYNC_SELECT); + RNA_def_property_ui_text( + prop, "Sync Outliner Selection", "Sync outliner selection with other editors"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL); + /* Granular restriction column option. */ prop = RNA_def_property(srna, "show_restrict_column_enable", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "show_restrict_flags", SO_RESTRICT_ENABLE); @@ -4539,6 +4553,7 @@ static void rna_def_space_text(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; srna = RNA_def_struct(brna, "SpaceTextEditor", "Space"); RNA_def_struct_sdna(srna, "SpaceText"); @@ -4568,6 +4583,15 @@ static void rna_def_space_text(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_LINENUMBERS_ON, 0); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TEXT, NULL); + func = RNA_def_function(srna, + "is_syntax_highlight_supported", + "rna_SpaceTextEditor_text_is_syntax_highlight_supported"); + RNA_def_function_return(func, + RNA_def_boolean(func, "is_syntax_highlight_supported", false, "", "")); + RNA_def_function_ui_description(func, + "Returns True if the editor supports syntax highlighting " + "for the current text datablock"); + prop = RNA_def_property(srna, "show_syntax_highlight", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "showsyntax", 0); RNA_def_property_ui_text(prop, "Syntax Highlight", "Syntax highlight for scripting"); diff --git a/source/blender/makesrna/intern/rna_text.c b/source/blender/makesrna/intern/rna_text.c index b09b5327f57..64a23dfa985 100644 --- a/source/blender/makesrna/intern/rna_text.c +++ b/source/blender/makesrna/intern/rna_text.c @@ -27,6 +27,8 @@ #include "BKE_text.h" +#include "ED_text.h" + #include "RNA_define.h" #include "rna_internal.h" diff --git a/source/blender/makesrna/intern/rna_text_api.c b/source/blender/makesrna/intern/rna_text_api.c index 4ca48226ee9..524dcfa9ad7 100644 --- a/source/blender/makesrna/intern/rna_text_api.c +++ b/source/blender/makesrna/intern/rna_text_api.c @@ -23,6 +23,8 @@ #include "BLI_utildefines.h" +#include "ED_text.h" + #include "RNA_define.h" #include "rna_internal.h" /* own include */ @@ -59,6 +61,14 @@ void RNA_api_text(StructRNA *srna) func, "write text at the cursor location and advance to the end of the text block"); parm = RNA_def_string(func, "text", "Text", 0, "", "New text for this data-block"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + func = RNA_def_function( + srna, "is_syntax_highlight_supported", "ED_text_is_syntax_highlight_supported"); + RNA_def_function_return(func, + RNA_def_boolean(func, "is_syntax_highlight_supported", false, "", "")); + RNA_def_function_ui_description(func, + "Returns True if the editor supports syntax highlighting " + "for the current text datablock"); } #endif diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index d50f97e88ca..c8b039bd2d6 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -796,6 +796,7 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_boolean(func, "emboss", true, "", "Draw the button itself, not just the icon/text"); RNA_def_int(func, "index", + /* RNA_NO_INDEX == -1 */ -1, -2, INT_MAX, @@ -803,7 +804,7 @@ void RNA_api_ui_layout(StructRNA *srna) "The index of this button, when set a single member of an array can be accessed, " "when set to -1 all array members are used", -2, - INT_MAX); /* RNA_NO_INDEX == -1 */ + INT_MAX); parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED); RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item"); RNA_def_boolean(func, "invert_checkbox", false, "", "Draw checkbox value inverted"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 48eee713fc9..72a3455b120 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2297,6 +2297,11 @@ static void rna_def_userdef_theme_space_outliner(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Selected Highlight", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "active", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Active Highlight", ""); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "selected_object", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Selected Objects", ""); @@ -4090,6 +4095,12 @@ static void rna_def_userdef_view(BlenderRNA *brna) "Show the frames per second screen refresh rate, while animation is played back"); RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "show_addons_enabled_only", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_ADDONS_ENABLED_ONLY); + RNA_def_property_ui_text(prop, + "Enabled Add-ons Only", + "Only show enabled add-ons. Un-check to see all installed add-ons"); + static const EnumPropertyItem factor_display_items[] = { {USER_FACTOR_AS_FACTOR, "FACTOR", 0, "Factor", "Display factors as values between 0 and 1"}, {USER_FACTOR_AS_PERCENTAGE, "PERCENTAGE", 0, "Percentage", "Display factors as percentages"}, @@ -5820,7 +5831,10 @@ void RNA_def_userdef(BlenderRNA *brna) /* Preferences Flags */ prop = RNA_def_property(srna, "use_preferences_save", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "pref_flag", USER_PREF_FLAG_SAVE); - RNA_def_property_ui_text(prop, "Save on Exit", "Save preferences on exit when modified"); + RNA_def_property_ui_text(prop, + "Save on Exit", + "Save preferences on exit when modified " + "(unless factory settings have been loaded)"); prop = RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "runtime.is_dirty", 0); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 1edda29a556..123ec9634c9 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1514,8 +1514,10 @@ static StructRNA *rna_Operator_register(Main *bmain, /* create a new operator type */ dummyot.ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummyot.idname, &RNA_Operator); - RNA_def_struct_flag(dummyot.ext.srna, - STRUCT_NO_IDPROPERTIES); /* operator properties are registered separately */ + + /* Operator properties are registered separately. */ + RNA_def_struct_flag(dummyot.ext.srna, STRUCT_NO_IDPROPERTIES); + RNA_def_struct_property_tags(dummyot.ext.srna, rna_enum_operator_property_tags); RNA_def_struct_translation_context(dummyot.ext.srna, dummyot.translation_context); dummyot.ext.data = data; diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index 38b6ac9ac52..534e3042768 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -870,9 +870,10 @@ static StructRNA *rna_GizmoGroup_register(Main *bmain, /* create a new gizmogroup type */ dummywgt.ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummywgt.idname, &RNA_GizmoGroup); - RNA_def_struct_flag( - dummywgt.ext.srna, - STRUCT_NO_IDPROPERTIES); /* gizmogroup properties are registered separately */ + + /* Gizmo group properties are registered separately. */ + RNA_def_struct_flag(dummywgt.ext.srna, STRUCT_NO_IDPROPERTIES); + dummywgt.ext.data = data; dummywgt.ext.call = call; dummywgt.ext.free = free; diff --git a/source/blender/makesrna/intern/rna_wm_gizmo_api.c b/source/blender/makesrna/intern/rna_wm_gizmo_api.c index f7335572a14..bc6c2e8a796 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo_api.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo_api.c @@ -90,7 +90,7 @@ static void rna_gizmo_target_set_prop(wmGizmo *gz, RPT_ERROR, "Property '%s.%s' not found", RNA_struct_identifier(ptr->type), - target_propname); + propname); return; } diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index 309af4d4812..a05b7023392 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -214,8 +214,9 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL); - BLI_assert(bm->vtoolflagpool == NULL && bm->etoolflagpool == NULL && - bm->ftoolflagpool == NULL); /* make sure we never alloc'd these */ + /* Make sure we never alloc'd these. */ + BLI_assert(bm->vtoolflagpool == NULL && bm->etoolflagpool == NULL && bm->ftoolflagpool == NULL); + BM_mesh_free(bm); result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c index 107622e33c0..dc4898c83ff 100644 --- a/source/blender/modifiers/intern/MOD_boolean.c +++ b/source/blender/modifiers/intern/MOD_boolean.c @@ -262,8 +262,9 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { mul_transposed_m3_v3(nmat, efa->no); normalize_v3(efa->no); - BM_elem_flag_enable( - efa, BM_FACE_TAG); /* temp tag to test which side split faces are from */ + + /* Temp tag to test which side split faces are from. */ + BM_elem_flag_enable(efa, BM_FACE_TAG); /* remap material */ if (LIKELY(efa->mat_nr < ob_src_totcol)) { diff --git a/source/blender/modifiers/intern/MOD_mask.c b/source/blender/modifiers/intern/MOD_mask.c index 3417aaeeb5c..f2f2b13b0df 100644 --- a/source/blender/modifiers/intern/MOD_mask.c +++ b/source/blender/modifiers/intern/MOD_mask.c @@ -138,10 +138,10 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes return mesh; } - /* Determine whether each vertexgroup is associated with a selected bone or not: - * - Each cell is a boolean saying whether bone corresponding to the ith group is selected. + /* Determine whether each vertex-group is associated with a selected bone or not: + * - Each cell is a boolean saying whether bone corresponding to the i'th group selected. * - Groups that don't match a bone are treated as not existing - * (along with the corresponding ungrouped verts). + * (along with the corresponding un-grouped verts). */ bone_select_array = MEM_malloc_arrayN((size_t)defbase_tot, sizeof(char), "mask array"); diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 760830ffb24..0f57b759e38 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -114,10 +114,20 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes } } + /* If this invocation is for the ORCO mesh, and the mesh in Alembic hasn't changed topology, we + * must return the mesh as-is instead of deforming it. */ + if (ctx->flag & MOD_APPLY_ORCO && + !ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) { + return mesh; + } + if (me != NULL) { MVert *mvert = mesh->mvert; MEdge *medge = mesh->medge; MPoly *mpoly = mesh->mpoly; + + /* TODO(sybren+bastien): possibly check relevant custom data layers (UV/color depending on + * flags) and duplicate those too. */ if ((me->mvert == mvert) || (me->medge == medge) || (me->mpoly == mpoly)) { /* We need to duplicate data here, otherwise we'll modify org mesh, see T51701. */ BKE_id_copy_ex(NULL, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index 8d511207b9b..d10d74da453 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -351,10 +351,8 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */ copy_v3_v3_unmap(co, co_remap, axis_map); - interp_v3_v3v3(vertexCos[i], - vertexCos[i], - co, - weight); /* Use vertex weight has coef of linear interpolation */ + /* Use vertex weight has coef of linear interpolation */ + interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight); if (transf) { BLI_space_transform_invert(transf, vertexCos[i]); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 80afcada4b9..284eaa8b70b 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC composite/nodes/node_composite_cryptomatte.c composite/nodes/node_composite_curves.c composite/nodes/node_composite_defocus.c + composite/nodes/node_composite_denoise.c composite/nodes/node_composite_despeckle.c composite/nodes/node_composite_diffMatte.c composite/nodes/node_composite_dilate.c @@ -147,6 +148,7 @@ set(SRC shader/nodes/node_shader_bsdf_velvet.c shader/nodes/node_shader_bump.c shader/nodes/node_shader_camera.c + shader/nodes/node_shader_clamp.c shader/nodes/node_shader_common.c shader/nodes/node_shader_curves.c shader/nodes/node_shader_displacement.c @@ -163,6 +165,7 @@ set(SRC shader/nodes/node_shader_layer_weight.c shader/nodes/node_shader_light_falloff.c shader/nodes/node_shader_light_path.c + shader/nodes/node_shader_map_range.c shader/nodes/node_shader_mapping.c shader/nodes/node_shader_math.c shader/nodes/node_shader_mixRgb.c diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index e6d9ed6f70e..534e9012693 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -74,6 +74,7 @@ void register_node_type_cmp_dilateerode(void); void register_node_type_cmp_inpaint(void); void register_node_type_cmp_despeckle(void); void register_node_type_cmp_defocus(void); +void register_node_type_cmp_denoise(void); void register_node_type_cmp_valtorgb(void); void register_node_type_cmp_rgbtobw(void); diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index ead42779bc0..9349a428021 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -48,6 +48,8 @@ void register_node_type_sh_brightcontrast(void); void register_node_type_sh_mapping(void); void register_node_type_sh_curve_vec(void); void register_node_type_sh_curve_rgb(void); +void register_node_type_sh_map_range(void); +void register_node_type_sh_clamp(void); void register_node_type_sh_math(void); void register_node_type_sh_vect_math(void); void register_node_type_sh_squeeze(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index f27c50ae736..c72e97642a2 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -50,6 +50,8 @@ DefNode(ShaderNode, SH_NODE_MAPPING, def_sh_mapping, "MAPPIN DefNode(ShaderNode, SH_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", VectorCurve, "Vector Curves", "" ) DefNode(ShaderNode, SH_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", RGBCurve, "RGB Curves", "" ) DefNode(ShaderNode, SH_NODE_CAMERA, 0, "CAMERA", CameraData, "Camera Data", "" ) +DefNode(ShaderNode, SH_NODE_MAP_RANGE, def_map_range, "MAP_RANGE", MapRange, "Map Range", "" ) +DefNode(ShaderNode, SH_NODE_CLAMP, 0, "CLAMP", Clamp, "Clamp", "" ) DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH", Math, "Math", "" ) DefNode(ShaderNode, SH_NODE_VECT_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "" ) DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "" ) @@ -214,6 +216,7 @@ DefNode(CompositorNode, CMP_NODE_PLANETRACKDEFORM,def_cmp_planetrackdeform,"PLAN DefNode(CompositorNode, CMP_NODE_CORNERPIN, 0, "CORNERPIN", CornerPin, "Corner Pin", "" ) DefNode(CompositorNode, CMP_NODE_SUNBEAMS, def_cmp_sunbeams, "SUNBEAMS", SunBeams, "Sun Beams", "" ) DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE, def_cmp_cryptomatte, "CRYPTOMATTE", Cryptomatte, "Cryptomatte", "" ) +DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOISE", Denoise, "Denoise", "" ) DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.c b/source/blender/nodes/composite/nodes/node_composite_denoise.c new file mode 100644 index 00000000000..e2fdb08816a --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_denoise.c @@ -0,0 +1,58 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Stefan Werner + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/nodes/composite/nodes/node_composite_denoise.c + * \ingroup cmpnodes + */ + +#include "node_composite_util.h" + +static bNodeSocketTemplate cmp_node_denoise_in[] = { + {SOCK_RGBA, 1, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, + {SOCK_RGBA, 1, N_("Albedo"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, + {SOCK_VECTOR, 0, N_("Normal"), 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f}, + {-1, 0, ""}}; +static bNodeSocketTemplate cmp_node_denoise_out[] = {{SOCK_RGBA, 0, N_("Image")}, {-1, 0, ""}}; + +static void node_composit_init_denonise(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeDenoise *ndg = MEM_callocN(sizeof(NodeDenoise), "node denoise data"); + ndg->hdr = true; + node->storage = ndg; +} + +void register_node_type_cmp_denoise(void) +{ + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_DENOISE, "Denoise", NODE_CLASS_OP_FILTER, 0); + node_type_socket_templates(&ntype, cmp_node_denoise_in, cmp_node_denoise_out); + node_type_init(&ntype, node_composit_init_denonise); + node_type_storage(&ntype, "NodeDenoise", node_free_standard_storage, node_copy_standard_storage); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c index 2a371b7d184..595ddf27d0a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c @@ -24,7 +24,17 @@ static bNodeSocketTemplate sh_node_bsdf_principled_in[] = { {SOCK_RGBA, 1, N_("Base Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, {SOCK_FLOAT, 1, N_("Subsurface"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, 1, N_("Subsurface Radius"), 1.0f, 0.2f, 0.1f, 0.0f, 0.0f, 100.0f}, + {SOCK_VECTOR, + 1, + N_("Subsurface Radius"), + 1.0f, + 0.2f, + 0.1f, + 0.0f, + 0.0f, + 100.0f, + PROP_NONE, + SOCK_COMPACT}, {SOCK_RGBA, 1, N_("Subsurface Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, {SOCK_FLOAT, 1, N_("Metallic"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, 1, N_("Specular"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, @@ -122,8 +132,13 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, &in[21].link); } + bool use_diffuse = socket_not_one(4) && socket_not_one(15); + bool use_subsurf = socket_not_zero(1) && use_diffuse && node->sss_id > 0; + bool use_refract = socket_not_one(4) && socket_not_zero(15); + bool use_clear = socket_not_zero(12); + /* SSS Profile */ - if (node->sss_id == 1) { + if (use_subsurf) { static short profile = SHD_SUBSURFACE_BURLEY; bNodeSocket *socket = BLI_findlink(&node->original->inputs, 2); bNodeSocketValueRGBA *socket_data = socket->default_value; @@ -138,11 +153,6 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, GPU_link(mat, "set_rgb_one", &sss_scale); } - bool use_diffuse = socket_not_one(4) && socket_not_one(15); - bool use_subsurf = socket_not_zero(1) && use_diffuse && node->sss_id == 1; - bool use_refract = socket_not_one(4) && socket_not_zero(15); - bool use_clear = socket_not_zero(12); - /* Due to the manual effort done per config, we only optimize the most common permutations. */ char *node_name; uint flag = 0; diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.c b/source/blender/nodes/shader/nodes/node_shader_clamp.c new file mode 100644 index 00000000000..8e5b90436ea --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.c @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.h" + +/* **************** Clamp ******************** */ +static bNodeSocketTemplate sh_node_clamp_in[] = { + {SOCK_FLOAT, 1, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {-1, 0, ""}, +}; +static bNodeSocketTemplate sh_node_clamp_out[] = { + {SOCK_FLOAT, 0, N_("Result")}, + {-1, 0, ""}, +}; + +static int gpu_shader_clamp(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "clamp_value", in, out); +} + +void register_node_type_sh_clamp(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTOR, 0); + node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); + node_type_gpu(&ntype, gpu_shader_clamp); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_holdout.c b/source/blender/nodes/shader/nodes/node_shader_holdout.c index a4d1e77c736..2762e5ec1f5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_holdout.c +++ b/source/blender/nodes/shader/nodes/node_shader_holdout.c @@ -30,6 +30,15 @@ static bNodeSocketTemplate sh_node_holdout_out[] = { {-1, 0, ""}, }; +static int gpu_shader_rgb(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "node_holdout", in, out); +} + /* node type definition */ void register_node_type_sh_holdout(void) { @@ -39,6 +48,7 @@ void register_node_type_sh_holdout(void) node_type_socket_templates(&ntype, sh_node_holdout_in, sh_node_holdout_out); node_type_init(&ntype, NULL); node_type_storage(&ntype, "", NULL, NULL); + node_type_gpu(&ntype, gpu_shader_rgb); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.c b/source/blender/nodes/shader/nodes/node_shader_map_range.c new file mode 100644 index 00000000000..7ebf3faf1f3 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.c @@ -0,0 +1,68 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.h" + +/* **************** Map Range ******************** */ +static bNodeSocketTemplate sh_node_map_range_in[] = { + {SOCK_FLOAT, 1, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("From Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("From Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("To Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("To Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {-1, 0, ""}, +}; +static bNodeSocketTemplate sh_node_map_range_out[] = { + {SOCK_FLOAT, 0, N_("Result")}, + {-1, 0, ""}, +}; + +static void node_shader_init_map_range(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = true; +} + +static int gpu_shader_map_range(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + GPU_stack_link(mat, node, "map_range", in, out); + if (node->custom1) { + GPU_link(mat, "clamp_value", out[0].link, in[3].link, in[4].link, &out[0].link); + } + return 1; +} + +void register_node_type_sh_map_range(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTOR, 0); + node_type_socket_templates(&ntype, sh_node_map_range_in, sh_node_map_range_out); + node_type_init(&ntype, node_shader_init_map_range); + node_type_gpu(&ntype, gpu_shader_map_range); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_math.c b/source/blender/nodes/shader/nodes/node_shader_math.c index f0cd2273e67..aaedc4aa1b7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.c +++ b/source/blender/nodes/shader/nodes/node_shader_math.c @@ -31,272 +31,6 @@ static bNodeSocketTemplate sh_node_math_in[] = { static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, 0, N_("Value")}, {-1, 0, ""}}; -static void node_shader_exec_math(void *UNUSED(data), - int UNUSED(thread), - bNode *node, - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float a, b, r = 0.0f; - - nodestack_get_vec(&a, SOCK_FLOAT, in[0]); - nodestack_get_vec(&b, SOCK_FLOAT, in[1]); - - switch (node->custom1) { - - case NODE_MATH_ADD: - r = a + b; - break; - case NODE_MATH_SUB: - r = a - b; - break; - case NODE_MATH_MUL: - r = a * b; - break; - case NODE_MATH_DIVIDE: { - if (b == 0) { /* We don't want to divide by zero. */ - r = 0.0; - } - else { - r = a / b; - } - break; - } - case NODE_MATH_SIN: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - r = sinf(a); - } - else { - r = sinf(b); - } - break; - } - case NODE_MATH_COS: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - r = cosf(a); - } - else { - r = cosf(b); - } - break; - } - case NODE_MATH_TAN: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - r = tanf(a); - } - else { - r = tanf(b); - } - break; - } - case NODE_MATH_ASIN: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - /* Can't do the impossible... */ - if (a <= 1 && a >= -1) { - r = asinf(a); - } - else { - r = 0.0; - } - } - else { - /* Can't do the impossible... */ - if (b <= 1 && b >= -1) { - r = asinf(b); - } - else { - r = 0.0; - } - } - break; - } - case NODE_MATH_ACOS: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - /* Can't do the impossible... */ - if (a <= 1 && a >= -1) { - r = acosf(a); - } - else { - r = 0.0; - } - } - else { - /* Can't do the impossible... */ - if (b <= 1 && b >= -1) { - r = acosf(b); - } - else { - r = 0.0; - } - } - break; - } - case NODE_MATH_ATAN: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - r = atan(a); - } - else { - r = atan(b); - } - break; - } - case NODE_MATH_POW: { - /* Only raise negative numbers by full integers */ - if (a >= 0) { - r = pow(a, b); - } - else { - float y_mod_1 = fabsf(fmodf(b, 1.0f)); - - /* if input value is not nearly an integer, - * fall back to zero, nicer than straight rounding. */ - if (y_mod_1 > 0.999f || y_mod_1 < 0.001f) { - r = powf(a, floorf(b + 0.5f)); - } - else { - r = 0.0f; - } - } - - break; - } - case NODE_MATH_LOG: { - /* Don't want any imaginary numbers... */ - if (a > 0 && b > 0) { - r = log(a) / log(b); - } - else { - r = 0.0; - } - break; - } - case NODE_MATH_MIN: { - if (a < b) { - r = a; - } - else { - r = b; - } - break; - } - case NODE_MATH_MAX: { - if (a > b) { - r = a; - } - else { - r = b; - } - break; - } - case NODE_MATH_ROUND: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - r = (a < 0) ? (int)(a - 0.5f) : (int)(a + 0.5f); - } - else { - r = (b < 0) ? (int)(b - 0.5f) : (int)(b + 0.5f); - } - break; - } - case NODE_MATH_LESS: { - if (a < b) { - r = 1.0f; - } - else { - r = 0.0f; - } - break; - } - case NODE_MATH_GREATER: { - if (a > b) { - r = 1.0f; - } - else { - r = 0.0f; - } - break; - } - case NODE_MATH_MOD: { - if (b == 0.0f) { - r = 0.0f; - } - else { - r = fmod(a, b); - } - break; - } - case NODE_MATH_ABS: { - r = fabsf(a); - break; - } - case NODE_MATH_ATAN2: { - r = atan2(a, b); - break; - } - case NODE_MATH_FLOOR: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - r = floorf(a); - } - else { - r = floorf(b); - } - break; - } - case NODE_MATH_CEIL: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - r = ceilf(a); - } - else { - r = ceilf(b); - } - break; - } - case NODE_MATH_FRACT: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - r = a - floorf(a); - } - else { - r = b - floorf(b); - } - break; - } - case NODE_MATH_SQRT: { - /* This one only takes one input, so we've got to choose. */ - if (in[0]->hasinput || !in[1]->hasinput) { - if (a > 0) { - r = sqrt(a); - } - else { - r = 0.0; - } - } - else { - if (b > 0) { - r = sqrt(b); - } - else { - r = 0.0; - } - } - break; - } - } - if (node->custom2 & SHD_MATH_CLAMP) { - CLAMP(r, 0.0f, 1.0f); - } - out[0]->vec[0] = r; -} - static int gpu_shader_math(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -304,68 +38,65 @@ static int gpu_shader_math(GPUMaterial *mat, GPUNodeStack *out) { static const char *names[] = { - "math_add", "math_subtract", "math_multiply", "math_divide", "math_sine", - "math_cosine", "math_tangent", "math_asin", "math_acos", "math_atan", - "math_pow", "math_log", "math_min", "math_max", "math_round", - "math_less_than", "math_greater_than", "math_modulo", "math_abs", "math_atan2", - "math_floor", "math_ceil", "math_fract", "math_sqrt", + [NODE_MATH_ADD] = "math_add", + [NODE_MATH_SUBTRACT] = "math_subtract", + [NODE_MATH_MULTIPLY] = "math_multiply", + [NODE_MATH_DIVIDE] = "math_divide", + + [NODE_MATH_POWER] = "math_power", + [NODE_MATH_LOGARITHM] = "math_logarithm", + [NODE_MATH_SQRT] = "math_sqrt", + [NODE_MATH_ABSOLUTE] = "math_absolute", + + [NODE_MATH_MINIMUM] = "math_minimum", + [NODE_MATH_MAXIMUM] = "math_maximum", + [NODE_MATH_LESS_THAN] = "math_less_than", + [NODE_MATH_GREATER_THAN] = "math_greater_than", + + [NODE_MATH_ROUND] = "math_round", + [NODE_MATH_FLOOR] = "math_floor", + [NODE_MATH_CEIL] = "math_ceil", + [NODE_MATH_FRACTION] = "math_fraction", + [NODE_MATH_MODULO] = "math_modulo", + + [NODE_MATH_SINE] = "math_sine", + [NODE_MATH_COSINE] = "math_cosine", + [NODE_MATH_TANGENT] = "math_tangent", + [NODE_MATH_ARCSINE] = "math_arcsine", + [NODE_MATH_ARCCOSINE] = "math_arccosine", + [NODE_MATH_ARCTANGENT] = "math_arctangent", + [NODE_MATH_ARCTAN2] = "math_arctan2", }; - switch (node->custom1) { - case NODE_MATH_ADD: - case NODE_MATH_SUB: - case NODE_MATH_MUL: - case NODE_MATH_DIVIDE: - case NODE_MATH_POW: - case NODE_MATH_LOG: - case NODE_MATH_MIN: - case NODE_MATH_MAX: - case NODE_MATH_LESS: - case NODE_MATH_GREATER: - case NODE_MATH_MOD: - case NODE_MATH_ATAN2: - GPU_stack_link(mat, node, names[node->custom1], in, out); - break; - case NODE_MATH_SIN: - case NODE_MATH_COS: - case NODE_MATH_TAN: - case NODE_MATH_ASIN: - case NODE_MATH_ACOS: - case NODE_MATH_ATAN: - case NODE_MATH_ROUND: - case NODE_MATH_ABS: - case NODE_MATH_FLOOR: - case NODE_MATH_FRACT: - case NODE_MATH_CEIL: - case NODE_MATH_SQRT: - if (in[0].hasinput || !in[1].hasinput) { - /* use only first item and terminator */ - GPUNodeStack tmp_in[2]; - memcpy(&tmp_in[0], &in[0], sizeof(GPUNodeStack)); - memcpy(&tmp_in[1], &in[2], sizeof(GPUNodeStack)); - GPU_stack_link(mat, node, names[node->custom1], tmp_in, out); - } - else { - /* use only second item and terminator */ - GPUNodeStack tmp_in[2]; - memcpy(&tmp_in[0], &in[1], sizeof(GPUNodeStack)); - memcpy(&tmp_in[1], &in[2], sizeof(GPUNodeStack)); - GPU_stack_link(mat, node, names[node->custom1], tmp_in, out); - } - break; - default: - return 0; - } + GPU_stack_link(mat, node, names[node->custom1], in, out); if (node->custom2 & SHD_MATH_CLAMP) { float min[3] = {0.0f, 0.0f, 0.0f}; float max[3] = {1.0f, 1.0f, 1.0f}; - GPU_link(mat, "clamp_val", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link); + GPU_link(mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link); } - return 1; } +static void node_shader_update_math(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock = BLI_findlink(&node->inputs, 1); + nodeSetSocketAvailability(sock, + !ELEM(node->custom1, + NODE_MATH_SQRT, + NODE_MATH_CEIL, + NODE_MATH_SINE, + NODE_MATH_ROUND, + NODE_MATH_FLOOR, + NODE_MATH_COSINE, + NODE_MATH_ARCSINE, + NODE_MATH_TANGENT, + NODE_MATH_ABSOLUTE, + NODE_MATH_FRACTION, + NODE_MATH_ARCCOSINE, + NODE_MATH_ARCTANGENT)); +} + void register_node_type_sh_math(void) { static bNodeType ntype; @@ -373,9 +104,8 @@ void register_node_type_sh_math(void) sh_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out); node_type_label(&ntype, node_math_label); - node_type_storage(&ntype, "", NULL, NULL); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_math); node_type_gpu(&ntype, gpu_shader_math); + node_type_update(&ntype, node_shader_update_math); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_normal.c b/source/blender/nodes/shader/nodes/node_shader_normal.c index 074cc3dd87f..9dd89258446 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal.c +++ b/source/blender/nodes/shader/nodes/node_shader_normal.c @@ -25,7 +25,7 @@ /* **************** NORMAL ******************** */ static bNodeSocketTemplate sh_node_normal_in[] = { - {SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_DIRECTION}, + {SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_DIRECTION}, {-1, 0, ""}, }; diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c index f1b4a0e9fe3..9a0a132b311 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c @@ -24,7 +24,7 @@ static bNodeSocketTemplate sh_node_subsurface_scattering_in[] = { {SOCK_RGBA, 1, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, {SOCK_FLOAT, 1, N_("Scale"), 1.0, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_VECTOR, 1, N_("Radius"), 1.0f, 0.2f, 0.1f, 0.0f, 0.0f, 100.0f}, + {SOCK_VECTOR, 1, N_("Radius"), 1.0f, 0.2f, 0.1f, 0.0f, 0.0f, 100.0f, PROP_NONE, SOCK_COMPACT}, {SOCK_FLOAT, 1, N_("Sharpness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, 1, N_("Texture Blur"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_VECTOR, @@ -63,7 +63,7 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SSS); - if (node->sss_id == 1) { + if (node->sss_id > 0) { bNodeSocket *socket = BLI_findlink(&node->original->inputs, 2); bNodeSocketValueRGBA *socket_data = socket->default_value; bNodeSocket *socket_sharp = BLI_findlink(&node->original->inputs, 3); diff --git a/source/blender/nodes/shader/nodes/node_shader_tangent.c b/source/blender/nodes/shader/nodes/node_shader_tangent.c index 6795f48edb3..478b9524737 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tangent.c +++ b/source/blender/nodes/shader/nodes/node_shader_tangent.c @@ -42,7 +42,8 @@ static int node_shader_gpu_tangent(GPUMaterial *mat, NodeShaderTangent *attr = node->storage; if (attr->direction_type == SHD_TANGENT_UVMAP) { - return GPU_stack_link(mat, node, "node_tangentmap", in, out, GPU_attribute(CD_TANGENT, "")); + return GPU_stack_link( + mat, node, "node_tangentmap", in, out, GPU_attribute(CD_TANGENT, attr->uv_map)); } else { GPUNodeLink *orco = GPU_attribute(CD_ORCO, ""); diff --git a/source/blender/nodes/texture/nodes/node_texture_math.c b/source/blender/nodes/texture/nodes/node_texture_math.c index 2eb32e0addc..b1d67a5a953 100644 --- a/source/blender/nodes/texture/nodes/node_texture_math.c +++ b/source/blender/nodes/texture/nodes/node_texture_math.c @@ -46,10 +46,10 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor case NODE_MATH_ADD: *out = in0 + in1; break; - case NODE_MATH_SUB: + case NODE_MATH_SUBTRACT: *out = in0 - in1; break; - case NODE_MATH_MUL: + case NODE_MATH_MULTIPLY: *out = in0 * in1; break; case NODE_MATH_DIVIDE: { @@ -62,19 +62,19 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor } break; } - case NODE_MATH_SIN: { + case NODE_MATH_SINE: { *out = sinf(in0); break; } - case NODE_MATH_COS: { + case NODE_MATH_COSINE: { *out = cosf(in0); break; } - case NODE_MATH_TAN: { + case NODE_MATH_TANGENT: { *out = tanf(in0); break; } - case NODE_MATH_ASIN: { + case NODE_MATH_ARCSINE: { /* Can't do the impossible... */ if (in0 <= 1 && in0 >= -1) { *out = asinf(in0); @@ -84,7 +84,7 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor } break; } - case NODE_MATH_ACOS: { + case NODE_MATH_ARCCOSINE: { /* Can't do the impossible... */ if (in0 <= 1 && in0 >= -1) { *out = acosf(in0); @@ -94,11 +94,11 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor } break; } - case NODE_MATH_ATAN: { + case NODE_MATH_ARCTANGENT: { *out = atan(in0); break; } - case NODE_MATH_POW: { + case NODE_MATH_POWER: { /* Only raise negative numbers by full integers */ if (in0 >= 0) { out[0] = pow(in0, in1); @@ -114,7 +114,7 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor } break; } - case NODE_MATH_LOG: { + case NODE_MATH_LOGARITHM: { /* Don't want any imaginary numbers... */ if (in0 > 0 && in1 > 0) { *out = log(in0) / log(in1); @@ -124,7 +124,7 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor } break; } - case NODE_MATH_MIN: { + case NODE_MATH_MINIMUM: { if (in0 < in1) { *out = in0; } @@ -133,7 +133,7 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor } break; } - case NODE_MATH_MAX: { + case NODE_MATH_MAXIMUM: { if (in0 > in1) { *out = in0; } @@ -147,7 +147,7 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor break; } - case NODE_MATH_LESS: { + case NODE_MATH_LESS_THAN: { if (in0 < in1) { *out = 1.0f; } @@ -157,7 +157,7 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor break; } - case NODE_MATH_GREATER: { + case NODE_MATH_GREATER_THAN: { if (in0 > in1) { *out = 1.0f; } @@ -167,7 +167,7 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor break; } - case NODE_MATH_MOD: { + case NODE_MATH_MODULO: { if (in1 == 0.0f) { *out = 0.0f; } @@ -177,12 +177,12 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor break; } - case NODE_MATH_ABS: { + case NODE_MATH_ABSOLUTE: { *out = fabsf(in0); break; } - case NODE_MATH_ATAN2: { + case NODE_MATH_ARCTAN2: { *out = atan2(in0, in1); break; } @@ -197,7 +197,7 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor break; } - case NODE_MATH_FRACT: { + case NODE_MATH_FRACTION: { *out = in0 - floorf(in0); break; } diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp index 95c16c2f033..07cb3370eec 100644 --- a/source/blender/physics/intern/BPH_mass_spring.cpp +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -393,7 +393,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s) if (s->type & CLOTH_SPRING_TYPE_SEWING) { /* TODO: verify, half verified (couldn't see error) * sewing springs usually have a large distance at first so clamp the force so we don't get - * tunnelling through collision objects. */ + * tunneling through collision objects. */ BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c index 93df51ce58d..c2eb7b465e1 100644 --- a/source/blender/physics/intern/implicit_blender.c +++ b/source/blender/physics/intern/implicit_blender.c @@ -1158,12 +1158,9 @@ bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSol double start = PIL_check_seconds_timer(); # endif - cg_filtered(data->dV, - data->A, - data->B, - data->z, - data->S, - result); /* conjugate gradient algorithm to solve Ax=b */ + /* Conjugate gradient algorithm to solve Ax=b. */ + cg_filtered(data->dV, data->A, data->B, data->z, data->S, result); + // cg_filtered_pre(id->dV, id->A, id->B, id->z, id->S, id->P, id->Pinv, id->bigI); # ifdef DEBUG_TIME diff --git a/source/blender/python/bmesh/bmesh_py_types.h b/source/blender/python/bmesh/bmesh_py_types.h index 460e7f82222..c61fdeab4b0 100644 --- a/source/blender/python/bmesh/bmesh_py_types.h +++ b/source/blender/python/bmesh/bmesh_py_types.h @@ -140,8 +140,8 @@ PyObject *BPy_BMFaceSeq_CreatePyObject(BMesh *bm); PyObject *BPy_BMLoopSeq_CreatePyObject(BMesh *bm); PyObject *BPy_BMIter_CreatePyObject(BMesh *bm); -PyObject *BPy_BMElem_CreatePyObject(BMesh *bm, - BMHeader *ele); /* just checks type and creates v/e/f/l */ +/* Just checks type and creates v/e/f/l. */ +PyObject *BPy_BMElem_CreatePyObject(BMesh *bm, BMHeader *ele); void *BPy_BMElem_PySeq_As_Array_FAST(BMesh **r_bm, PyObject *seq_fast, diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index 56c25edb7e4..a7f4e30b494 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -516,8 +516,10 @@ static PyObject *bpy_bmlayercollection_keys(BPy_BMLayerCollection *self) BPY_BM_CHECK_OBJ(self); data = bpy_bm_customdata_get(self->bm, self->htype); - index = CustomData_get_layer_index(data, - self->type); /* absolute, but no need to make relative */ + + /* Absolute, but no need to make relative. */ + index = CustomData_get_layer_index(data, self->type); + tot = (index != -1) ? CustomData_number_of_layers(data, self->type) : 0; ret = PyList_New(tot); diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c index 0aa01ddb594..2e15c1d9ce0 100644 --- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c @@ -20,8 +20,8 @@ /** \file * \ingroup pybmesh * - * This file defines customdata types which can't be accessed as primitive - * python types such as MDeformVert, MLoopUV, MTexPoly + * This file defines custom-data types which can't be accessed as primitive + * python types such as #MDeformVert, #MLoopUV. */ #include <Python.h> @@ -515,8 +515,8 @@ static PySequenceMethods bpy_bmdeformvert_as_sequence = { NULL, /* sq_concat */ NULL, /* sq_repeat */ - /* note: if this is set PySequence_Check() returns True, - * but in this case we dont want to be treated as a seq */ + /* Note: if this is set #PySequence_Check() returns True, + * but in this case we don't want to be treated as a seq. */ NULL, /* sq_item */ NULL, /* sq_slice */ diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 30cad991b55..d10d281c1f9 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -1428,8 +1428,9 @@ static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject size = (end - begin); alloc_len = size * elem_size; - vec = MEM_mallocN(alloc_len, - "array assignment"); /* NOTE: we count on int/float being the same size here */ + /* NOTE: we count on int/float being the same size here */ + vec = MEM_mallocN(alloc_len, "array assignment"); + if (PyC_AsArray(vec, seq, size, py_type, is_double, "slice assignment: ") == -1) { MEM_freeN(vec); return -1; diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index a48cd742448..3c1dbfba72e 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -890,7 +890,7 @@ PyObject *BPY_app_translations_struct(void) void BPY_app_translations_end(void) { - /* Incase the object remains in a module's namespace, see T44127. */ + /* In case the object remains in a module's name-space, see T44127. */ #ifdef WITH_INTERNATIONAL _clear_translations_cache(); #endif diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index aef4ab6667a..5e3b000c604 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -252,8 +252,9 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) ReportList *reports; reports = MEM_mallocN(sizeof(ReportList), "wmOperatorReportList"); - BKE_reports_init(reports, - RPT_STORE | RPT_OP_HOLD); /* own so these don't move into global reports */ + + /* Own so these don't move into global reports. */ + BKE_reports_init(reports, RPT_STORE | RPT_OP_HOLD); #ifdef BPY_RELEASE_GIL /* release GIL, since a thread could be started from an operator diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 8f2f08c7c37..2e88a2a5b06 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -58,7 +58,7 @@ static void operator_properties_init(wmOperatorType *ot) /* set the default property: ot->prop */ { /* Picky developers will notice that 'bl_property' won't work with inheritance - * get direct from the dict to avoid raising a load of attribute errors (yes this isnt ideal) + * get direct from the dict to avoid raising a load of attribute errors (yes this isn't ideal) * - campbell. */ PyObject *py_class_dict = py_class->tp_dict; PyObject *bl_property = PyDict_GetItem(py_class_dict, bpy_intern_str_bl_property); diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 70bfa76e344..9e734123caa 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -2931,8 +2931,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw prop = RNA_def_property(srna, id, PROP_STRING, subtype); if (maxlen != 0) { - RNA_def_property_string_maxlength(prop, - maxlen + 1); /* +1 since it includes null terminator */ + /* +1 since it includes null terminator. */ + RNA_def_property_string_maxlength(prop, maxlen + 1); } if (def && def[0]) { RNA_def_property_string_default(prop, def); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 9a8d8c5ec21..e7fed02fe79 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -8557,9 +8557,10 @@ static PyObject *pyrna_register_class(PyObject *UNUSED(self), PyObject *py_class } if (PyDict_GetItem(((PyTypeObject *)py_class)->tp_dict, bpy_intern_str_bl_rna)) { - PyErr_SetString(PyExc_ValueError, - "register_class(...): " - "already registered as a subclass"); + PyErr_Format(PyExc_ValueError, + "register_class(...): " + "already registered as a subclass '%.200s'", + ((PyTypeObject *)py_class)->tp_name); return NULL; } diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index fb9454bf108..7a3499d0295 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -455,8 +455,8 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb NlaStrip *strip = (NlaStrip *)ptr.data; FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), index); - BLI_assert(fcu != - NULL); /* NOTE: This should be true, or else we wouldn't be able to get here */ + /* NOTE: This should be true, or else we wouldn't be able to get here. */ + BLI_assert(fcu != NULL); if (BKE_fcurve_is_protected(fcu)) { BKE_reportf( diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c index 9d8fff5dfe4..a8312d89ef8 100644 --- a/source/blender/python/intern/bpy_rna_array.c +++ b/source/blender/python/intern/bpy_rna_array.c @@ -554,8 +554,9 @@ static int py_to_array(PyObject *seq, /* not freeing allocated mem, RNA_parameter_list_free() will do this */ ParameterDynAlloc *param_alloc = (ParameterDynAlloc *)param_data; param_alloc->array_tot = (int)totitem; - param_alloc->array = MEM_callocN(item_size * totitem, - "py_to_array dyn"); /* freeing param list will free */ + + /* freeing param list will free */ + param_alloc->array = MEM_callocN(item_size * totitem, "py_to_array dyn"); data = param_alloc->array; } diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index befa6532e97..2b1ddbbb03a 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -25,6 +25,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" #ifndef MATH_STANDALONE @@ -328,6 +329,153 @@ int mathutils_array_parse_alloc_v(float **array, return size; } +/* Parse an sequence array_dim integers into array. */ +int mathutils_int_array_parse(int *array, int array_dim, PyObject *value, const char *error_prefix) +{ + int size, i; + PyObject *value_fast, **value_fast_items, *item; + + if (!(value_fast = PySequence_Fast(value, error_prefix))) { + /* PySequence_Fast sets the error */ + return -1; + } + + if ((size = PySequence_Fast_GET_SIZE(value_fast)) != array_dim) { + PyErr_Format(PyExc_ValueError, + "%.200s: sequence size is %d, expected %d", + error_prefix, + size, + array_dim); + Py_DECREF(value_fast); + return -1; + } + + value_fast_items = PySequence_Fast_ITEMS(value_fast); + i = size; + while (i > 0) { + i--; + if (((array[i] = PyC_Long_AsI32((item = value_fast_items[i]))) == -1) && PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, "%.200s: sequence index %d expected an int", error_prefix, i); + size = -1; + break; + } + } + Py_DECREF(value_fast); + + return size; +} + +/* Parse sequence of array_dim sequences of integers and return allocated result. */ +int mathutils_array_parse_alloc_vi(int **array, + int array_dim, + PyObject *value, + const char *error_prefix) +{ + PyObject *value_fast; + int i, size; + + if (!(value_fast = PySequence_Fast(value, error_prefix))) { + /* PySequence_Fast sets the error */ + return -1; + } + + size = PySequence_Fast_GET_SIZE(value_fast); + + if (size != 0) { + PyObject **value_fast_items = PySequence_Fast_ITEMS(value_fast); + int *ip; + + ip = *array = PyMem_Malloc(size * array_dim * sizeof(int)); + + for (i = 0; i < size; i++, ip += array_dim) { + PyObject *item = value_fast_items[i]; + + if (mathutils_int_array_parse(ip, array_dim, item, error_prefix) == -1) { + PyMem_Free(*array); + *array = NULL; + size = -1; + break; + } + } + } + + Py_DECREF(value_fast); + return size; +} + +/* Parse sequence of variable-length sequences of int and return allocated + * triple of arrays to represent the result: + * The flattened sequences are put into *array. + * The start index of each sequence goes into start_table. + * The length of each index goes into len_table. + */ +int mathutils_array_parse_alloc_viseq( + int **array, int **start_table, int **len_table, PyObject *value, const char *error_prefix) +{ + PyObject *value_fast, *subseq; + int i, size, start, subseq_len; + int *ip; + + *array = NULL; + *start_table = NULL; + *len_table = NULL; + if (!(value_fast = PySequence_Fast(value, error_prefix))) { + /* PySequence_Fast sets the error */ + return -1; + } + + size = PySequence_Fast_GET_SIZE(value_fast); + + if (size != 0) { + PyObject **value_fast_items = PySequence_Fast_ITEMS(value_fast); + + *start_table = PyMem_Malloc(size * sizeof(int)); + *len_table = PyMem_Malloc(size * sizeof(int)); + + /* First pass to set starts and len, and calculate size of array needed */ + start = 0; + for (i = 0; i < size; i++) { + subseq = value_fast_items[i]; + if ((subseq_len = (int)PySequence_Size(subseq)) == -1) { + PyErr_Format( + PyExc_ValueError, "%.200s: sequence expected to have subsequences", error_prefix); + PyMem_Free(*start_table); + PyMem_Free(*len_table); + Py_DECREF(value_fast); + *start_table = NULL; + *len_table = NULL; + return -1; + } + (*start_table)[i] = start; + (*len_table)[i] = subseq_len; + start += subseq_len; + } + + ip = *array = PyMem_Malloc(start * sizeof(int)); + + /* Second pass to parse the subsequences into array */ + for (i = 0; i < size; i++) { + subseq = value_fast_items[i]; + subseq_len = (*len_table)[i]; + + if (mathutils_int_array_parse(ip, subseq_len, subseq, error_prefix) == -1) { + PyMem_Free(*array); + PyMem_Free(*start_table); + PyMem_Free(*len_table); + *array = NULL; + *len_table = NULL; + *start_table = NULL; + size = -1; + break; + } + ip += subseq_len; + } + } + + Py_DECREF(value_fast); + return size; +} + int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix) { if (EulerObject_Check(value)) { diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h index 8afd60a7324..70bd3a64481 100644 --- a/source/blender/python/mathutils/mathutils.h +++ b/source/blender/python/mathutils/mathutils.h @@ -96,16 +96,16 @@ int EXPP_VectorsAreEqual(const float *vecA, const float *vecB, int size, int flo typedef struct Mathutils_Callback Mathutils_Callback; -typedef int (*BaseMathCheckFunc)(BaseMathObject *); /* checks the user is still valid */ -typedef int (*BaseMathGetFunc)(BaseMathObject *, int); /* gets the vector from the user */ -typedef int (*BaseMathSetFunc)(BaseMathObject *, - int); /* sets the users vector values once its modified */ -typedef int (*BaseMathGetIndexFunc)(BaseMathObject *, - int, - int); /* same as above but only for an index */ -typedef int (*BaseMathSetIndexFunc)(BaseMathObject *, - int, - int); /* same as above but only for an index */ +/** Checks the user is still valid. */ +typedef int (*BaseMathCheckFunc)(BaseMathObject *); +/** Gets the vector from the user. */ +typedef int (*BaseMathGetFunc)(BaseMathObject *, int); +/** Sets the users vector values once its modified. */ +typedef int (*BaseMathSetFunc)(BaseMathObject *, int); +/** Same as above but only for an index. */ +typedef int (*BaseMathGetIndexFunc)(BaseMathObject *, int, int); +/** Same as above but only for an index. */ +typedef int (*BaseMathSetIndexFunc)(BaseMathObject *, int, int); struct Mathutils_Callback { BaseMathCheckFunc check; @@ -167,6 +167,16 @@ int mathutils_array_parse_alloc_v(float **array, int array_dim, PyObject *value, const char *error_prefix); +int mathutils_int_array_parse(int *array, + int array_dim, + PyObject *value, + const char *error_prefix); +int mathutils_array_parse_alloc_vi(int **array, + int array_dim, + PyObject *value, + const char *error_prefix); +int mathutils_array_parse_alloc_viseq( + int **array, int **start_table, int **len_table, PyObject *value, const char *error_prefix); int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix); Py_hash_t mathutils_array_hash(const float *float_array, size_t array_len); diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index 23fd65319a6..267971408bf 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -1015,7 +1015,7 @@ static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2) return NULL; } /*------------------------obj @= obj------------------------------ - * inplace quaternion multiplication */ + * in-place quaternion multiplication */ static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2) { float quat[QUAT_SIZE]; diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 490a1d9dd76..aa7cbadde14 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -786,10 +786,8 @@ static PyObject *Vector_to_track_quat(VectorObject *self, PyObject *args) return NULL; } - /* - * flip vector around, since vectoquat expect a vector from target to tracking object - * and the python function expects the inverse (a vector to the target). - */ + /* Flip vector around, since #vec_to_quat expect a vector from target to tracking object + * and the python function expects the inverse (a vector to the target). */ negate_v3_v3(vec, self->vec); vec_to_quat(quat, vec, track, up); diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index d4f56490627..13d36e5af91 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -29,6 +29,7 @@ # include "BLI_blenlib.h" # include "BLI_boxpack_2d.h" # include "BLI_convexhull_2d.h" +# include "BLI_delaunay_2d.h" # include "BKE_displist.h" # include "BKE_curve.h" #endif @@ -283,6 +284,42 @@ static PyObject *M_Geometry_intersect_sphere_sphere_2d(PyObject *UNUSED(self), P return ret; } +PyDoc_STRVAR(M_Geometry_intersect_tri_tri_2d_doc, + ".. function:: intersect_tri_tri_2d(tri_a1, tri_a2, tri_a3, tri_b1, tri_b2, tri_b3)\n" + "\n" + " Check if two 2D triangles intersect.\n" + "\n" + " :rtype: bool\n"); +static PyObject *M_Geometry_intersect_tri_tri_2d(PyObject *UNUSED(self), PyObject *args) +{ + const char *error_prefix = "intersect_tri_tri_2d"; + PyObject *tri_pair_py[2][3]; + float tri_pair[2][3][2]; + + if (!PyArg_ParseTuple(args, + "OOOOOO:intersect_tri_tri_2d", + &tri_pair_py[0][0], + &tri_pair_py[0][1], + &tri_pair_py[0][2], + &tri_pair_py[1][0], + &tri_pair_py[1][1], + &tri_pair_py[1][2])) { + return NULL; + } + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + if (mathutils_array_parse( + tri_pair[i][j], 2, 2 | MU_ARRAY_SPILL, tri_pair_py[i][j], error_prefix) == -1) { + return NULL; + } + } + } + + bool ret = isect_tri_tri_v2(UNPACK3(tri_pair[0]), UNPACK3(tri_pair[1])); + return PyBool_FromLong(ret); +} + PyDoc_STRVAR(M_Geometry_normal_doc, ".. function:: normal(vectors)\n" "\n" @@ -1176,7 +1213,7 @@ static PyObject *M_Geometry_tessellate_polygon(PyObject *UNUSED(self), PyObject PyObject *polyLine, *polyVec; int i, len_polylines, len_polypoints, ls_error = 0; - /* display listbase */ + /* Display #ListBase. */ ListBase dispbase = {NULL, NULL}; DispList *dl; float *fp; /*pointer to the array of malloced dl->verts to set the points from the vectors */ @@ -1262,7 +1299,7 @@ static PyObject *M_Geometry_tessellate_polygon(PyObject *UNUSED(self), PyObject BKE_displist_fill(&dispbase, &dispbase, NULL, false); /* The faces are stored in a new DisplayList - * that's added to the head of the listbase */ + * that's added to the head of the #ListBase. */ dl = dispbase.first; tri_list = PyList_New(dl->parts); @@ -1471,6 +1508,186 @@ static PyObject *M_Geometry_convex_hull_2d(PyObject *UNUSED(self), PyObject *poi return ret; } +/* Return a PyObject that is a list of lists, using the flattened list array + * to fill values, with start_table and len_table giving the start index + * and length of the toplevel_len sub-lists. + */ +static PyObject *list_of_lists_from_arrays(int *array, + int *start_table, + int *len_table, + int toplevel_len) +{ + PyObject *ret, *sublist; + int i, j, sublist_len, sublist_start, val; + + ret = PyList_New(toplevel_len); + for (i = 0; i < toplevel_len; i++) { + sublist_len = len_table[i]; + sublist = PyList_New(sublist_len); + sublist_start = start_table[i]; + for (j = 0; j < sublist_len; j++) { + val = array[sublist_start + j]; + PyList_SET_ITEM(sublist, j, PyLong_FromLong(val)); + } + PyList_SET_ITEM(ret, i, sublist); + } + return ret; +} + +PyDoc_STRVAR( + M_Geometry_delaunay_2d_cdt_doc, + ".. function:: delaunay_2d_cdt(vert_coords, edges, faces, output_type, epsilon)\n" + "\n" + "Computes the Constrained Delaunay Triangulation of a set of vertices, " + "with edges and faces that must appear in the triangulation. " + "Some triangles may be eaten away, or combined with other triangles, " + "according to output type. " + "The returned verts may be in a different order from input verts, may be moved " + "slightly, and may be merged with other nearby verts. " + "The three returned orig lists give, for each of verts, edges, and faces, the list of " + "input element indices corresponding to the positionally same output element. " + "For edges, the orig indices start with the input edges and then continue " + "with the edges implied by each of the faces (n of them for an n-gon).\n" + "\n" + " :arg vert_coords: Vertex coordinates (2d)\n" + " :type vert_coords: list of :class:`mathutils.Vector`\n" + " :arg edges: Edges, as pairs of indices in `vert_coords`\n" + " :type edges: list of (int, int)\n" + " :arg faces: Faces, each sublist is a face, as indices in `vert_coords` (CCW oriented)\n" + " :type faces: list of list of int\n" + " :arg output_type: What output looks like. 0 => triangles with convex hull. " + "1 => triangles inside constraints. " + "2 => the input constraints, intersected. " + "3 => like 2 but with extra edges to make valid BMesh faces.\n" + " :type output_type: int\\n" + " :arg epsilon: For nearness tests; should not be zero\n" + " :type epsilon: float\n" + " :return: Output tuple, (vert_coords, edges, faces, orig_verts, orig_edges, orig_faces)\n" + " :rtype: (list of `mathutils.Vector`, " + "list of (int, int), " + "list of list of int, " + "list of list of int, " + "list of list of int, " + "list of list of int)\n" + "\n"); +static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *args) +{ + const char *error_prefix = "delaunay_2d_cdt"; + PyObject *vert_coords, *edges, *faces, *item; + int output_type; + float epsilon; + float(*in_coords)[2] = NULL; + int(*in_edges)[2] = NULL; + int *in_faces = NULL; + int *in_faces_start_table = NULL; + int *in_faces_len_table = NULL; + Py_ssize_t vert_coords_len, edges_len, faces_len; + CDT_input in; + CDT_result *res = NULL; + PyObject *out_vert_coords = NULL; + PyObject *out_edges = NULL; + PyObject *out_faces = NULL; + PyObject *out_orig_verts = NULL; + PyObject *out_orig_edges = NULL; + PyObject *out_orig_faces = NULL; + PyObject *ret_value = NULL; + int i; + + if (!PyArg_ParseTuple( + args, "OOOif:delaunay_2d_cdt", &vert_coords, &edges, &faces, &output_type, &epsilon)) { + return NULL; + } + + vert_coords_len = mathutils_array_parse_alloc_v( + (float **)&in_coords, 2, vert_coords, error_prefix); + if (vert_coords_len == -1) { + return NULL; + } + + edges_len = mathutils_array_parse_alloc_vi((int **)&in_edges, 2, edges, error_prefix); + if (edges_len == -1) { + goto exit_cdt; + } + + faces_len = mathutils_array_parse_alloc_viseq( + &in_faces, &in_faces_start_table, &in_faces_len_table, faces, error_prefix); + if (faces_len == -1) { + goto exit_cdt; + } + + in.verts_len = (int)vert_coords_len; + in.vert_coords = in_coords; + in.edges_len = edges_len; + in.faces_len = faces_len; + in.edges = in_edges; + in.faces = in_faces; + in.faces_start_table = in_faces_start_table; + in.faces_len_table = in_faces_len_table; + in.epsilon = epsilon; + + res = BLI_delaunay_2d_cdt_calc(&in, output_type); + + ret_value = PyTuple_New(6); + + out_vert_coords = PyList_New(res->verts_len); + for (i = 0; i < res->verts_len; i++) { + item = Vector_CreatePyObject(res->vert_coords[i], 2, NULL); + if (item == NULL) { + Py_DECREF(ret_value); + Py_DECREF(out_vert_coords); + goto exit_cdt; + } + PyList_SET_ITEM(out_vert_coords, i, item); + } + PyTuple_SET_ITEM(ret_value, 0, out_vert_coords); + + out_edges = PyList_New(res->edges_len); + for (i = 0; i < res->edges_len; i++) { + item = PyTuple_New(2); + PyTuple_SET_ITEM(item, 0, PyLong_FromLong((long)res->edges[i][0])); + PyTuple_SET_ITEM(item, 1, PyLong_FromLong((long)res->edges[i][1])); + PyList_SET_ITEM(out_edges, i, item); + } + PyTuple_SET_ITEM(ret_value, 1, out_edges); + + out_faces = list_of_lists_from_arrays( + res->faces, res->faces_start_table, res->faces_len_table, res->faces_len); + PyTuple_SET_ITEM(ret_value, 2, out_faces); + + out_orig_verts = list_of_lists_from_arrays( + res->verts_orig, res->verts_orig_start_table, res->verts_orig_len_table, res->verts_len); + PyTuple_SET_ITEM(ret_value, 3, out_orig_verts); + + out_orig_edges = list_of_lists_from_arrays( + res->edges_orig, res->edges_orig_start_table, res->edges_orig_len_table, res->edges_len); + PyTuple_SET_ITEM(ret_value, 4, out_orig_edges); + + out_orig_faces = list_of_lists_from_arrays( + res->faces_orig, res->faces_orig_start_table, res->faces_orig_len_table, res->faces_len); + PyTuple_SET_ITEM(ret_value, 5, out_orig_faces); + +exit_cdt: + if (in_coords != NULL) { + PyMem_Free(in_coords); + } + if (in_edges != NULL) { + PyMem_Free(in_edges); + } + if (in_faces != NULL) { + PyMem_Free(in_faces); + } + if (in_faces_start_table != NULL) { + PyMem_Free(in_faces_start_table); + } + if (in_faces_len_table != NULL) { + PyMem_Free(in_faces_len_table); + } + if (res) { + BLI_delaunay_2d_cdt_free(res); + } + return ret_value; +} + #endif /* MATH_STANDALONE */ static PyMethodDef M_Geometry_methods[] = { @@ -1526,6 +1743,10 @@ static PyMethodDef M_Geometry_methods[] = { (PyCFunction)M_Geometry_intersect_sphere_sphere_2d, METH_VARARGS, M_Geometry_intersect_sphere_sphere_2d_doc}, + {"intersect_tri_tri_2d", + (PyCFunction)M_Geometry_intersect_tri_tri_2d, + METH_VARARGS, + M_Geometry_intersect_tri_tri_2d_doc}, {"area_tri", (PyCFunction)M_Geometry_area_tri, METH_VARARGS, M_Geometry_area_tri_doc}, {"volume_tetrahedron", (PyCFunction)M_Geometry_volume_tetrahedron, @@ -1553,6 +1774,10 @@ static PyMethodDef M_Geometry_methods[] = { (PyCFunction)M_Geometry_convex_hull_2d, METH_O, M_Geometry_convex_hull_2d_doc}, + {"delaunay_2d_cdt", + (PyCFunction)M_Geometry_delaunay_2d_cdt, + METH_VARARGS, + M_Geometry_delaunay_2d_cdt_doc}, {"box_fit_2d", (PyCFunction)M_Geometry_box_fit_2d, METH_O, M_Geometry_box_fit_2d_doc}, {"box_pack_2d", (PyCFunction)M_Geometry_box_pack_2d, METH_O, M_Geometry_box_pack_2d_doc}, #endif diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index 93b85b6b96a..01a9e1a538b 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -256,7 +256,7 @@ void RE_SetView(struct Render *re, float mat[4][4]); /* get current view and window transform */ void RE_GetViewPlane(struct Render *re, rctf *r_viewplane, rcti *r_disprect); -/* set the render threads based on the commandline and autothreads setting */ +/* set the render threads based on the command-line and autothreads setting */ void RE_init_threadcount(Render *re); bool RE_WriteRenderViewsImage(struct ReportList *reports, diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c index e9175d8d024..9672184cec8 100644 --- a/source/blender/render/intern/source/imagetexture.c +++ b/source/blender/render/intern/source/imagetexture.c @@ -267,7 +267,7 @@ int imagewrap(Tex *tex, if (texres->nor) { if (tex->imaflag & TEX_NORMALMAP) { - /* qdn: normal from color + /* Normal from color: * The invert of the red channel is to make * the normal map compliant with the outside world. * It needs to be done because in Blender diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index d620cd38b76..b4d0c2147f2 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1680,7 +1680,6 @@ static void do_render_all_options(Render *re) { Object *camera; bool render_seq = false; - int cfra = re->r.cfra; re->current_scene_update(re->suh, re->scene); @@ -1692,16 +1691,6 @@ static void do_render_all_options(Render *re) BKE_image_all_free_anim_ibufs(re->main, re->r.cfra); BKE_sequencer_all_free_anim_ibufs(re->scene, re->r.cfra); - /* Update for sequencer and compositing animation. - * TODO: ideally we would create a depsgraph with a copy of the scene - * like the render engine, but sequencer and compositing do not (yet?) - * work with copy-on-write. */ - BKE_animsys_evaluate_all_animation(re->main, NULL, re->scene, (float)cfra); - - /* Update for masks - * (these do not use animsys but own lighter weight structure to define animation). */ - BKE_mask_evaluate_all_masks(re->main, (float)cfra, true); - if (RE_engine_render(re, 1)) { /* in this case external render overrides all */ } @@ -2003,8 +1992,8 @@ static int render_initialize_from_main(Render *re, winx = (rd->size * rd->xsch) / 100; winy = (rd->size * rd->ysch) / 100; - /* We always render smaller part, inserting it in larger image is compositor bizz, - * it uses disprect for it. */ + /* We always render smaller part, inserting it in larger image is compositor business, + * it uses 'disprect' for it. */ if (scene->r.mode & R_BORDER) { disprect.xmin = rd->border.xmin * winx; disprect.xmax = rd->border.xmax * winx; diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 64f506f03a8..ddd0ddb46da 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -69,6 +69,7 @@ set(SRC intern/wm_operators.c intern/wm_panel_type.c intern/wm_playanim.c + intern/wm_splash_screen.c intern/wm_stereo.c intern/wm_subwindow.c intern/wm_toolsystem.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index a479ab62b88..6ff2f11226a 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -162,6 +162,7 @@ enum { WM_WINDOW_RENDER = 1, WM_WINDOW_USERPREFS, WM_WINDOW_DRIVERS, + WM_WINDOW_INFO, WM_WINDOW_FILESEL, }; @@ -420,11 +421,14 @@ int WM_operator_call_py(struct bContext *C, struct ReportList *reports, const bool is_undo); +/* Used for keymap and macro items. */ void WM_operator_properties_alloc(struct PointerRNA **ptr, struct IDProperty **properties, - const char *opstring); /* used for keymap and macro items */ -void WM_operator_properties_sanitize( - struct PointerRNA *ptr, const bool no_context); /* make props context sensitive or not */ + const char *opstring); + +/* Make props context sensitive or not. */ +void WM_operator_properties_sanitize(struct PointerRNA *ptr, const bool no_context); + bool WM_operator_properties_default(struct PointerRNA *ptr, const bool do_update); void WM_operator_properties_reset(struct wmOperator *op); void WM_operator_properties_create(struct PointerRNA *ptr, const char *opstring); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 4e9604545ca..93df9acf6b1 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1406,8 +1406,10 @@ static int wm_operator_invoke(bContext *C, if (WM_operator_poll(C, ot)) { wmWindowManager *wm = CTX_wm_manager(C); - wmOperator *op = wm_operator_create( - wm, ot, properties, reports); /* if reports == NULL, they'll be initialized */ + + /* if reports == NULL, they'll be initialized */ + wmOperator *op = wm_operator_create(wm, ot, properties, reports); + const bool is_nested_call = (wm->op_undo_depth != 0); if (event != NULL) { diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index eeec78729ea..68ea38287e4 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1351,7 +1351,7 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo } } - /* Call pre-save callbacks befores writing preview, + /* Call pre-save callbacks before writing preview, * that way you can generate custom file thumbnail. */ BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_PRE); @@ -1804,7 +1804,9 @@ static void wm_userpref_update_when_changed(bContext *C, BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()"); #endif + WM_reinit_gizmomap_all(bmain); WM_keyconfig_reload(C); + userdef_curr->runtime.is_dirty = is_dirty; } @@ -1959,6 +1961,10 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op) if (use_userdef) { wm_userpref_read_exceptions(&U, &U_backup); SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT); + + if (use_factory_settings) { + U.runtime.is_dirty = true; + } } return OPERATOR_FINISHED; diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index f2db68ec6b8..058348ca4b9 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -512,8 +512,9 @@ void WM_operator_properties_checker_interval_from_op(struct wmOperator *op, op_params->nth = nth; op_params->skip = skip; - op_params->offset = mod_i(offset, - nth + skip); /* so input of offset zero ends up being (nth - 1) */ + + /* So input of offset zero ends up being (nth - 1). */ + op_params->offset = mod_i(offset, nth + skip); } bool WM_operator_properties_checker_interval_test(const struct CheckerIntervalParams *op_params, diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index c984191076c..75d262f48a0 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -46,7 +46,6 @@ #include "DNA_scene_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" -#include "DNA_workspace_types.h" #include "BLT_translation.h" @@ -58,10 +57,6 @@ #include "BLI_math.h" #include "BLI_utildefines.h" -#include "BLO_readfile.h" - -#include "BKE_appdir.h" -#include "BKE_blender_version.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_global.h" @@ -87,7 +82,6 @@ #include "GPU_state.h" #include "IMB_imbuf_types.h" -#include "IMB_imbuf.h" #include "ED_numinput.h" #include "ED_screen.h" @@ -107,14 +101,17 @@ #include "wm.h" #include "wm_draw.h" -#include "wm_event_system.h" #include "wm_event_types.h" #include "wm_files.h" #include "wm_window.h" #define UNDOCUMENTED_OPERATOR_TIP N_("(undocumented operator)") -/* ************ operator API, exported ********** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operator API + * \{ */ /* SOME_OT_op -> some.op */ void WM_operator_py_idname(char *to, const char *from) @@ -695,7 +692,11 @@ void WM_operator_properties_free(PointerRNA *ptr) } } -/* ************ default op callbacks, exported *********** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Default Operator Callbacks + * \{ */ void WM_operator_view3d_unit_defaults(struct bContext *C, struct wmOperator *op) { @@ -1395,7 +1396,13 @@ int WM_operator_redo_popup(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -/* ***************** Debug menu ************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug Menu Operator + * + * Set internal debug value, mainly for developers. + * \{ */ static int wm_debug_menu_exec(bContext *C, wmOperator *op) { @@ -1425,7 +1432,12 @@ static void WM_OT_debug_menu(wmOperatorType *ot) RNA_def_int(ot->srna, "debug_value", 0, SHRT_MIN, SHRT_MAX, "Debug Value", "", -10000, 10000); } -/* ***************** Operator defaults ************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Reset Defaults Operator + * \{ */ + static int wm_operator_defaults_exec(bContext *C, wmOperator *op) { PointerRNA ptr = CTX_data_pointer_get_type(C, "active_operator", &RNA_Operator); @@ -1451,260 +1463,11 @@ static void WM_OT_operator_defaults(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; } -/* ***************** Splash Screen ************************* */ - -static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg)) -{ - wmWindow *win = CTX_wm_window(C); - UI_popup_block_close(C, win, arg_block); -} - -static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *arg_unused); - -static void wm_block_splash_refreshmenu(bContext *C, void *UNUSED(arg_block), void *UNUSED(arg)) -{ - ARegion *ar_menu = CTX_wm_menu(C); - ED_region_tag_refresh_ui(ar_menu); -} - -static void wm_block_splash_add_label(uiBlock *block, const char *label, int x, int *y) -{ - if (!(label && label[0])) { - return; - } - - uiStyle *style = UI_style_get(); - - BLF_size(style->widgetlabel.uifont_id, style->widgetlabel.points, U.pixelsize * U.dpi); - int label_width = BLF_width(style->widgetlabel.uifont_id, label, strlen(label)); - label_width = label_width + U.widget_unit; - - UI_block_emboss_set(block, UI_EMBOSS_NONE); - - uiBut *but = uiDefBut(block, - UI_BTYPE_LABEL, - 0, - label, - x - label_width, - *y, - label_width, - UI_UNIT_Y, - NULL, - 0, - 0, - 0, - 0, - NULL); - - /* 1 = UI_SELECT, internal flag to draw in white. */ - UI_but_flag_enable(but, 1); - UI_block_emboss_set(block, UI_EMBOSS); - *y -= 12 * U.dpi_fac; -} - -static void wm_block_splash_add_labels(uiBlock *block, int x, int y) -{ - /* Version number. */ - const char *version_suffix = NULL; - bool show_build_info = true; - - if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha")) { - version_suffix = " Alpha"; - } - else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "beta")) { - version_suffix = " Beta"; - } - else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) { - version_suffix = " Release Candidate"; - show_build_info = false; - } - else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) { - version_suffix = STRINGIFY(BLENDER_VERSION_CHAR); - show_build_info = false; - } - - char version_buf[256] = "\0"; - BLI_snprintf(version_buf, - sizeof(version_buf), - "v %d.%d%s", - BLENDER_VERSION / 100, - BLENDER_VERSION % 100, - version_suffix); - - wm_block_splash_add_label(block, version_buf, x, &y); - -#ifdef WITH_BUILDINFO - if (show_build_info) { - extern unsigned long build_commit_timestamp; - extern char build_hash[], build_commit_date[], build_commit_time[], build_branch[]; - - /* Date, hidden for builds made from tag. */ - if (build_commit_timestamp != 0) { - char date_buf[256] = "\0"; - BLI_snprintf( - date_buf, sizeof(date_buf), "Date: %s %s", build_commit_date, build_commit_time); - wm_block_splash_add_label(block, date_buf, x, &y); - } - - /* Hash. */ - char hash_buf[256] = "\0"; - BLI_snprintf(hash_buf, sizeof(hash_buf), "Hash: %s", build_hash); - wm_block_splash_add_label(block, hash_buf, x, &y); - - /* Branch. */ - if (!STREQ(build_branch, "master")) { - char branch_buf[256] = "\0"; - BLI_snprintf(branch_buf, sizeof(branch_buf), "Branch: %s", build_branch); - - wm_block_splash_add_label(block, branch_buf, x, &y); - } - } -#else - UNUSED_VARS(show_build_info); -#endif /* WITH_BUILDINFO */ -} - -static ImBuf *wm_block_splash_image(void) -{ -#ifndef WITH_HEADLESS - extern char datatoc_splash_png[]; - extern int datatoc_splash_png_size; - extern char datatoc_splash_2x_png[]; - extern int datatoc_splash_2x_png_size; - - ImBuf *ibuf = NULL; - - if (U.dpi_fac > 1.0) { - ibuf = IMB_ibImageFromMemory((const uchar *)datatoc_splash_2x_png, - datatoc_splash_2x_png_size, - IB_rect, - NULL, - "<splash screen>"); - } - else { - ibuf = IMB_ibImageFromMemory((const uchar *)datatoc_splash_png, - datatoc_splash_png_size, - IB_rect, - NULL, - "<splash screen>"); - } - - /* overwrite splash with template image */ - if (U.app_template[0] != '\0') { - ImBuf *ibuf_template = NULL; - char splash_filepath[FILE_MAX]; - char template_directory[FILE_MAX]; - - if (BKE_appdir_app_template_id_search( - U.app_template, template_directory, sizeof(template_directory))) { - BLI_join_dirfile(splash_filepath, - sizeof(splash_filepath), - template_directory, - (U.dpi_fac > 1.0) ? "splash_2x.png" : "splash.png"); - ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL); - if (ibuf_template) { - const int x_expect = ibuf->x; - const int y_expect = 250 * (int)U.dpi_fac; - /* don't cover the header text */ - if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) { - memcpy(ibuf->rect, - ibuf_template->rect, - ibuf_template->x * ibuf_template->y * sizeof(char[4])); - } - else { - CLOG_ERROR(WM_LOG_OPERATORS, - "Splash expected %dx%d found %dx%d, ignoring: %s\n", - x_expect, - y_expect, - ibuf_template->x, - ibuf_template->y, - splash_filepath); - } - IMB_freeImBuf(ibuf_template); - } - } - } - return ibuf; -#else - return NULL; -#endif -} - -static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg)) -{ - uiBlock *block; - uiBut *but; - uiStyle *style = UI_style_get(); - - block = UI_block_begin(C, ar, "splash", UI_EMBOSS); - - /* note on UI_BLOCK_NO_WIN_CLIP, the window size is not always synchronized - * with the OS when the splash shows, window clipping in this case gives - * ugly results and clipping the splash isn't useful anyway, just disable it [#32938] */ - UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_KEEP_OPEN | UI_BLOCK_NO_WIN_CLIP); - UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); - - ImBuf *ibuf = wm_block_splash_image(); - but = uiDefBut(block, - UI_BTYPE_IMAGE, - 0, - "", - 0, - 0.5f * U.widget_unit, - U.dpi_fac * 501, - U.dpi_fac * 250, - ibuf, - 0.0, - 0.0, - 0, - 0, - ""); /* button owns the imbuf now */ - UI_but_func_set(but, wm_block_splash_close, block, NULL); - UI_block_func_set(block, wm_block_splash_refreshmenu, block, NULL); - - int x = U.dpi_fac * 502; - int y = U.dpi_fac * 237; - - wm_block_splash_add_labels(block, x, y); - - uiLayout *layout = UI_block_layout(block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - U.dpi_fac * 26, - 0, - U.dpi_fac * 450, - U.dpi_fac * 110, - 0, - style); - - MenuType *mt = WM_menutype_find("WM_MT_splash", true); - if (mt) { - UI_menutype_draw(C, mt, layout); - } - - UI_block_bounds_set_centered(block, 0); - - return block; -} - -static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) -{ - UI_popup_block_invoke(C, wm_block_create_splash, NULL, NULL); - - return OPERATOR_FINISHED; -} - -static void WM_OT_splash(wmOperatorType *ot) -{ - ot->name = "Splash Screen"; - ot->idname = "WM_OT_splash"; - ot->description = "Open the splash screen with release info"; - - ot->invoke = wm_splash_invoke; - ot->poll = WM_operator_winactive; -} +/** \} */ -/* ***************** Search menu ************************* */ +/* -------------------------------------------------------------------- */ +/** \name Operator Search Menu + * \{ */ struct SearchPopupInit_Data { int size[2]; @@ -1913,7 +1676,11 @@ static void WM_OT_call_panel(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ************ window / screen operator definitions ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Window/Screen Operators + * \{ */ /* this poll functions is needed in place of WM_operator_winactive * while it crashes on full screen */ @@ -2002,7 +1769,11 @@ static void WM_OT_quit_blender(wmOperatorType *ot) ot->exec = wm_exit_blender_exec; } -/* *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Console Toggle Operator (WIN32 only) + * \{ */ #if defined(WIN32) @@ -2025,12 +1796,16 @@ static void WM_OT_console_toggle(wmOperatorType *ot) #endif -/* ************ default paint cursors, draw always around cursor *********** */ -/* - * - returns handler to free - * - poll(bContext): returns 1 if draw should happen - * - draw(bContext): drawing callback for paint cursor - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name default paint cursors, draw always around cursor + * + * - Returns handler to free. + * - `poll(bContext)`: returns 1 if draw should happen. + * - `draw(bContext)`: drawing callback for paint cursor. + * + * \{ */ wmPaintCursor *WM_paint_cursor_activate(wmWindowManager *wm, short space_type, @@ -2067,7 +1842,11 @@ bool WM_paint_cursor_end(wmWindowManager *wm, wmPaintCursor *handle) return false; } -/* *********************** radial control ****************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Radial Control Operator + * \{ */ #define WM_RADIAL_CONTROL_DISPLAY_SIZE (200 * UI_DPI_FAC) #define WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE (35 * UI_DPI_FAC) @@ -3031,7 +2810,13 @@ static void WM_OT_radial_control(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ************************** timer for testing ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Redraw Timer Operator + * + * Use for simple benchmarks. + * \{ */ /* uses no type defines, fully local testing function anyway... ;) */ @@ -3223,7 +3008,13 @@ static void WM_OT_redraw_timer(wmOperatorType *ot) 60.0); } -/* ************************** memory statistics for testing ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Report Memory Statistics + * + * Use for testing/debugging. + * \{ */ static int memory_statistics_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) { @@ -3240,7 +3031,13 @@ static void WM_OT_memory_statistics(wmOperatorType *ot) ot->exec = memory_statistics_exec; } -/* *************************** Mat/tex/etc. previews generation ************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Data-Block Preview Generation Operator + * + * Use for material/texture/light ... etc. + * \{ */ typedef struct PreviewsIDEnsureData { bContext *C; @@ -3330,7 +3127,11 @@ static void WM_OT_previews_ensure(wmOperatorType *ot) ot->exec = previews_ensure_exec; } -/* *************************** Datablocks previews clear ************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Data-Block Preview Clear Operator + * \{ */ /* Only types supporting previews currently. */ static const EnumPropertyItem preview_id_type_items[] = { @@ -3429,7 +3230,11 @@ static void WM_OT_previews_clear(wmOperatorType *ot) "Which data-block previews to clear"); } -/* *************************** Doc from UI ************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Doc from UI Operator + * \{ */ static int doc_view_manual_ui_context_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -3462,8 +3267,14 @@ static void WM_OT_doc_view_manual_ui_context(wmOperatorType *ot) ot->exec = doc_view_manual_ui_context_exec; } -/* ******************************************************* */ -/* toggle 3D for current window, turning it fullscreen if needed */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Stereo 3D Operator + * + * Turning it fullscreen if needed. + * \{ */ + static void WM_OT_stereo3d_set(wmOperatorType *ot) { PropertyRNA *prop; @@ -3514,6 +3325,12 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operator Registration & Keymaps + * \{ */ + void wm_operatortypes_register(void) { WM_operatortype_append(WM_OT_window_close); @@ -3717,10 +3534,16 @@ void wm_window_keymap(wmKeyConfig *keyconf) WM_keymap_fix_linking(); } -/** +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Enum Filter Functions + * * Filter functions that can be used with rna_id_itemf() below. * Should return false if 'id' should be excluded. - */ + * + * \{ */ + static bool rna_id_enum_filter_single(ID *id, void *user_data) { return (id != user_data); @@ -3873,3 +3696,5 @@ const EnumPropertyItem *RNA_mask_local_itemf(bContext *C, return rna_id_itemf( C, ptr, r_free, C ? (ID *)CTX_data_main(C)->masks.first : NULL, true, NULL, NULL); } + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_splash_screen.c b/source/blender/windowmanager/intern/wm_splash_screen.c new file mode 100644 index 00000000000..8629997030f --- /dev/null +++ b/source/blender/windowmanager/intern/wm_splash_screen.c @@ -0,0 +1,314 @@ +/* + * 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) 2007 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup wm + * + * This file contains the splash screen logic (the `WM_OT_splash` operator). + * + * - Loads the splash image. + * - Displaying version information. + * - Lists New Files (application templates). + * - Lists Recent files. + * - Links to web sites. + */ + +#include <string.h> + +#include "CLG_log.h" + +#include "DNA_ID.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BKE_appdir.h" +#include "BKE_blender_version.h" +#include "BKE_context.h" +#include "BKE_screen.h" + +#include "BLF_api.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + +#include "ED_screen.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm.h" + +static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg)) +{ + wmWindow *win = CTX_wm_window(C); + UI_popup_block_close(C, win, arg_block); +} + +static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *arg_unused); + +static void wm_block_splash_refreshmenu(bContext *C, void *UNUSED(arg_block), void *UNUSED(arg)) +{ + ARegion *ar_menu = CTX_wm_menu(C); + ED_region_tag_refresh_ui(ar_menu); +} + +static void wm_block_splash_add_label(uiBlock *block, const char *label, int x, int *y) +{ + if (!(label && label[0])) { + return; + } + + uiStyle *style = UI_style_get(); + + BLF_size(style->widgetlabel.uifont_id, style->widgetlabel.points, U.pixelsize * U.dpi); + int label_width = BLF_width(style->widgetlabel.uifont_id, label, strlen(label)); + label_width = label_width + U.widget_unit; + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + + uiBut *but = uiDefBut(block, + UI_BTYPE_LABEL, + 0, + label, + x - label_width, + *y, + label_width, + UI_UNIT_Y, + NULL, + 0, + 0, + 0, + 0, + NULL); + + /* 1 = UI_SELECT, internal flag to draw in white. */ + UI_but_flag_enable(but, 1); + UI_block_emboss_set(block, UI_EMBOSS); + *y -= 12 * U.dpi_fac; +} + +static void wm_block_splash_add_labels(uiBlock *block, int x, int y) +{ + /* Version number. */ + const char *version_suffix = NULL; + bool show_build_info = true; + + if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha")) { + version_suffix = " Alpha"; + } + else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "beta")) { + version_suffix = " Beta"; + } + else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) { + version_suffix = " Release Candidate"; + show_build_info = false; + } + else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) { + version_suffix = STRINGIFY(BLENDER_VERSION_CHAR); + show_build_info = false; + } + + char version_buf[256] = "\0"; + BLI_snprintf(version_buf, + sizeof(version_buf), + "v %d.%d%s", + BLENDER_VERSION / 100, + BLENDER_VERSION % 100, + version_suffix); + + wm_block_splash_add_label(block, version_buf, x, &y); + +#ifdef WITH_BUILDINFO + if (show_build_info) { + extern unsigned long build_commit_timestamp; + extern char build_hash[], build_commit_date[], build_commit_time[], build_branch[]; + + /* Date, hidden for builds made from tag. */ + if (build_commit_timestamp != 0) { + char date_buf[256] = "\0"; + BLI_snprintf( + date_buf, sizeof(date_buf), "Date: %s %s", build_commit_date, build_commit_time); + wm_block_splash_add_label(block, date_buf, x, &y); + } + + /* Hash. */ + char hash_buf[256] = "\0"; + BLI_snprintf(hash_buf, sizeof(hash_buf), "Hash: %s", build_hash); + wm_block_splash_add_label(block, hash_buf, x, &y); + + /* Branch. */ + if (!STREQ(build_branch, "master")) { + char branch_buf[256] = "\0"; + BLI_snprintf(branch_buf, sizeof(branch_buf), "Branch: %s", build_branch); + + wm_block_splash_add_label(block, branch_buf, x, &y); + } + } +#else + UNUSED_VARS(show_build_info); +#endif /* WITH_BUILDINFO */ +} + +static ImBuf *wm_block_splash_image(void) +{ +#ifndef WITH_HEADLESS + extern char datatoc_splash_png[]; + extern int datatoc_splash_png_size; + extern char datatoc_splash_2x_png[]; + extern int datatoc_splash_2x_png_size; + + ImBuf *ibuf = NULL; + + if (U.dpi_fac > 1.0) { + ibuf = IMB_ibImageFromMemory((const uchar *)datatoc_splash_2x_png, + datatoc_splash_2x_png_size, + IB_rect, + NULL, + "<splash screen>"); + } + else { + ibuf = IMB_ibImageFromMemory((const uchar *)datatoc_splash_png, + datatoc_splash_png_size, + IB_rect, + NULL, + "<splash screen>"); + } + + /* overwrite splash with template image */ + if (U.app_template[0] != '\0') { + ImBuf *ibuf_template = NULL; + char splash_filepath[FILE_MAX]; + char template_directory[FILE_MAX]; + + if (BKE_appdir_app_template_id_search( + U.app_template, template_directory, sizeof(template_directory))) { + BLI_join_dirfile(splash_filepath, + sizeof(splash_filepath), + template_directory, + (U.dpi_fac > 1.0) ? "splash_2x.png" : "splash.png"); + ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL); + if (ibuf_template) { + const int x_expect = ibuf->x; + const int y_expect = 250 * (int)U.dpi_fac; + /* don't cover the header text */ + if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) { + memcpy(ibuf->rect, + ibuf_template->rect, + ibuf_template->x * ibuf_template->y * sizeof(char[4])); + } + else { + CLOG_ERROR(WM_LOG_OPERATORS, + "Splash expected %dx%d found %dx%d, ignoring: %s\n", + x_expect, + y_expect, + ibuf_template->x, + ibuf_template->y, + splash_filepath); + } + IMB_freeImBuf(ibuf_template); + } + } + } + return ibuf; +#else + return NULL; +#endif +} + +static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg)) +{ + uiBlock *block; + uiBut *but; + uiStyle *style = UI_style_get(); + + block = UI_block_begin(C, ar, "splash", UI_EMBOSS); + + /* note on UI_BLOCK_NO_WIN_CLIP, the window size is not always synchronized + * with the OS when the splash shows, window clipping in this case gives + * ugly results and clipping the splash isn't useful anyway, just disable it [#32938] */ + UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_KEEP_OPEN | UI_BLOCK_NO_WIN_CLIP); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + + ImBuf *ibuf = wm_block_splash_image(); + but = uiDefBut(block, + UI_BTYPE_IMAGE, + 0, + "", + 0, + 0.5f * U.widget_unit, + U.dpi_fac * 501, + U.dpi_fac * 250, + /* Button owns the imbuf now. */ + ibuf, + 0.0, + 0.0, + 0, + 0, + ""); + UI_but_func_set(but, wm_block_splash_close, block, NULL); + UI_block_func_set(block, wm_block_splash_refreshmenu, block, NULL); + + int x = U.dpi_fac * 502; + int y = U.dpi_fac * 237; + + wm_block_splash_add_labels(block, x, y); + + uiLayout *layout = UI_block_layout(block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + U.dpi_fac * 26, + 0, + U.dpi_fac * 450, + U.dpi_fac * 110, + 0, + style); + + MenuType *mt = WM_menutype_find("WM_MT_splash", true); + if (mt) { + UI_menutype_draw(C, mt, layout); + } + + UI_block_bounds_set_centered(block, 0); + + return block; +} + +static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +{ + UI_popup_block_invoke(C, wm_block_create_splash, NULL, NULL); + + return OPERATOR_FINISHED; +} + +void WM_OT_splash(wmOperatorType *ot) +{ + ot->name = "Splash Screen"; + ot->idname = "WM_OT_splash"; + ot->description = "Open the splash screen with release info"; + + ot->invoke = wm_splash_invoke; + ot->poll = WM_operator_winactive; +} diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 4c095e0dca0..00ed203c208 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -482,8 +482,8 @@ void wm_window_title(wmWindowManager *wm, wmWindow *win) } /* Informs GHOST of unsaved changes, to set window modified visual indicator (macOS) - * and to give hint of unsaved changes for a user warning mechanism in case of OS - * application terminate request (e.g. OS Shortcut Alt+F4, Cmd+Q, (...), or session end). */ + * and to give hint of unsaved changes for a user warning mechanism in case of OS application + * terminate request (e.g. OS Shortcut Alt+F4, Command+Q, (...), or session end). */ GHOST_SetWindowModifiedState(win->ghostwin, (GHOST_TUns8)!wm->file_saved); } } @@ -658,7 +658,7 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm) BLI_assert(G.background == false); - /* no commandline prefsize? then we set this. + /* No command-line prefsize? then we set this. * Note that these values will be used only * when there is no startup.blend yet. */ @@ -900,6 +900,9 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i else if (type == WM_WINDOW_FILESEL) { space_type = SPACE_FILE; } + else if (type == WM_WINDOW_INFO) { + ED_area_newspace(C, sa, SPACE_INFO, false); + } else { BLI_assert(false); } @@ -925,6 +928,9 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i else if (sa->spacetype == SPACE_GRAPH) { title = IFACE_("Blender Drivers Editor"); } + else if (sa->spacetype == SPACE_INFO) { + title = IFACE_("Blender Info Log"); + } else { title = "Blender"; } diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index fa375efb469..00d9a8a2fab 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -78,6 +78,9 @@ void wm_autosave_delete(void); void wm_autosave_read(bContext *C, struct ReportList *reports); void wm_autosave_location(char *filepath); +/* wm_splash_screen.c */ +void WM_OT_splash(wmOperatorType *ot); + /* wm_stereo.c */ void wm_stereo3d_draw_interlace(wmWindow *win, struct ARegion *ar); void wm_stereo3d_draw_anaglyph(wmWindow *win, struct ARegion *ar); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index e911a591881..f74fd57252d 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -678,13 +678,13 @@ elseif(WIN32) if(NOT CMAKE_COMPILER_IS_GNUCC) install( - FILES ${LIBDIR}/python/lib/python${_PYTHON_VERSION_NO_DOTS}.dll + FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( - FILES ${LIBDIR}/python/lib/python${_PYTHON_VERSION_NO_DOTS}_d.dll + FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll DESTINATION "." CONFIGURATIONS Debug ) @@ -696,75 +696,55 @@ elseif(WIN32) install(DIRECTORY DESTINATION ${TARGETDIR_VER}/python) install(DIRECTORY DESTINATION ${TARGETDIR_VER}/python/lib) - # WARNING: its important that 'CMAKE_INSTALL_CONFIG_NAME' is evaluated at build time - # and _NOT_ configuration time, when modifying the lines below, - # check it works in both Release & Debug mode. - # - # Edit with extreme care! - Campbell - - # extract python install( - CODE - " - message(STATUS \"Extracting Python to: \${CMAKE_INSTALL_PREFIX}/${BLENDER_VERSION}/python\") - if(\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Dd][Ee][Bb][Uu][Gg])$\") - set(PYTHON_ZIP \"${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}_d.tar.gz\") - else() - set(PYTHON_ZIP \"${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}.tar.gz\") - endif() - - execute_process( - COMMAND \${CMAKE_COMMAND} -E make_directory - \"\${CMAKE_INSTALL_PREFIX}/${BLENDER_VERSION}/python\" - COMMAND \${CMAKE_COMMAND} -E - chdir \"\${CMAKE_INSTALL_PREFIX}/${BLENDER_VERSION}/python\" - \${CMAKE_COMMAND} -E - tar xzfv \"\${PYTHON_ZIP}\" - ) - unset(PYTHON_ZIP) - " + DIRECTORY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/lib + DESTINATION ${BLENDER_VERSION}/python/ + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + PATTERN ".svn" EXCLUDE + PATTERN "*_d.*" EXCLUDE # * debug libraries * + PATTERN "__pycache__" EXCLUDE # * any cache * + PATTERN "*.pyc" EXCLUDE # * any cache * + PATTERN "*.pyo" EXCLUDE # * any cache * ) - # release/site-packages - install( - DIRECTORY ${LIBDIR}/release/site-packages - DESTINATION ${BLENDER_VERSION}/python/lib + install( + DIRECTORY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/lib + DESTINATION ${BLENDER_VERSION}/python/ + CONFIGURATIONS Debug PATTERN ".svn" EXCLUDE PATTERN "__pycache__" EXCLUDE # * any cache * PATTERN "*.pyc" EXCLUDE # * any cache * PATTERN "*.pyo" EXCLUDE # * any cache *) + ) - if(WITH_PYTHON_INSTALL_NUMPY) - set(PYTHON_NUMPY_VERSION 1.15) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages/numpy/__init__.py - COMMAND ${CMAKE_COMMAND} -E - tar xzvf "${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}_numpy_${PYTHON_NUMPY_VERSION}$<$<CONFIG:Debug>:d>.tar.gz" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages - ) - add_custom_target( - python_numpy ALL - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages/numpy/__init__.py - ) - install( - DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages/numpy - DESTINATION ${BLENDER_VERSION}/python/lib/site-packages - ) - endif() + install( + DIRECTORY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/DLLs + DESTINATION ${BLENDER_VERSION}/python + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + PATTERN "*.pdb" EXCLUDE + PATTERN "*_d.*" EXCLUDE + ) + install( + DIRECTORY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/DLLs + DESTINATION ${BLENDER_VERSION}/python + CONFIGURATIONS Debug + ) - # TODO(sergey): For unti we've got better way to deal with python binary install( - FILES ${LIBDIR}/python/lib/python${_PYTHON_VERSION_NO_DOTS}.dll + FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python.exe DESTINATION ${BLENDER_VERSION}/python/bin CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( - FILES ${LIBDIR}/python/lib/python${_PYTHON_VERSION_NO_DOTS}_d.dll + FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python_d.exe DESTINATION ${BLENDER_VERSION}/python/bin CONFIGURATIONS Debug ) + if(WINDOWS_PYTHON_DEBUG) install( FILES ${LIBDIR}/python/lib/python${_PYTHON_VERSION_NO_DOTS}.pdb diff --git a/source/creator/creator.c b/source/creator/creator.c index d6e1d7e7f5f..6fd472d24c5 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -400,8 +400,8 @@ int main(int argc, /* background render uses this font too */ BKE_vfont_builtin_register(datatoc_bfont_pfb, datatoc_bfont_pfb_size); - /* Initialize ffmpeg if built in, also needed for bg mode if videos are - * rendered via ffmpeg */ + /* Initialize ffmpeg if built in, also needed for background-mode if videos are + * rendered via ffmpeg. */ BKE_sound_init_once(); init_def_material(); @@ -442,7 +442,7 @@ int main(int argc, "this is not intended for typical usage\n\n"); #endif - CTX_py_init_set(C, 1); + CTX_py_init_set(C, true); WM_keyconfig_init(C); #ifdef WITH_FREESTYLE diff --git a/tests/gtests/blenlib/BLI_delaunay_2d_test.cc b/tests/gtests/blenlib/BLI_delaunay_2d_test.cc new file mode 100644 index 00000000000..ce84baf802a --- /dev/null +++ b/tests/gtests/blenlib/BLI_delaunay_2d_test.cc @@ -0,0 +1,818 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +extern "C" { +#include "MEM_guardedalloc.h" +#include "BLI_math.h" +#include "BLI_rand.h" +#include "PIL_time.h" + +#include "BLI_delaunay_2d.h" +} + +#include <iostream> +#include <fstream> +#include <sstream> + +#define DLNY_EPSILON 1e-8 + +static void fill_input_verts(CDT_input *r_input, float (*vcos)[2], int nverts) +{ + r_input->verts_len = nverts; + r_input->edges_len = 0; + r_input->faces_len = 0; + r_input->vert_coords = vcos; + r_input->edges = NULL; + r_input->faces = NULL; + r_input->faces_start_table = NULL; + r_input->faces_len_table = NULL; + r_input->epsilon = 1e-6f; +} + +static void add_input_edges(CDT_input *r_input, int (*edges)[2], int nedges) +{ + r_input->edges_len = nedges; + r_input->edges = edges; +} + +static void add_input_faces( + CDT_input *r_input, int *faces, int *faces_start_table, int *faces_len_table, int nfaces) +{ + r_input->faces_len = nfaces; + r_input->faces = faces; + r_input->faces_start_table = faces_start_table; + r_input->faces_len_table = faces_len_table; +} + +/* which output vert index goes with given input vertex? -1 if not found */ +static int get_output_vert_index(const CDT_result *r, int in_index) +{ + int i, j; + + for (i = 0; i < r->verts_len; i++) { + for (j = 0; j < r->verts_orig_len_table[i]; j++) { + if (r->verts_orig[r->verts_orig_start_table[i] + j] == in_index) { + return i; + } + } + } + return -1; +} + +/* which output edge index is for given output vert indices? */ +static int get_edge(const CDT_result *r, int out_index_1, int out_index_2) +{ + int i; + + for (i = 0; i < r->edges_len; i++) { + if ((r->edges[i][0] == out_index_1 && r->edges[i][1] == out_index_2) || + (r->edges[i][0] == out_index_2 && r->edges[i][1] == out_index_1)) + return i; + } + return -1; +} + +/* return true if given output edge has given input edge id in its originals list */ +static bool out_edge_has_input_id(const CDT_result *r, int out_edge_index, int in_edge_index) +{ + if (r->edges_orig == NULL) + return false; + if (out_edge_index < 0 || out_edge_index >= r->edges_len) + return false; + for (int i = 0; i < r->edges_orig_len_table[out_edge_index]; i++) { + if (r->edges_orig[r->edges_orig_start_table[out_edge_index] + i] == in_edge_index) + return true; + } + return false; +} + +/* which face is for given output vertex ngon? */ +static int get_face(const CDT_result *r, int *out_indices, int nverts) +{ + int f, cycle_start, k, fstart; + bool ok; + + if (r->faces_len == 0) + return -1; + for (f = 0; f < r->faces_len; f++) { + if (r->faces_len_table[f] != nverts) + continue; + fstart = r->faces_start_table[f]; + for (cycle_start = 0; cycle_start < nverts; cycle_start++) { + ok = true; + for (k = 0; ok && k < nverts; k++) { + if (r->faces[fstart + ((cycle_start + k) % nverts)] != out_indices[k]) { + ok = false; + } + } + if (ok) { + return f; + } + } + } + return -1; +} + +static int get_face_tri(const CDT_result *r, int out_index_1, int out_index_2, int out_index_3) +{ + int tri[3]; + + tri[0] = out_index_1; + tri[1] = out_index_2; + tri[2] = out_index_3; + return get_face(r, tri, 3); +} + +/* return true if given otuput face has given input face id in its originals list */ +static bool out_face_has_input_id(const CDT_result *r, int out_face_index, int in_face_index) +{ + if (r->faces_orig == NULL) + return false; + if (out_face_index < 0 || out_face_index >= r->faces_len) + return false; + for (int i = 0; i < r->faces_orig_len_table[out_face_index]; i++) { + if (r->faces_orig[r->faces_orig_start_table[out_face_index] + i] == in_face_index) + return true; + } + return false; +} + +/* for debugging */ +static void dump_result(CDT_result *r) +{ + int i, j; + + fprintf(stderr, "\nRESULT\n"); + fprintf(stderr, + "verts_len=%d edges_len=%d faces_len=%d\n", + r->verts_len, + r->edges_len, + r->faces_len); + fprintf(stderr, "\nvert coords:\n"); + for (i = 0; i < r->verts_len; i++) + fprintf(stderr, "%d: (%f,%f)\n", i, r->vert_coords[i][0], r->vert_coords[i][1]); + fprintf(stderr, "vert orig:\n"); + for (i = 0; i < r->verts_len; i++) { + fprintf(stderr, "%d:", i); + for (j = 0; j < r->verts_orig_len_table[i]; j++) + fprintf(stderr, " %d", r->verts_orig[r->verts_orig_start_table[i] + j]); + fprintf(stderr, "\n"); + } + fprintf(stderr, "\nedges:\n"); + for (i = 0; i < r->edges_len; i++) + fprintf(stderr, "%d: (%d,%d)\n", i, r->edges[i][0], r->edges[i][1]); + if (r->edges_orig) { + fprintf(stderr, "edge orig:\n"); + for (i = 0; i < r->edges_len; i++) { + fprintf(stderr, "%d:", i); + for (j = 0; j < r->edges_orig_len_table[i]; j++) + fprintf(stderr, " %d", r->edges_orig[r->edges_orig_start_table[i] + j]); + fprintf(stderr, "\n"); + } + } + fprintf(stderr, "\nfaces:\n"); + for (i = 0; i < r->faces_len; i++) { + fprintf(stderr, "%d: ", i); + for (j = 0; j < r->faces_len_table[i]; j++) + fprintf(stderr, " %d", r->faces[r->faces_start_table[i] + j]); + fprintf(stderr, "\n"); + } + if (r->faces_orig) { + fprintf(stderr, "face orig:\n"); + for (i = 0; i < r->faces_len; i++) { + fprintf(stderr, "%d:", i); + for (j = 0; j < r->faces_orig_len_table[i]; j++) + fprintf(stderr, " %d", r->faces_orig[r->faces_orig_start_table[i] + j]); + fprintf(stderr, "\n"); + } + } +} + +TEST(delaunay, Empty) +{ + CDT_input in; + CDT_result *out; + + fill_input_verts(&in, NULL, 0); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_NE((CDT_result *)NULL, out); + EXPECT_EQ(out->verts_len, 0); + EXPECT_EQ(out->edges_len, 0); + EXPECT_EQ(out->faces_len, 0); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, OnePt) +{ + CDT_input in; + CDT_result *out; + float p[][2] = {{0.0f, 0.0f}}; + + fill_input_verts(&in, p, 1); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 1); + EXPECT_EQ(out->edges_len, 0); + EXPECT_EQ(out->faces_len, 0); + EXPECT_EQ(out->vert_coords[0][0], 0.0f); + EXPECT_EQ(out->vert_coords[0][1], 0.0f); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, TwoPt) +{ + CDT_input in; + CDT_result *out; + int v0_out, v1_out, e0_out; + float p[][2] = {{0.0f, -0.75f}, {0.0f, 0.75f}}; + + fill_input_verts(&in, p, 2); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 2); + EXPECT_EQ(out->edges_len, 1); + EXPECT_EQ(out->faces_len, 0); + v0_out = get_output_vert_index(out, 0); + v1_out = get_output_vert_index(out, 1); + EXPECT_NE(v0_out, -1); + EXPECT_NE(v1_out, -1); + EXPECT_NE(v0_out, v1_out); + EXPECT_NEAR(out->vert_coords[v0_out][0], p[0][0], in.epsilon); + EXPECT_NEAR(out->vert_coords[v0_out][1], p[0][1], in.epsilon); + EXPECT_NEAR(out->vert_coords[v1_out][0], p[1][0], in.epsilon); + EXPECT_NEAR(out->vert_coords[v1_out][1], p[1][1], in.epsilon); + e0_out = get_edge(out, v0_out, v1_out); + EXPECT_EQ(e0_out, 0); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, ThreePt) +{ + CDT_input in; + CDT_result *out; + int v0_out, v1_out, v2_out; + int e0_out, e1_out, e2_out; + int f0_out; + float p[][2] = {{-0.1f, -0.75f}, {0.1f, 0.75f}, {0.5f, 0.5f}}; + + fill_input_verts(&in, p, 3); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 3); + EXPECT_EQ(out->edges_len, 3); + EXPECT_EQ(out->faces_len, 1); + v0_out = get_output_vert_index(out, 0); + v1_out = get_output_vert_index(out, 1); + v2_out = get_output_vert_index(out, 2); + EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1); + EXPECT_TRUE(v0_out != v1_out && v0_out != v2_out && v1_out != v2_out); + e0_out = get_edge(out, v0_out, v1_out); + e1_out = get_edge(out, v1_out, v2_out); + e2_out = get_edge(out, v2_out, v0_out); + EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1); + EXPECT_TRUE(e0_out != e1_out && e0_out != e2_out && e1_out != e2_out); + f0_out = get_face_tri(out, v0_out, v2_out, v1_out); + EXPECT_EQ(f0_out, 0); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, ThreePtsMerge) +{ + CDT_input in; + CDT_result *out; + int v0_out, v1_out, v2_out; + /* equilateral triangle with side 0.1 */ + float p[][2] = {{-0.05f, -0.05f}, {0.05f, -0.05f}, {0.0f, 0.03660254f}}; + + /* First with epsilon such that points are within that distance of each other */ + fill_input_verts(&in, p, 3); + in.epsilon = 0.21f; + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 1); + EXPECT_EQ(out->edges_len, 0); + EXPECT_EQ(out->faces_len, 0); + v0_out = get_output_vert_index(out, 0); + v1_out = get_output_vert_index(out, 1); + v2_out = get_output_vert_index(out, 2); + EXPECT_EQ(v0_out, 0); + EXPECT_EQ(v1_out, 0); + EXPECT_EQ(v2_out, 0); + BLI_delaunay_2d_cdt_free(out); + /* Now with epsilon such that points are farther away than that. + * Note that the points won't merge with each other if distance is + * less than .01, but that they may merge with points on the Delaunay + * triangulation lines, so make epsilon even smaller to avoid that for + * this test. + */ + in.epsilon = 0.05f; + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 3); + EXPECT_EQ(out->edges_len, 3); + EXPECT_EQ(out->faces_len, 1); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, MixedPts) +{ + CDT_input in; + CDT_result *out; + float p[][2] = {{0.0f, 0.0f}, {-0.5f, -0.5f}, {-0.4f, -0.25f}, {-0.3f, 0.8}}; + int e[][2] = {{0, 1}, {1, 2}, {2, 3}}; + int v0_out, v1_out, v2_out, v3_out; + int e0_out, e1_out, e2_out; + + fill_input_verts(&in, p, 4); + add_input_edges(&in, e, 3); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 4); + EXPECT_EQ(out->edges_len, 6); + v0_out = get_output_vert_index(out, 0); + v1_out = get_output_vert_index(out, 1); + v2_out = get_output_vert_index(out, 2); + v3_out = get_output_vert_index(out, 3); + EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1); + e0_out = get_edge(out, v0_out, v1_out); + e1_out = get_edge(out, v1_out, v2_out); + e2_out = get_edge(out, v2_out, v3_out); + EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1); + EXPECT_TRUE(out_edge_has_input_id(out, e0_out, 0)); + EXPECT_TRUE(out_edge_has_input_id(out, e1_out, 1)); + EXPECT_TRUE(out_edge_has_input_id(out, e2_out, 2)); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, CrossSegs) +{ + CDT_input in; + CDT_result *out; + float p[][2] = {{-0.5f, 0.0f}, {0.5f, 0.0f}, {-0.4f, -0.5f}, {0.4f, 0.5f}}; + int e[][2] = {{0, 1}, {2, 3}}; + int v0_out, v1_out, v2_out, v3_out, v_intersect; + int i; + + fill_input_verts(&in, p, 4); + add_input_edges(&in, e, 2); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 5); + EXPECT_EQ(out->edges_len, 8); + EXPECT_EQ(out->faces_len, 4); + v0_out = get_output_vert_index(out, 0); + v1_out = get_output_vert_index(out, 1); + v2_out = get_output_vert_index(out, 2); + v3_out = get_output_vert_index(out, 3); + EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1); + v_intersect = -1; + for (i = 0; i < out->verts_len; i++) { + if (i != v0_out && i != v1_out && i != v2_out && i != v3_out) { + EXPECT_EQ(v_intersect, -1); + v_intersect = i; + } + } + EXPECT_NE(v_intersect, -1); + EXPECT_NEAR(out->vert_coords[v_intersect][0], 0.0f, in.epsilon); + EXPECT_NEAR(out->vert_coords[v_intersect][1], 0.0f, in.epsilon); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, DiamondCross) +{ + CDT_input in; + CDT_result *out; + float p[][2] = { + {0.0f, 0.0f}, + {1.0f, 3.0f}, + {2.0f, 0.0f}, + {1.0f, -3.0f}, + {0.0f, 0.0f}, + {1.0f, -3.0f}, + {1.0f, 3.0f}, + }; + int e[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}}; + + fill_input_verts(&in, p, 7); + add_input_edges(&in, e, 5); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 4); + EXPECT_EQ(out->edges_len, 5); + EXPECT_EQ(out->faces_len, 2); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, TwoDiamondsCrossed) +{ + CDT_input in; + CDT_result *out; + /* Input has some repetition of vertices, on purpose */ + float p[][2] = { + {0.0f, 0.0f}, + {1.0f, 2.0f}, + {2.0f, 0.0f}, + {1.0f, -2.0f}, + {0.0f, 0.0f}, + {3.0f, 0.0f}, + {4.0f, 2.0f}, + {5.0f, 0.0f}, + {4.0f, -2.0f}, + {3.0f, 0.0f}, + {0.0f, 0.0f}, + {5.0f, 0.0f}, + }; + int e[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {10, 11}}; + int v_out[12]; + int e_out[9], e_cross_1, e_cross_2, e_cross_3; + int i; + + fill_input_verts(&in, p, 12); + add_input_edges(&in, e, 9); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 8); + EXPECT_EQ(out->edges_len, 15); + EXPECT_EQ(out->faces_len, 8); + for (i = 0; i < 12; i++) { + v_out[i] = get_output_vert_index(out, i); + EXPECT_NE(v_out[i], -1); + } + EXPECT_EQ(v_out[0], v_out[4]); + EXPECT_EQ(v_out[0], v_out[10]); + EXPECT_EQ(v_out[5], v_out[9]); + EXPECT_EQ(v_out[7], v_out[11]); + for (i = 0; i < 8; i++) { + e_out[i] = get_edge(out, v_out[e[i][0]], v_out[e[i][1]]); + EXPECT_NE(e_out[i], -1); + } + /* there won't be a single edge for the input cross edge, but rather 3 */ + EXPECT_EQ(get_edge(out, v_out[10], v_out[11]), -1); + e_cross_1 = get_edge(out, v_out[0], v_out[2]); + e_cross_2 = get_edge(out, v_out[2], v_out[5]); + e_cross_3 = get_edge(out, v_out[5], v_out[7]); + EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1); + EXPECT_TRUE(out_edge_has_input_id(out, e_cross_1, 8)); + EXPECT_TRUE(out_edge_has_input_id(out, e_cross_2, 8)); + EXPECT_TRUE(out_edge_has_input_id(out, e_cross_3, 8)); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, ManyCross) +{ + CDT_input in; + CDT_result *out; + /* Input has some repetition of vertices, on purpose */ + float p[][2] = { + /* upper: verts 0 to 10 */ + {0.0f, 0.0f}, + {6.0f, 9.0f}, + {15.0f, 18.0f}, + {35.0f, 13.0f}, + {43.0f, 18.0f}, + {57.0f, 12.0f}, + {69.0f, 10.0f}, + {78.0f, 0.0f}, + {91.0f, 0.0f}, + {107.0f, 22.0f}, + {123.0f, 0.0f}, + /* lower part 1: verts 11 to 16 */ + {0.0f, 0.0f}, + {10.0f, -14.0f}, + {35.0f, -8.0f}, + {43.0f, -12.0f}, + {64.0f, -13.0f}, + {78.0f, 0.0f}, + /* lower part 2: verts 17 to 20 */ + {91.0f, 0.0f}, + {102.0f, -9.0f}, + {116.0f, -9.0f}, + {123.0f, 0.0f}, + /* cross 1: verts 21, 22 */ + {43.0f, 18.0f}, + {43.0f, -12.0f}, + /* cross 2: verts 23, 24 */ + {107.0f, 22.0f}, + {102.0f, -9.0f}, + /* cross all: verts 25, 26 */ + {0.0f, 0.0f}, + {123.0f, 0.0f}, + }; + int e[][2] = { + {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}, + {7, 8}, {8, 9}, {9, 10}, {11, 12}, {12, 13}, {13, 14}, {14, 15}, + {15, 16}, {17, 18}, {18, 19}, {19, 20}, {21, 22}, {23, 24}, {25, 26}, + }; + + fill_input_verts(&in, p, 27); + add_input_edges(&in, e, 21); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 19); + EXPECT_EQ(out->edges_len, 46); + EXPECT_EQ(out->faces_len, 28); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, TwoFace) +{ + CDT_input in; + CDT_result *out; + float p[][2] = { + {0.0f, 0.0f}, {1.0f, 0.0f}, {0.5f, 1.0f}, {1.1f, 1.0f}, {1.1f, 0.0f}, {1.6f, 1.0f}}; + int f[] = {/* 0 */ 0, 1, 2, /* 1 */ 3, 4, 5}; + int fstart[] = {0, 3}; + int flen[] = {3, 3}; + int v_out[6], f0_out, f1_out, e0_out, e1_out, e2_out; + int i; + + fill_input_verts(&in, p, 6); + add_input_faces(&in, f, fstart, flen, 2); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 6); + EXPECT_EQ(out->edges_len, 9); + EXPECT_EQ(out->faces_len, 4); + for (i = 0; i < 6; i++) { + v_out[i] = get_output_vert_index(out, i); + EXPECT_NE(v_out[i], -1); + } + f0_out = get_face(out, &v_out[0], 3); + f1_out = get_face(out, &v_out[3], 3); + EXPECT_NE(f0_out, -1); + EXPECT_NE(f1_out, -1); + e0_out = get_edge(out, v_out[0], v_out[1]); + e1_out = get_edge(out, v_out[1], v_out[2]); + e2_out = get_edge(out, v_out[2], v_out[0]); + EXPECT_NE(e0_out, -1); + EXPECT_NE(e1_out, -1); + EXPECT_NE(e2_out, -1); + EXPECT_TRUE(out_edge_has_input_id(out, e0_out, out->face_edge_offset + 0)); + EXPECT_TRUE(out_edge_has_input_id(out, e1_out, out->face_edge_offset + 1)); + EXPECT_TRUE(out_edge_has_input_id(out, e2_out, out->face_edge_offset + 2)); + EXPECT_TRUE(out_face_has_input_id(out, f0_out, 0)); + EXPECT_TRUE(out_face_has_input_id(out, f1_out, 1)); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, OverlapFaces) +{ + CDT_input in; + CDT_result *out; + float p[][2] = { + {0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 1.0f}, + {0.0f, 1.0f}, + {0.5f, 0.5f}, + {1.5f, 0.5f}, + {1.5f, 1.3f}, + {0.5f, 1.3f}, + {0.1f, 0.1f}, + {0.3f, 0.1f}, + {0.3f, 0.3f}, + {0.1f, 0.3f}, + }; + int f[] = {/* 0 */ 0, 1, 2, 3, /* 1 */ 4, 5, 6, 7, /* 2*/ 8, 9, 10, 11}; + int fstart[] = {0, 4, 8}; + int flen[] = {4, 4, 4}; + int v_out[12], v_int1, v_int2, f0_out, f1_out, f2_out; + int i; + + fill_input_verts(&in, p, 12); + add_input_faces(&in, f, fstart, flen, 3); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + EXPECT_EQ(out->verts_len, 14); + EXPECT_EQ(out->edges_len, 33); + EXPECT_EQ(out->faces_len, 20); + for (i = 0; i < 12; i++) { + v_out[i] = get_output_vert_index(out, i); + EXPECT_NE(v_out[i], -1); + } + v_int1 = 12; + v_int2 = 13; + if (fabsf(out->vert_coords[v_int1][0] - 1.0f) > in.epsilon) { + v_int1 = 13; + v_int2 = 12; + } + EXPECT_NEAR(out->vert_coords[v_int1][0], 1.0, in.epsilon); + EXPECT_NEAR(out->vert_coords[v_int1][1], 0.5, in.epsilon); + EXPECT_NEAR(out->vert_coords[v_int2][0], 0.5, in.epsilon); + EXPECT_NEAR(out->vert_coords[v_int2][1], 1.0, in.epsilon); + f0_out = get_face_tri(out, v_out[1], v_int1, v_out[4]); + EXPECT_NE(f0_out, -1); + EXPECT_TRUE(out_face_has_input_id(out, f0_out, 0)); + f1_out = get_face_tri(out, v_out[4], v_int1, v_out[2]); + EXPECT_NE(f1_out, -1); + EXPECT_TRUE(out_face_has_input_id(out, f1_out, 0)); + EXPECT_TRUE(out_face_has_input_id(out, f1_out, 0)); + f2_out = get_face_tri(out, v_out[8], v_out[9], v_out[10]); + EXPECT_NE(f2_out, -1); + EXPECT_TRUE(out_face_has_input_id(out, f2_out, 0)); + EXPECT_TRUE(out_face_has_input_id(out, f2_out, 2)); + BLI_delaunay_2d_cdt_free(out); + + /* Different output types */ + out = BLI_delaunay_2d_cdt_calc(&in, CDT_INSIDE); + EXPECT_EQ(out->faces_len, 18); + BLI_delaunay_2d_cdt_free(out); + + out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); + EXPECT_EQ(out->faces_len, 4); + BLI_delaunay_2d_cdt_free(out); + + out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out->faces_len, 5); + BLI_delaunay_2d_cdt_free(out); +} + +TEST(delaunay, TwoSquaresOverlap) +{ + CDT_input in; + CDT_result *out; + float p[][2] = { + {1.0f, -1.0f}, + {-1.0f, -1.0f}, + {-1.0f, 1.0f}, + {1.0f, 1.0f}, + {-1.5f, 1.5f}, + {0.5f, 1.5f}, + {0.5f, -0.5f}, + {-1.5f, -0.5f}, + }; + int f[] = {/* 0 */ 7, 6, 5, 4, /* 1 */ 3, 2, 1, 0}; + int fstart[] = {0, 4}; + int flen[] = {4, 4}; + + fill_input_verts(&in, p, 8); + add_input_faces(&in, f, fstart, flen, 2); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out->verts_len, 10); + EXPECT_EQ(out->edges_len, 12); + EXPECT_EQ(out->faces_len, 3); + BLI_delaunay_2d_cdt_free(out); +} + +enum { + RANDOM_PTS, + RANDOM_SEGS, + RANDOM_POLY, +}; + +// #define DO_TIMING +static void rand_delaunay_test(int test_kind, + int max_lg_size, + int reps_per_size, + CDT_output_type otype) +{ + CDT_input in; + CDT_result *out; + int lg_size, size, rep, i, npts, nedges; + float(*p)[2]; + int(*e)[2]; + double tstart; + double *times; + RNG *rng; + + rng = BLI_rng_new(0); + npts = (1 << max_lg_size); + p = (float(*)[2])MEM_malloc_arrayN(npts, 2 * sizeof(float), "delaunay"); + switch (test_kind) { + case RANDOM_PTS: + nedges = 0; + e = NULL; + break; + + case RANDOM_SEGS: + case RANDOM_POLY: + /* TODO: use faces for poly case, but need to deal with winding parity issue */ + nedges = npts - 1 + (test_kind == RANDOM_POLY); + e = (int(*)[2])MEM_malloc_arrayN(nedges, 2 * sizeof(int), "delaunay"); + break; + + default: + fprintf(stderr, "unknown random delaunay test kind\n"); + return; + } + times = (double *)MEM_malloc_arrayN(max_lg_size + 1, sizeof(double), "delaunay"); + for (lg_size = 0; lg_size <= max_lg_size; lg_size++) { + size = 1 << lg_size; + times[lg_size] = 0.0; + if (size == 1 && test_kind != RANDOM_PTS) + continue; + for (rep = 0; rep < reps_per_size; rep++) { + for (i = 0; i < size; i++) { + p[i][0] = (float)BLI_rng_get_double(rng); /* will be in range in [0,1) */ + p[i][1] = (float)BLI_rng_get_double(rng); + } + fill_input_verts(&in, p, size); + + if (test_kind == RANDOM_SEGS || test_kind == RANDOM_POLY) { + for (i = 0; i < size - 1; i++) { + e[i][0] = i; + e[i][1] = i + 1; + } + if (test_kind == RANDOM_POLY) { + e[size - 1][0] = size - 1; + e[size - 1][1] = 0; + } + add_input_edges(&in, e, size - 1 + (test_kind == RANDOM_POLY)); + } + tstart = PIL_check_seconds_timer(); + out = BLI_delaunay_2d_cdt_calc(&in, otype); + EXPECT_NE(out->verts_len, 0); + BLI_delaunay_2d_cdt_free(out); + times[lg_size] += PIL_check_seconds_timer() - tstart; + } + } +#ifdef DO_TIMING + fprintf(stderr, "size,time\n"); + for (lg_size = 0; lg_size <= max_lg_size; lg_size++) { + fprintf(stderr, "%d,%f\n", 1 << lg_size, times[lg_size] / reps_per_size); + } +#endif + MEM_freeN(p); + if (e) + MEM_freeN(e); + MEM_freeN(times); + BLI_rng_free(rng); +} + +TEST(delaunay, randompts) +{ + rand_delaunay_test(RANDOM_PTS, 7, 1, CDT_FULL); +} + +TEST(delaunay, randomsegs) +{ + rand_delaunay_test(RANDOM_SEGS, 7, 1, CDT_FULL); +} + +TEST(delaunay, randompoly) +{ + rand_delaunay_test(RANDOM_POLY, 7, 1, CDT_FULL); +} + +TEST(delaunay, randompoly_inside) +{ + rand_delaunay_test(RANDOM_POLY, 7, 1, CDT_INSIDE); +} + +TEST(delaunay, randompoly_constraints) +{ + rand_delaunay_test(RANDOM_POLY, 7, 1, CDT_CONSTRAINTS); +} + +TEST(delaunay, randompoly_validbmesh) +{ + rand_delaunay_test(RANDOM_POLY, 7, 1, CDT_CONSTRAINTS_VALID_BMESH); +} + +#if 0 +/* For debugging or timing large examples. + * The given file should have one point per line, as a space-separated pair of floats + */ +static void points_from_file_test(const char *filename) +{ + std::ifstream f; + std::string line; + struct XY { + float x; + float y; + } xy; + std::vector<XY> pts; + int i, n; + CDT_input in; + CDT_result *out; + double tstart; + float (*p)[2]; + + f.open(filename); + while (getline(f, line)) { + std::istringstream iss(line); + iss >> xy.x >> xy.y; + pts.push_back(xy); + } + n = (int)pts.size(); + fprintf(stderr, "read %d points\n", (int)pts.size()); + p = (float (*)[2])MEM_malloc_arrayN(n, 2 * sizeof(float), "delaunay"); + for (i = 0; i < n; i++) { + xy = pts[i]; + p[i][0] = xy.x; + p[i][1] = xy.y; + } + tstart = PIL_check_seconds_timer(); + fill_input_verts(&in, p, n); + out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); + fprintf(stderr, "time to triangulate=%f seconds\n", PIL_check_seconds_timer() - tstart); + BLI_delaunay_2d_cdt_free(out); + MEM_freeN(p); +} + +# define POINTFILEROOT "/tmp/" + +TEST(delaunay, terrain1) +{ + points_from_file_test(POINTFILEROOT "points1.txt"); +} + +TEST(delaunay, terrain2) +{ + points_from_file_test(POINTFILEROOT "points2.txt"); +} + +TEST(delaunay, terrain3) +{ + points_from_file_test(POINTFILEROOT "points3.txt"); +} +#endif diff --git a/tests/gtests/blenlib/BLI_path_util_test.cc b/tests/gtests/blenlib/BLI_path_util_test.cc index c07ff2b0b05..0bdaa346428 100644 --- a/tests/gtests/blenlib/BLI_path_util_test.cc +++ b/tests/gtests/blenlib/BLI_path_util_test.cc @@ -618,3 +618,17 @@ TEST(path_util, PathFrameGet) PATH_FRAME_GET("", -1, -1, false); } #undef PATH_FRAME_GET + +/* BLI_path_extension */ +TEST(path_util, PathExtension) +{ + EXPECT_EQ(NULL, BLI_path_extension("some.def/file")); + EXPECT_EQ(NULL, BLI_path_extension("Text")); + EXPECT_EQ(NULL, BLI_path_extension("Text…001")); + + EXPECT_STREQ(".", BLI_path_extension("some/file.")); + EXPECT_STREQ(".gz", BLI_path_extension("some/file.tar.gz")); + EXPECT_STREQ(".abc", BLI_path_extension("some.def/file.abc")); + EXPECT_STREQ(".abc", BLI_path_extension("C:\\some.def\\file.abc")); + EXPECT_STREQ(".001", BLI_path_extension("Text.001")); +} diff --git a/tests/gtests/blenlib/BLI_string_test.cc b/tests/gtests/blenlib/BLI_string_test.cc index bffd38016a7..d69cb519494 100644 --- a/tests/gtests/blenlib/BLI_string_test.cc +++ b/tests/gtests/blenlib/BLI_string_test.cc @@ -12,6 +12,7 @@ extern "C" { #include "BLI_utildefines.h" #include "BLI_string.h" #include "BLI_string_utf8.h" +#include "BLI_string_utils.h" } using std::initializer_list; @@ -401,8 +402,8 @@ TEST(string, StrFormatByteUnits) BLI_str_format_byte_unit(size_str, size = -1024, true); EXPECT_STREQ("-1 KB", size_str); - BLI_str_format_byte_unit( - size_str, size = 9223372036854775807, true); /* LLONG_MAX - largest possible value */ + /* LLONG_MAX - largest possible value */ + BLI_str_format_byte_unit(size_str, size = 9223372036854775807, true); EXPECT_STREQ("9223.372 PB", size_str); BLI_str_format_byte_unit(size_str, size = -9223372036854775807, true); EXPECT_STREQ("-9223.372 PB", size_str); @@ -428,8 +429,8 @@ TEST(string, StrFormatByteUnits) BLI_str_format_byte_unit(size_str, size = -1024, false); EXPECT_STREQ("-1 KiB", size_str); - BLI_str_format_byte_unit( - size_str, size = 9223372036854775807, false); /* LLONG_MAX - largest possible value */ + /* LLONG_MAX - largest possible value */ + BLI_str_format_byte_unit(size_str, size = 9223372036854775807, false); EXPECT_STREQ("8192.0 PiB", size_str); BLI_str_format_byte_unit(size_str, size = -9223372036854775807, false); EXPECT_STREQ("-8192.0 PiB", size_str); @@ -588,3 +589,22 @@ TEST(string, StringStrncasestr) res = BLI_strncasestr(str_test0, "not there", 9); EXPECT_EQ(res, (void *)NULL); } + +/* BLI_string_is_decimal */ +TEST(string, StrIsDecimal) +{ + EXPECT_FALSE(BLI_string_is_decimal("")); + EXPECT_FALSE(BLI_string_is_decimal("je moeder")); + EXPECT_FALSE(BLI_string_is_decimal("je møder")); + EXPECT_FALSE(BLI_string_is_decimal("Agent 327")); + EXPECT_FALSE(BLI_string_is_decimal("Agent\000327")); + EXPECT_FALSE(BLI_string_is_decimal("\000327")); + EXPECT_FALSE(BLI_string_is_decimal("0x16")); + EXPECT_FALSE(BLI_string_is_decimal("16.4")); + EXPECT_FALSE(BLI_string_is_decimal("-1")); + + EXPECT_TRUE(BLI_string_is_decimal("0")); + EXPECT_TRUE(BLI_string_is_decimal("1")); + EXPECT_TRUE(BLI_string_is_decimal("001")); + EXPECT_TRUE(BLI_string_is_decimal("11342908713948713498745980171334059871345098713405981734")); +} diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index c2b5930e649..4e5811d140e 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -40,6 +40,7 @@ endif() BLENDER_TEST(BLI_array_store "bf_blenlib") BLENDER_TEST(BLI_array_utils "bf_blenlib") +BLENDER_TEST(BLI_delaunay_2d "bf_blenlib") BLENDER_TEST(BLI_expr_pylike_eval "bf_blenlib") BLENDER_TEST(BLI_edgehash "bf_blenlib") BLENDER_TEST(BLI_ghash "bf_blenlib") |