diff options
660 files changed, 16141 insertions, 11183 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index bc4d4e032af..cd518227e27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,6 +214,8 @@ if(WITH_GHOST_X11) option(WITH_GHOST_XDND "Enable drag'n'drop support on X11 using XDND protocol" ON) endif() +option(WITH_GMP "Use the gmp library for more accurate booleans" OFF) + # Misc... option(WITH_HEADLESS "Build without graphical support (renderfarm, server mode only)" OFF) mark_as_advanced(WITH_HEADLESS) diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 276311fe2d8..c26b90637c3 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -117,6 +117,7 @@ if(WIN32) endif() if(NOT WIN32 OR ENABLE_MINGW64) + include(cmake/gmp.cmake) include(cmake/openjpeg.cmake) if(NOT WIN32 OR BUILD_MODE STREQUAL Release) if(WIN32) diff --git a/build_files/build_environment/cmake/gmp.cmake b/build_files/build_environment/cmake/gmp.cmake new file mode 100644 index 00000000000..40d8e04b3a7 --- /dev/null +++ b/build_files/build_environment/cmake/gmp.cmake @@ -0,0 +1,88 @@ +# ***** 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(GMP_EXTRA_ARGS -enable-cxx) + +if(WIN32) + # Shared for windows because static libs will drag in a libgcc dependency. + set(GMP_OPTIONS --disable-static --enable-shared --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32) +else() + set(GMP_OPTIONS --enable-static --disable-shared ) +endif() + +ExternalProject_Add(external_gmp + URL ${GMP_URI} + DOWNLOAD_DIR ${DOWNLOAD_DIR} + URL_HASH MD5=${GMP_HASH} + PREFIX ${BUILD_DIR}/gmp + CONFIGURE_COMMAND ${CONFIGURE_ENV_NO_PERL} && cd ${BUILD_DIR}/gmp/src/external_gmp/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/gmp ${GMP_OPTIONS} ${GMP_EXTRA_ARGS} + BUILD_COMMAND ${CONFIGURE_ENV_NO_PERL} && cd ${BUILD_DIR}/gmp/src/external_gmp/ && make -j${MAKE_THREADS} + INSTALL_COMMAND ${CONFIGURE_ENV_NO_PERL} && cd ${BUILD_DIR}/gmp/src/external_gmp/ && make install + INSTALL_DIR ${LIBDIR}/gmp +) + +if(MSVC) + set_target_properties(external_gmp PROPERTIES FOLDER Mingw) +endif() + +if(BUILD_MODE STREQUAL Release AND WIN32) + ExternalProject_Add_Step(external_gmp after_install + COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-3.dll.def ${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-10.def + COMMAND lib /def:${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-10.def /machine:x64 /out:${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-10.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/gmp/bin/libgmp-10.dll ${HARVEST_TARGET}/gmp/lib/libgmp-10.dll + COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-10.lib ${HARVEST_TARGET}/gmp/lib/libgmp-10.lib + COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/gmp/include ${HARVEST_TARGET}/gmp/include + DEPENDEES install + ) +endif() + +if(BUILD_MODE STREQUAL Debug AND WIN32) +ExternalProject_Add_Step(external_gmp after_install + COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-3.dll.def ${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-10.def + COMMAND lib /def:${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-10.def /machine:x64 /out:${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-10.lib + DEPENDEES install + ) +endif() + +if(WIN32) + # gmpxx is somewhat special, it builds on top of the C style gmp library but exposes C++ bindings + # given the C++ ABI between MSVC and mingw is not compatible, we need to build the bindings + # with MSVC, while GMP can only be build with mingw. + ExternalProject_Add(external_gmpxx + URL ${GMP_URI} + DOWNLOAD_DIR ${DOWNLOAD_DIR} + URL_HASH MD5=${GMP_HASH} + PREFIX ${BUILD_DIR}/gmpxx + PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/cmakelists_gmpxx.txt ${BUILD_DIR}/gmpxx/src/external_gmpxx/CMakeLists.txt && + ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/config_gmpxx.h ${BUILD_DIR}/gmpxx/src/external_gmpxx/config.h + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/gmpxx ${DEFAULT_CMAKE_FLAGS} -DGMP_LIBRARY=${BUILD_DIR}/gmp/src/external_gmp/.libs/libgmp-10.lib -DGMP_INCLUDE_DIR=${BUILD_DIR}/gmp/src/external_gmp -DCMAKE_DEBUG_POSTFIX=_d + INSTALL_DIR ${LIBDIR}/gmpxx + ) + set_target_properties(external_gmpxx PROPERTIES FOLDER Mingw) + + add_dependencies( + external_gmpxx + external_gmp + ) + + ExternalProject_Add_Step(external_gmpxx after_install + COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/gmpxx/ ${HARVEST_TARGET}/gmp + DEPENDEES install + ) + +endif() diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake index 3f5d2044dbc..dbb3a33e705 100644 --- a/build_files/build_environment/cmake/harvest.cmake +++ b/build_files/build_environment/cmake/harvest.cmake @@ -89,6 +89,8 @@ harvest(freetype/include freetype/include "*.h") harvest(freetype/lib/libfreetype2ST.a freetype/lib/libfreetype.a) harvest(glew/include glew/include "*.h") harvest(glew/lib glew/lib "*.a") +harvest(gmp/include gmp/include "*.h") +harvest(gmp/lib gmp/lib "*.a") harvest(jemalloc/include jemalloc/include "*.h") harvest(jemalloc/lib jemalloc/lib "*.a") harvest(jpg/include jpeg/include "*.h") diff --git a/build_files/build_environment/cmake/ispc.cmake b/build_files/build_environment/cmake/ispc.cmake index b67351dcf9f..a352f87a9aa 100644 --- a/build_files/build_environment/cmake/ispc.cmake +++ b/build_files/build_environment/cmake/ispc.cmake @@ -73,4 +73,3 @@ if(WIN32) external_flexbison ) endif() - diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index ce2a1191f17..30a0b1184c2 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -315,3 +315,7 @@ set(XR_OPENXR_SDK_HASH c6de63d2e0f9029aa58dfa97cad8ce07) set(ISPC_VERSION v1.13.0) set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz) set(ISPC_HASH 4bf5e8d0020c4b9980faa702c1a6f25f) + +set(GMP_VERSION 6.2.0) +set(GMP_URI https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.xz) +set(GMP_HASH a325e3f09e6d91e62101e59f9bda3ec1) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 49d7b8209ee..4c7c7652d29 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -1030,7 +1030,7 @@ Those libraries should be available as packages in all recent distributions (opt * libjpeg, libpng, libtiff, [openjpeg2], [libopenal]. * libx11, libxcursor, libxi, libxrandr, libxinerama (and other libx... as needed). * libsqlite3, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp. - * libsdl2, libglew, [libglewmx].\"" + * libsdl2, libglew, [libgmp], [libglewmx].\"" DEPS_SPECIFIC_INFO="\"BUILDABLE DEPENDENCIES: @@ -3524,7 +3524,8 @@ install_DEB() { libxcursor-dev libxi-dev wget libsqlite3-dev libxrandr-dev libxinerama-dev \ libbz2-dev libncurses5-dev libssl-dev liblzma-dev libreadline-dev \ libopenal-dev libglew-dev yasm $THEORA_DEV $VORBIS_DEV $OGG_DEV \ - libsdl2-dev libfftw3-dev patch bzip2 libxml2-dev libtinyxml-dev libjemalloc-dev" + libsdl2-dev libfftw3-dev patch bzip2 libxml2-dev libtinyxml-dev libjemalloc-dev \ + libgmp-dev" # libglewmx-dev (broken in deb testing currently...) VORBIS_USE=true @@ -4175,7 +4176,7 @@ install_RPM() { libX11-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel \ wget ncurses-devel readline-devel $OPENJPEG_DEV openal-soft-devel \ glew-devel yasm $THEORA_DEV $VORBIS_DEV $OGG_DEV patch \ - libxml2-devel yaml-cpp-devel tinyxml-devel jemalloc-devel" + libxml2-devel yaml-cpp-devel tinyxml-devel jemalloc-devel gmp-devel" OPENJPEG_USE=true VORBIS_USE=true @@ -4735,7 +4736,7 @@ install_ARCH() { _packages="$BASE_DEVEL git cmake \ libxi libxcursor libxrandr libxinerama glew libpng libtiff wget openal \ $OPENJPEG_DEV $VORBIS_DEV $OGG_DEV $THEORA_DEV yasm sdl2 fftw \ - libxml2 yaml-cpp tinyxml python-requests jemalloc" + libxml2 yaml-cpp tinyxml python-requests jemalloc gmp" OPENJPEG_USE=true VORBIS_USE=true diff --git a/build_files/build_environment/patches/cmakelists_gmpxx.txt b/build_files/build_environment/patches/cmakelists_gmpxx.txt new file mode 100644 index 00000000000..5aa6c404035 --- /dev/null +++ b/build_files/build_environment/patches/cmakelists_gmpxx.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.1) +project(libgmpxx) + +include_directories(. cxx ${GMP_INCLUDE_DIR}) +add_definitions(-D__GMP_WITHIN_GMPXX) +add_library(libgmpxx SHARED + cxx/dummy.cc + cxx/isfuns.cc + cxx/ismpf.cc + cxx/ismpq.cc + cxx/ismpz.cc + cxx/ismpznw.cc + cxx/limits.cc + cxx/osdoprnti.cc + cxx/osfuns.cc + cxx/osmpf.cc + cxx/osmpq.cc + cxx/osmpz.cc +) + +target_link_libraries(libgmpxx ${GMP_LIBRARY}) +install(TARGETS libgmpxx DESTINATION lib) diff --git a/build_files/build_environment/patches/config_gmpxx.h b/build_files/build_environment/patches/config_gmpxx.h new file mode 100644 index 00000000000..842d6d5d240 --- /dev/null +++ b/build_files/build_environment/patches/config_gmpxx.h @@ -0,0 +1,668 @@ +/* config.h. Generated from config.in by configure. */ +/* config.in. Generated from configure.ac by autoheader. */ + +/* + +Copyright 1996-2020 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * 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. + +or both in parallel, as here. + +The GNU MP Library 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 copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. +*/ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* The gmp-mparam.h file (a string) the tune program should suggest updating. + */ +#define GMP_MPARAM_H_SUGGEST "./mpn/x86_64/coreisbr/gmp-mparam.h" + +/* Define to 1 if you have the `alarm' function. */ +#define HAVE_ALARM 1 + +/* Define to 1 if alloca() works (via gmp-impl.h). */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix). + */ +/* #undef HAVE_ALLOCA_H */ + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((const)) */ +//#define HAVE_ATTRIBUTE_CONST 1 + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((malloc)) */ +//#define HAVE_ATTRIBUTE_MALLOC 1 + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((mode (XX))) + */ +//#define HAVE_ATTRIBUTE_MODE 1 + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((noreturn)) */ +//#define HAVE_ATTRIBUTE_NORETURN 1 + +/* Define to 1 if you have the `attr_get' function. */ +/* #undef HAVE_ATTR_GET */ + +/* Define to 1 if tests/libtests has calling conventions checking for the CPU + */ +/* #undef HAVE_CALLING_CONVENTIONS */ + +/* Define to 1 if you have the `clock' function. */ +#define HAVE_CLOCK 1 + +/* Define to 1 if you have the `clock_gettime' function */ +/* #undef HAVE_CLOCK_GETTIME */ + +/* Define to 1 if you have the `cputime' function. */ +/* #undef HAVE_CPUTIME */ + +/* Define to 1 if you have the declaration of `fgetc', and to 0 if you don't. + */ +#define HAVE_DECL_FGETC 1 + +/* Define to 1 if you have the declaration of `fscanf', and to 0 if you don't. + */ +#define HAVE_DECL_FSCANF 1 + +/* Define to 1 if you have the declaration of `optarg', and to 0 if you don't. + */ +#define HAVE_DECL_OPTARG 1 + +/* Define to 1 if you have the declaration of `sys_errlist', and to 0 if you + don't. */ +#define HAVE_DECL_SYS_ERRLIST 0 + +/* Define to 1 if you have the declaration of `sys_nerr', and to 0 if you + don't. */ +#define HAVE_DECL_SYS_NERR 0 + +/* Define to 1 if you have the declaration of `ungetc', and to 0 if you don't. + */ +#define HAVE_DECL_UNGETC 1 + +/* Define to 1 if you have the declaration of `vfprintf', and to 0 if you + don't. */ +#define HAVE_DECL_VFPRINTF 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define one of the following to 1 for the format of a `double'. + If your format is not among these choices, or you don't know what it is, + then leave all undefined. + IEEE_LITTLE_SWAPPED means little endian, but with the two 4-byte halves + swapped, as used by ARM CPUs in little endian mode. */ +/* #undef HAVE_DOUBLE_IEEE_BIG_ENDIAN */ +#define HAVE_DOUBLE_IEEE_LITTLE_ENDIAN 1 +/* #undef HAVE_DOUBLE_IEEE_LITTLE_SWAPPED */ +/* #undef HAVE_DOUBLE_VAX_D */ +/* #undef HAVE_DOUBLE_VAX_G */ +/* #undef HAVE_DOUBLE_CRAY_CFP */ + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the <float.h> header file. */ +#define HAVE_FLOAT_H 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `getrusage' function. */ +/* #undef HAVE_GETRUSAGE */ + +/* Define to 1 if you have the `getsysinfo' function. */ +/* #undef HAVE_GETSYSINFO */ + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((visibility)) + and __attribute__ ((alias)) */ +#define HAVE_HIDDEN_ALIAS 1 + +/* Define one of these to 1 for the host CPU family. + If your CPU is not in any of these families, leave all undefined. + For an AMD64 chip, define "x86" in ABI=32, but not in ABI=64. */ +/* #undef HAVE_HOST_CPU_FAMILY_alpha */ +/* #undef HAVE_HOST_CPU_FAMILY_m68k */ +/* #undef HAVE_HOST_CPU_FAMILY_power */ +/* #undef HAVE_HOST_CPU_FAMILY_powerpc */ +/* #undef HAVE_HOST_CPU_FAMILY_x86 */ +#define HAVE_HOST_CPU_FAMILY_x86_64 1 + +/* Define one of the following to 1 for the host CPU, as per the output of + ./config.guess. If your CPU is not listed here, leave all undefined. */ +/* #undef HAVE_HOST_CPU_alphaev67 */ +/* #undef HAVE_HOST_CPU_alphaev68 */ +/* #undef HAVE_HOST_CPU_alphaev7 */ +/* #undef HAVE_HOST_CPU_m68020 */ +/* #undef HAVE_HOST_CPU_m68030 */ +/* #undef HAVE_HOST_CPU_m68040 */ +/* #undef HAVE_HOST_CPU_m68060 */ +/* #undef HAVE_HOST_CPU_m68360 */ +/* #undef HAVE_HOST_CPU_powerpc604 */ +/* #undef HAVE_HOST_CPU_powerpc604e */ +/* #undef HAVE_HOST_CPU_powerpc750 */ +/* #undef HAVE_HOST_CPU_powerpc7400 */ +/* #undef HAVE_HOST_CPU_supersparc */ +/* #undef HAVE_HOST_CPU_i386 */ +/* #undef HAVE_HOST_CPU_i586 */ +/* #undef HAVE_HOST_CPU_i686 */ +/* #undef HAVE_HOST_CPU_pentium */ +/* #undef HAVE_HOST_CPU_pentiummmx */ +/* #undef HAVE_HOST_CPU_pentiumpro */ +/* #undef HAVE_HOST_CPU_pentium2 */ +/* #undef HAVE_HOST_CPU_pentium3 */ +/* #undef HAVE_HOST_CPU_pentium4 */ +/* #undef HAVE_HOST_CPU_core2 */ +/* #undef HAVE_HOST_CPU_nehalem */ +/* #undef HAVE_HOST_CPU_westmere */ +/* #undef HAVE_HOST_CPU_sandybridge */ +#define HAVE_HOST_CPU_ivybridge 1 +/* #undef HAVE_HOST_CPU_haswell */ +/* #undef HAVE_HOST_CPU_broadwell */ +/* #undef HAVE_HOST_CPU_skylake */ +/* #undef HAVE_HOST_CPU_silvermont */ +/* #undef HAVE_HOST_CPU_goldmont */ +/* #undef HAVE_HOST_CPU_k8 */ +/* #undef HAVE_HOST_CPU_k10 */ +/* #undef HAVE_HOST_CPU_bulldozer */ +/* #undef HAVE_HOST_CPU_piledriver */ +/* #undef HAVE_HOST_CPU_steamroller */ +/* #undef HAVE_HOST_CPU_excavator */ +/* #undef HAVE_HOST_CPU_zen */ +/* #undef HAVE_HOST_CPU_bobcat */ +/* #undef HAVE_HOST_CPU_jaguar */ +/* #undef HAVE_HOST_CPU_s390_z900 */ +/* #undef HAVE_HOST_CPU_s390_z990 */ +/* #undef HAVE_HOST_CPU_s390_z9 */ +/* #undef HAVE_HOST_CPU_s390_z10 */ +/* #undef HAVE_HOST_CPU_s390_z196 */ + +/* Define to 1 iff we have a s390 with 64-bit registers. */ +/* #undef HAVE_HOST_CPU_s390_zarch */ + +/* Define to 1 if the system has the type `intmax_t'. */ +#define HAVE_INTMAX_T 1 + +/* Define to 1 if the system has the type `intptr_t'. */ +#define HAVE_INTPTR_T 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <invent.h> header file. */ +/* #undef HAVE_INVENT_H */ + +/* Define to 1 if you have the <langinfo.h> header file. */ +/* #undef HAVE_LANGINFO_H */ + +/* Define one of these to 1 for the endianness of `mp_limb_t'. + If the endianness is not a simple big or little, or you don't know what + it is, then leave both undefined. */ +/* #undef HAVE_LIMB_BIG_ENDIAN */ +#define HAVE_LIMB_LITTLE_ENDIAN 1 + +/* Define to 1 if you have the `localeconv' function. */ +#define HAVE_LOCALECONV 1 + +/* Define to 1 if you have the <locale.h> header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if the system has the type `long double'. */ +#define HAVE_LONG_DOUBLE 1 + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the <machine/hal_sysinfo.h> header file. */ +/* #undef HAVE_MACHINE_HAL_SYSINFO_H */ + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mmap' function. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `mprotect' function. */ +#define HAVE_MPROTECT 1 + +/* Define to 1 each of the following for which a native (ie. CPU specific) + implementation of the corresponding routine exists. */ +#define HAVE_NATIVE_mpn_add_n 1 +/* #undef HAVE_NATIVE_mpn_add_n_sub_n */ +#define HAVE_NATIVE_mpn_add_nc 1 +/* #undef HAVE_NATIVE_mpn_addaddmul_1msb0 */ +#define HAVE_NATIVE_mpn_addlsh1_n 1 +#define HAVE_NATIVE_mpn_addlsh2_n 1 +#define HAVE_NATIVE_mpn_addlsh_n 1 +#define HAVE_NATIVE_mpn_addlsh1_nc 1 +#define HAVE_NATIVE_mpn_addlsh2_nc 1 +#define HAVE_NATIVE_mpn_addlsh_nc 1 +/* #undef HAVE_NATIVE_mpn_addlsh1_n_ip1 */ +/* #undef HAVE_NATIVE_mpn_addlsh2_n_ip1 */ +/* #undef HAVE_NATIVE_mpn_addlsh_n_ip1 */ +/* #undef HAVE_NATIVE_mpn_addlsh1_nc_ip1 */ +/* #undef HAVE_NATIVE_mpn_addlsh2_nc_ip1 */ +/* #undef HAVE_NATIVE_mpn_addlsh_nc_ip1 */ +/* #undef HAVE_NATIVE_mpn_addlsh1_n_ip2 */ +/* #undef HAVE_NATIVE_mpn_addlsh2_n_ip2 */ +/* #undef HAVE_NATIVE_mpn_addlsh_n_ip2 */ +/* #undef HAVE_NATIVE_mpn_addlsh1_nc_ip2 */ +/* #undef HAVE_NATIVE_mpn_addlsh2_nc_ip2 */ +/* #undef HAVE_NATIVE_mpn_addlsh_nc_ip2 */ +/* #undef HAVE_NATIVE_mpn_addmul_1c */ +#define HAVE_NATIVE_mpn_addmul_2 1 +/* #undef HAVE_NATIVE_mpn_addmul_3 */ +/* #undef HAVE_NATIVE_mpn_addmul_4 */ +/* #undef HAVE_NATIVE_mpn_addmul_5 */ +/* #undef HAVE_NATIVE_mpn_addmul_6 */ +/* #undef HAVE_NATIVE_mpn_addmul_7 */ +/* #undef HAVE_NATIVE_mpn_addmul_8 */ +/* #undef HAVE_NATIVE_mpn_addmul_2s */ +#define HAVE_NATIVE_mpn_and_n 1 +#define HAVE_NATIVE_mpn_andn_n 1 +#define HAVE_NATIVE_mpn_bdiv_dbm1c 1 +#define HAVE_NATIVE_mpn_bdiv_q_1 1 +#define HAVE_NATIVE_mpn_pi1_bdiv_q_1 1 +#define HAVE_NATIVE_mpn_cnd_add_n 1 +#define HAVE_NATIVE_mpn_cnd_sub_n 1 +#define HAVE_NATIVE_mpn_com 1 +#define HAVE_NATIVE_mpn_copyd 1 +#define HAVE_NATIVE_mpn_copyi 1 +/* #undef HAVE_NATIVE_mpn_div_qr_1n_pi1 */ +/* #undef HAVE_NATIVE_mpn_div_qr_2 */ +#define HAVE_NATIVE_mpn_divexact_1 1 +/* #undef HAVE_NATIVE_mpn_divexact_by3c */ +#define HAVE_NATIVE_mpn_divrem_1 1 +/* #undef HAVE_NATIVE_mpn_divrem_1c */ +#define HAVE_NATIVE_mpn_divrem_2 1 +/* #undef HAVE_NATIVE_mpn_gcd_1 */ +#define HAVE_NATIVE_mpn_gcd_11 1 +/* #undef HAVE_NATIVE_mpn_gcd_22 */ +#define HAVE_NATIVE_mpn_hamdist 1 +#define HAVE_NATIVE_mpn_invert_limb 1 +#define HAVE_NATIVE_mpn_ior_n 1 +#define HAVE_NATIVE_mpn_iorn_n 1 +#define HAVE_NATIVE_mpn_lshift 1 +#define HAVE_NATIVE_mpn_lshiftc 1 +/* #undef HAVE_NATIVE_mpn_lshsub_n */ +/* #undef HAVE_NATIVE_mpn_mod_1 */ +#define HAVE_NATIVE_mpn_mod_1_1p 1 +/* #undef HAVE_NATIVE_mpn_mod_1c */ +#define HAVE_NATIVE_mpn_mod_1s_2p 1 +#define HAVE_NATIVE_mpn_mod_1s_4p 1 +#define HAVE_NATIVE_mpn_mod_34lsub1 1 +#define HAVE_NATIVE_mpn_modexact_1_odd 1 +#define HAVE_NATIVE_mpn_modexact_1c_odd 1 +#define HAVE_NATIVE_mpn_mul_1 1 +#define HAVE_NATIVE_mpn_mul_1c 1 +#define HAVE_NATIVE_mpn_mul_2 1 +/* #undef HAVE_NATIVE_mpn_mul_3 */ +/* #undef HAVE_NATIVE_mpn_mul_4 */ +/* #undef HAVE_NATIVE_mpn_mul_5 */ +/* #undef HAVE_NATIVE_mpn_mul_6 */ +#define HAVE_NATIVE_mpn_mul_basecase 1 +#define HAVE_NATIVE_mpn_mullo_basecase 1 +#define HAVE_NATIVE_mpn_nand_n 1 +#define HAVE_NATIVE_mpn_nior_n 1 +#define HAVE_NATIVE_mpn_popcount 1 +#define HAVE_NATIVE_mpn_preinv_divrem_1 1 +/* #undef HAVE_NATIVE_mpn_preinv_mod_1 */ +#define HAVE_NATIVE_mpn_redc_1 1 +/* #undef HAVE_NATIVE_mpn_redc_2 */ +#define HAVE_NATIVE_mpn_rsblsh1_n 1 +#define HAVE_NATIVE_mpn_rsblsh2_n 1 +#define HAVE_NATIVE_mpn_rsblsh_n 1 +#define HAVE_NATIVE_mpn_rsblsh1_nc 1 +/* #undef HAVE_NATIVE_mpn_rsblsh2_nc */ +/* #undef HAVE_NATIVE_mpn_rsblsh_nc */ +#define HAVE_NATIVE_mpn_rsh1add_n 1 +#define HAVE_NATIVE_mpn_rsh1add_nc 1 +#define HAVE_NATIVE_mpn_rsh1sub_n 1 +#define HAVE_NATIVE_mpn_rsh1sub_nc 1 +#define HAVE_NATIVE_mpn_rshift 1 +/* #undef HAVE_NATIVE_mpn_sbpi1_bdiv_r */ +#define HAVE_NATIVE_mpn_sqr_basecase 1 +/* #undef HAVE_NATIVE_mpn_sqr_diagonal */ +#define HAVE_NATIVE_mpn_sqr_diag_addlsh1 1 +#define HAVE_NATIVE_mpn_sub_n 1 +#define HAVE_NATIVE_mpn_sub_nc 1 +#define HAVE_NATIVE_mpn_sublsh1_n 1 +#define HAVE_NATIVE_mpn_sublsh2_n 1 +/* #undef HAVE_NATIVE_mpn_sublsh_n */ +/* #undef HAVE_NATIVE_mpn_sublsh1_nc */ +/* #undef HAVE_NATIVE_mpn_sublsh2_nc */ +/* #undef HAVE_NATIVE_mpn_sublsh_nc */ +/* #undef HAVE_NATIVE_mpn_sublsh1_n_ip1 */ +/* #undef HAVE_NATIVE_mpn_sublsh2_n_ip1 */ +/* #undef HAVE_NATIVE_mpn_sublsh_n_ip1 */ +/* #undef HAVE_NATIVE_mpn_sublsh1_nc_ip1 */ +/* #undef HAVE_NATIVE_mpn_sublsh2_nc_ip1 */ +/* #undef HAVE_NATIVE_mpn_sublsh_nc_ip1 */ +/* #undef HAVE_NATIVE_mpn_submul_1c */ +/* #undef HAVE_NATIVE_mpn_tabselect */ +/* #undef HAVE_NATIVE_mpn_udiv_qrnnd */ +/* #undef HAVE_NATIVE_mpn_udiv_qrnnd_r */ +/* #undef HAVE_NATIVE_mpn_umul_ppmm */ +/* #undef HAVE_NATIVE_mpn_umul_ppmm_r */ +#define HAVE_NATIVE_mpn_xor_n 1 +#define HAVE_NATIVE_mpn_xnor_n 1 + +/* Define to 1 if you have the `nl_langinfo' function. */ +/* #undef HAVE_NL_LANGINFO */ + +/* Define to 1 if you have the <nl_types.h> header file. */ +/* #undef HAVE_NL_TYPES_H */ + +/* Define to 1 if you have the `obstack_vprintf' function. */ +/* #undef HAVE_OBSTACK_VPRINTF */ + +/* Define to 1 if you have the `popen' function. */ +#define HAVE_POPEN 1 + +/* Define to 1 if you have the `processor_info' function. */ +/* #undef HAVE_PROCESSOR_INFO */ + +/* Define to 1 if <sys/pstat.h> `struct pst_processor' exists and contains + `psp_iticksperclktick'. */ +/* #undef HAVE_PSP_ITICKSPERCLKTICK */ + +/* Define to 1 if you have the `pstat_getprocessor' function. */ +/* #undef HAVE_PSTAT_GETPROCESSOR */ + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#define HAVE_PTRDIFF_T 1 + +/* Define to 1 if the system has the type `quad_t'. */ +/* #undef HAVE_QUAD_T */ + +/* Define to 1 if you have the `raise' function. */ +#define HAVE_RAISE 1 + +/* Define to 1 if you have the `read_real_time' function. */ +/* #undef HAVE_READ_REAL_TIME */ + +/* Define to 1 if you have the `sigaction' function. */ +/* #undef HAVE_SIGACTION */ + +/* Define to 1 if you have the `sigaltstack' function. */ +/* #undef HAVE_SIGALTSTACK */ + +/* Define to 1 if you have the `sigstack' function. */ +/* #undef HAVE_SIGSTACK */ + +/* Tune directory speed_cyclecounter, undef=none, 1=32bits, 2=64bits) */ +#define HAVE_SPEED_CYCLECOUNTER 2 + +/* Define to 1 if you have the <sstream> header file. */ +#define HAVE_SSTREAM 1 + +/* Define to 1 if the system has the type `stack_t'. */ +/* #undef HAVE_STACK_T */ + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if the system has the type `std::locale'. */ +#define HAVE_STD__LOCALE 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strnlen' function. */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the `sysconf' function. */ +/* #undef HAVE_SYSCONF */ + +/* Define to 1 if you have the `sysctl' function. */ +/* #undef HAVE_SYSCTL */ + +/* Define to 1 if you have the `sysctlbyname' function. */ +/* #undef HAVE_SYSCTLBYNAME */ + +/* Define to 1 if you have the `syssgi' function. */ +/* #undef HAVE_SYSSGI */ + +/* Define to 1 if you have the <sys/attributes.h> header file. */ +/* #undef HAVE_SYS_ATTRIBUTES_H */ + +/* Define to 1 if you have the <sys/iograph.h> header file. */ +/* #undef HAVE_SYS_IOGRAPH_H */ + +/* Define to 1 if you have the <sys/mman.h> header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/processor.h> header file. */ +/* #undef HAVE_SYS_PROCESSOR_H */ + +/* Define to 1 if you have the <sys/pstat.h> header file. */ +/* #undef HAVE_SYS_PSTAT_H */ + +/* Define to 1 if you have the <sys/resource.h> header file. */ +/* #undef HAVE_SYS_RESOURCE_H */ + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/sysctl.h> header file. */ +/* #undef HAVE_SYS_SYSCTL_H */ + +/* Define to 1 if you have the <sys/sysinfo.h> header file. */ +/* #undef HAVE_SYS_SYSINFO_H */ + +/* Define to 1 if you have the <sys/syssgi.h> header file. */ +/* #undef HAVE_SYS_SYSSGI_H */ + +/* Define to 1 if you have the <sys/systemcfg.h> header file. */ +/* #undef HAVE_SYS_SYSTEMCFG_H */ + +/* Define to 1 if you have the <sys/times.h> header file. */ +/* #undef HAVE_SYS_TIMES_H */ + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the `times' function. */ +/* #undef HAVE_TIMES */ + +/* Define to 1 if the system has the type `uint_least32_t'. */ +#define HAVE_UINT_LEAST32_T 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vsnprintf' function and it works properly. */ +/* #undef HAVE_VSNPRINTF */ + +/* Define to 1 for Windos/64 */ +#define HOST_DOS64 1 + +/* Assembler local label prefix */ +#define LSYM_PREFIX "L" + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 to disable the use of inline assembly */ +/* #undef NO_ASM */ + +/* Name of package */ +#define PACKAGE "gmp" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "gmp-bugs@gmplib.org, see https://gmplib.org/manual/Reporting-Bugs.html" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "GNU MP" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "GNU MP 6.2.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "gmp" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "http://www.gnu.org/software/gmp/" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "6.2.0" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* The size of `mp_limb_t', as computed by sizeof. */ +#define SIZEOF_MP_LIMB_T 8 + +/* The size of `unsigned', as computed by sizeof. */ +#define SIZEOF_UNSIGNED 4 + +/* The size of `unsigned long', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* The size of `unsigned short', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_SHORT 2 + +/* The size of `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P 8 + +/* Define to 1 if sscanf requires writable inputs */ +/* #undef SSCANF_WRITABLE_INPUT */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#define TIME_WITH_SYS_TIME 1 + +/* Maximum size the tune program can test for SQR_TOOM2_THRESHOLD */ +/* #undef TUNE_SQR_TOOM2_MAX */ + +/* Version number of package */ +#define VERSION "6.2.0" + +/* Define to 1 to enable ASSERT checking, per --enable-assert */ +/* #undef WANT_ASSERT */ + +/* Define to 1 to enable GMP_CPU_TYPE faking cpuid, per --enable-fake-cpuid */ +/* #undef WANT_FAKE_CPUID */ + +/* Define to 1 when building a fat binary. */ +/* #undef WANT_FAT_BINARY */ + +/* Define to 1 to enable FFTs for multiplication, per --enable-fft */ +#define WANT_FFT 1 + +/* Define to 1 to enable old mpn_mul_fft_full for multiplication, per + --enable-old-fft-full */ +/* #undef WANT_OLD_FFT_FULL */ + +/* Define to 1 if --enable-profiling=gprof */ +/* #undef WANT_PROFILING_GPROF */ + +/* Define to 1 if --enable-profiling=instrument */ +/* #undef WANT_PROFILING_INSTRUMENT */ + +/* Define to 1 if --enable-profiling=prof */ +/* #undef WANT_PROFILING_PROF */ + +/* Define one of these to 1 for the desired temporary memory allocation + method, per --enable-alloca. */ +#define WANT_TMP_ALLOCA 1 +/* #undef WANT_TMP_REENTRANT */ +/* #undef WANT_TMP_NOTREENTRANT */ +/* #undef WANT_TMP_DEBUG */ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if the assembler understands the mulx instruction */ +/* #undef X86_ASM_MULX */ + +/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a + `char[]'. */ +/* #undef YYTEXT_POINTER */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to the equivalent of the C99 'restrict' keyword, or to + nothing if this is not supported. Do not define if restrict is + supported directly. */ +#define restrict __restrict +/* Work around a bug in Sun C++: it does not support _Restrict or + __restrict__, even though the corresponding Sun C compiler ends up with + "#define restrict _Restrict" or "#define restrict __restrict__" in the + previous line. Perhaps some future version of Sun C++ will work with + restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ +#if defined __SUNPRO_CC && !defined __RESTRICT +# define _Restrict +# define __restrict__ +#endif + +/* Define to empty if the keyword `volatile' does not work. Warning: valid + code using `volatile' can become incorrect without. Disable with care. */ +/* #undef volatile */ diff --git a/build_files/cmake/Modules/FindGMP.cmake b/build_files/cmake/Modules/FindGMP.cmake new file mode 100644 index 00000000000..4469f32c785 --- /dev/null +++ b/build_files/cmake/Modules/FindGMP.cmake @@ -0,0 +1,96 @@ +# - Find GMP library +# Find the native GMP includes and library +# This module defines +# GMP_INCLUDE_DIRS, where to find gmp.h, Set when +# GMP_INCLUDE_DIR is found. +# GMP_LIBRARIES, libraries to link against to use GMP. +# GMP_ROOT_DIR, The base directory to search for GMP. +# This can also be an environment variable. +# GMP_FOUND, If false, do not try to use GMP. +# +# also defined, but not for general use are +# GMP_LIBRARY, where to find the GMP library. + +#============================================================================= +# Copyright 2011 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 GMP_ROOT_DIR was defined in the environment, use it. +IF(NOT GMP_ROOT_DIR AND NOT $ENV{GMP_ROOT_DIR} STREQUAL "") + SET(GMP_ROOT_DIR $ENV{GMP_ROOT_DIR}) +ENDIF() + +SET(_gmp_SEARCH_DIRS + ${GMP_ROOT_DIR} + /opt/lib/gmp +) + +FIND_PATH(GMP_INCLUDE_DIR + NAMES + gmp.h + HINTS + ${_gmp_SEARCH_DIRS} + PATH_SUFFIXES + include/gmp +) + +FIND_PATH(GMPXX_INCLUDE_DIR + NAMES + gmpxx.h + HINTS + ${_gmp_SEARCH_DIRS} + PATH_SUFFIXES + include/gmp +) + +FIND_LIBRARY(GMP_LIBRARY + NAMES + gmp + HINTS + ${_gmp_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + +FIND_LIBRARY(GMPXX_LIBRARY + NAMES + gmpxx + HINTS + ${_gmp_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + +if(GMP_INCLUDE_DIR) + SET(_version_regex "^#define[ \t]+__GNU_MP_VERSION[ \t]+\"([^\"]+)\".*") + file(STRINGS "${GMP_INCLUDE_DIR}/gmp.h" + GMP_VERSION REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" + GMP_VERSION "${GMP_VERSION}") + unset(_version_regex) +endif() + +# handle the QUIETLY and REQUIRED arguments and set GMP_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMP DEFAULT_MSG + GMP_LIBRARY GMPXX_LIBRARY GMP_INCLUDE_DIR GMPXX_INCLUDE_DIR) + +IF(GMP_FOUND) + SET(GMP_LIBRARIES ${GMP_LIBRARY} ${GMPXX_LIBRARY}) + SET(GMP_INCLUDE_DIRS ${GMP_INCLUDE_DIR} ${GMPXX_INCLUDE_DIR}) +ENDIF(GMP_FOUND) + +MARK_AS_ADVANCED( + GMP_INCLUDE_DIR + GMP_LIBRARY + GMPXX_INCLUDE_DIR + GMPXX_LIBRARY +) diff --git a/build_files/cmake/Modules/GTest.cmake b/build_files/cmake/Modules/GTest.cmake new file mode 100644 index 00000000000..9a82fc49628 --- /dev/null +++ b/build_files/cmake/Modules/GTest.cmake @@ -0,0 +1,550 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +GoogleTest +---------- + +.. versionadded:: 3.9 + +This module defines functions to help use the Google Test infrastructure. Two +mechanisms for adding tests are provided. :command:`gtest_add_tests` has been +around for some time, originally via ``find_package(GTest)``. +:command:`gtest_discover_tests` was introduced in CMake 3.10. + +The (older) :command:`gtest_add_tests` scans source files to identify tests. +This is usually effective, with some caveats, including in cross-compiling +environments, and makes setting additional properties on tests more convenient. +However, its handling of parameterized tests is less comprehensive, and it +requires re-running CMake to detect changes to the list of tests. + +The (newer) :command:`gtest_discover_tests` discovers tests by asking the +compiled test executable to enumerate its tests. This is more robust and +provides better handling of parameterized tests, and does not require CMake +to be re-run when tests change. However, it may not work in a cross-compiling +environment, and setting test properties is less convenient. + +More details can be found in the documentation of the respective functions. + +Both commands are intended to replace use of :command:`add_test` to register +tests, and will create a separate CTest test for each Google Test test case. +Note that this is in some cases less efficient, as common set-up and tear-down +logic cannot be shared by multiple test cases executing in the same instance. +However, it provides more fine-grained pass/fail information to CTest, which is +usually considered as more beneficial. By default, the CTest test name is the +same as the Google Test name (i.e. ``suite.testcase``); see also +``TEST_PREFIX`` and ``TEST_SUFFIX``. + +.. command:: gtest_add_tests + + Automatically add tests with CTest by scanning source code for Google Test + macros:: + + gtest_add_tests(TARGET target + [SOURCES src1...] + [EXTRA_ARGS arg1...] + [WORKING_DIRECTORY dir] + [TEST_PREFIX prefix] + [TEST_SUFFIX suffix] + [SKIP_DEPENDENCY] + [TEST_LIST outVar] + ) + + ``gtest_add_tests`` attempts to identify tests by scanning source files. + Although this is generally effective, it uses only a basic regular expression + match, which can be defeated by atypical test declarations, and is unable to + fully "split" parameterized tests. Additionally, it requires that CMake be + re-run to discover any newly added, removed or renamed tests (by default, + this means that CMake is re-run when any test source file is changed, but see + ``SKIP_DEPENDENCY``). However, it has the advantage of declaring tests at + CMake time, which somewhat simplifies setting additional properties on tests, + and always works in a cross-compiling environment. + + The options are: + + ``TARGET target`` + Specifies the Google Test executable, which must be a known CMake + executable target. CMake will substitute the location of the built + executable when running the test. + + ``SOURCES src1...`` + When provided, only the listed files will be scanned for test cases. If + this option is not given, the :prop_tgt:`SOURCES` property of the + specified ``target`` will be used to obtain the list of sources. + + ``EXTRA_ARGS arg1...`` + Any extra arguments to pass on the command line to each test case. + + ``WORKING_DIRECTORY dir`` + Specifies the directory in which to run the discovered test cases. If this + option is not provided, the current binary directory is used. + + ``TEST_PREFIX prefix`` + Specifies a ``prefix`` to be prepended to the name of each discovered test + case. This can be useful when the same source files are being used in + multiple calls to ``gtest_add_test()`` but with different ``EXTRA_ARGS``. + + ``TEST_SUFFIX suffix`` + Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of + every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may + be specified. + + ``SKIP_DEPENDENCY`` + Normally, the function creates a dependency which will cause CMake to be + re-run if any of the sources being scanned are changed. This is to ensure + that the list of discovered tests is updated. If this behavior is not + desired (as may be the case while actually writing the test cases), this + option can be used to prevent the dependency from being added. + + ``TEST_LIST outVar`` + The variable named by ``outVar`` will be populated in the calling scope + with the list of discovered test cases. This allows the caller to do + things like manipulate test properties of the discovered tests. + + .. code-block:: cmake + + include(GoogleTest) + add_executable(FooTest FooUnitTest.cxx) + gtest_add_tests(TARGET FooTest + TEST_SUFFIX .noArgs + TEST_LIST noArgsTests + ) + gtest_add_tests(TARGET FooTest + EXTRA_ARGS --someArg someValue + TEST_SUFFIX .withArgs + TEST_LIST withArgsTests + ) + set_tests_properties(${noArgsTests} PROPERTIES TIMEOUT 10) + set_tests_properties(${withArgsTests} PROPERTIES TIMEOUT 20) + + For backward compatibility, the following form is also supported:: + + gtest_add_tests(exe args files...) + + ``exe`` + The path to the test executable or the name of a CMake target. + ``args`` + A ;-list of extra arguments to be passed to executable. The entire + list must be passed as a single argument. Enclose it in quotes, + or pass ``""`` for no arguments. + ``files...`` + A list of source files to search for tests and test fixtures. + Alternatively, use ``AUTO`` to specify that ``exe`` is the name + of a CMake executable target whose sources should be scanned. + + .. code-block:: cmake + + include(GoogleTest) + set(FooTestArgs --foo 1 --bar 2) + add_executable(FooTest FooUnitTest.cxx) + gtest_add_tests(FooTest "${FooTestArgs}" AUTO) + +.. command:: gtest_discover_tests + + Automatically add tests with CTest by querying the compiled test executable + for available tests:: + + gtest_discover_tests(target + [EXTRA_ARGS arg1...] + [WORKING_DIRECTORY dir] + [TEST_PREFIX prefix] + [TEST_SUFFIX suffix] + [NO_PRETTY_TYPES] [NO_PRETTY_VALUES] + [PROPERTIES name1 value1...] + [TEST_LIST var] + [DISCOVERY_TIMEOUT seconds] + [XML_OUTPUT_DIR dir] + [DISCOVERY_MODE <POST_BUILD|PRE_TEST>] + ) + + ``gtest_discover_tests()`` sets up a post-build command on the test executable + that generates the list of tests by parsing the output from running the test + with the ``--gtest_list_tests`` argument. Compared to the source parsing + approach of :command:`gtest_add_tests`, this ensures that the full list of + tests, including instantiations of parameterized tests, is obtained. Since + test discovery occurs at build time, it is not necessary to re-run CMake when + the list of tests changes. + However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set + in order to function in a cross-compiling environment. + + Additionally, setting properties on tests is somewhat less convenient, since + the tests are not available at CMake time. Additional test properties may be + assigned to the set of tests as a whole using the ``PROPERTIES`` option. If + more fine-grained test control is needed, custom content may be provided + through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES` + directory property. The set of discovered tests is made accessible to such a + script via the ``<target>_TESTS`` variable. + + The options are: + + ``target`` + Specifies the Google Test executable, which must be a known CMake + executable target. CMake will substitute the location of the built + executable when running the test. + + ``EXTRA_ARGS arg1...`` + Any extra arguments to pass on the command line to each test case. + + ``WORKING_DIRECTORY dir`` + Specifies the directory in which to run the discovered test cases. If this + option is not provided, the current binary directory is used. + + ``TEST_PREFIX prefix`` + Specifies a ``prefix`` to be prepended to the name of each discovered test + case. This can be useful when the same test executable is being used in + multiple calls to ``gtest_discover_tests()`` but with different + ``EXTRA_ARGS``. + + ``TEST_SUFFIX suffix`` + Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of + every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may + be specified. + + ``NO_PRETTY_TYPES`` + By default, the type index of type-parameterized tests is replaced by the + actual type name in the CTest test name. If this behavior is undesirable + (e.g. because the type names are unwieldy), this option will suppress this + behavior. + + ``NO_PRETTY_VALUES`` + By default, the value index of value-parameterized tests is replaced by the + actual value in the CTest test name. If this behavior is undesirable + (e.g. because the value strings are unwieldy), this option will suppress + this behavior. + + ``PROPERTIES name1 value1...`` + Specifies additional properties to be set on all tests discovered by this + invocation of ``gtest_discover_tests()``. + + ``TEST_LIST var`` + Make the list of tests available in the variable ``var``, rather than the + default ``<target>_TESTS``. This can be useful when the same test + executable is being used in multiple calls to ``gtest_discover_tests()``. + Note that this variable is only available in CTest. + + ``DISCOVERY_TIMEOUT num`` + Specifies how long (in seconds) CMake will wait for the test to enumerate + available tests. If the test takes longer than this, discovery (and your + build) will fail. Most test executables will enumerate their tests very + quickly, but under some exceptional circumstances, a test may require a + longer timeout. The default is 5. See also the ``TIMEOUT`` option of + :command:`execute_process`. + + .. note:: + + In CMake versions 3.10.1 and 3.10.2, this option was called ``TIMEOUT``. + This clashed with the ``TIMEOUT`` test property, which is one of the + common properties that would be set with the ``PROPERTIES`` keyword, + usually leading to legal but unintended behavior. The keyword was + changed to ``DISCOVERY_TIMEOUT`` in CMake 3.10.3 to address this + problem. The ambiguous behavior of the ``TIMEOUT`` keyword in 3.10.1 + and 3.10.2 has not been preserved. + + ``XML_OUTPUT_DIR dir`` + If specified, the parameter is passed along with ``--gtest_output=xml:`` + to test executable. The actual file name is the same as the test target, + including prefix and suffix. This should be used instead of + ``EXTRA_ARGS --gtest_output=xml`` to avoid race conditions writing the + XML result output when using parallel test execution. + + ``DISCOVERY_MODE`` + Provides greater control over when ``gtest_discover_tests()`` performs test + discovery. By default, ``POST_BUILD`` sets up a post-build command + to perform test discovery at build time. In certain scenarios, like + cross-compiling, this ``POST_BUILD`` behavior is not desirable. + By contrast, ``PRE_TEST`` delays test discovery until just prior to test + execution. This way test discovery occurs in the target environment + where the test has a better chance at finding appropriate runtime + dependencies. + + ``DISCOVERY_MODE`` defaults to the value of the + ``CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not + passed when calling ``gtest_discover_tests()``. This provides a mechanism + for globally selecting a preferred test discovery behavior without having + to modify each call site. + +#]=======================================================================] + +# Save project's policies +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) # if IN_LIST + +#------------------------------------------------------------------------------ +function(gtest_add_tests) + + if (ARGC LESS 1) + message(FATAL_ERROR "No arguments supplied to gtest_add_tests()") + endif() + + set(options + SKIP_DEPENDENCY + ) + set(oneValueArgs + TARGET + WORKING_DIRECTORY + TEST_PREFIX + TEST_SUFFIX + TEST_LIST + ) + set(multiValueArgs + SOURCES + EXTRA_ARGS + ) + set(allKeywords ${options} ${oneValueArgs} ${multiValueArgs}) + + unset(sources) + if("${ARGV0}" IN_LIST allKeywords) + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(autoAddSources YES) + else() + # Non-keyword syntax, convert to keyword form + if (ARGC LESS 3) + message(FATAL_ERROR "gtest_add_tests() without keyword options requires at least 3 arguments") + endif() + set(ARGS_TARGET "${ARGV0}") + set(ARGS_EXTRA_ARGS "${ARGV1}") + if(NOT "${ARGV2}" STREQUAL "AUTO") + set(ARGS_SOURCES "${ARGV}") + list(REMOVE_AT ARGS_SOURCES 0 1) + endif() + endif() + + # The non-keyword syntax allows the first argument to be an arbitrary + # executable rather than a target if source files are also provided. In all + # other cases, both forms require a target. + if(NOT TARGET "${ARGS_TARGET}" AND NOT ARGS_SOURCES) + message(FATAL_ERROR "${ARGS_TARGET} does not define an existing CMake target") + endif() + if(NOT ARGS_WORKING_DIRECTORY) + unset(workDir) + else() + set(workDir WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}") + endif() + + if(NOT ARGS_SOURCES) + get_property(ARGS_SOURCES TARGET ${ARGS_TARGET} PROPERTY SOURCES) + endif() + + unset(testList) + + set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*") + set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)") + + foreach(source IN LISTS ARGS_SOURCES) + if(NOT ARGS_SKIP_DEPENDENCY) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${source}) + endif() + file(READ "${source}" contents) + string(REGEX MATCHALL "${gtest_test_type_regex} *\\(([A-Za-z_0-9 ,]+)\\)" found_tests "${contents}") + foreach(hit ${found_tests}) + string(REGEX MATCH "${gtest_test_type_regex}" test_type ${hit}) + + # Parameterized tests have a different signature for the filter + if("x${test_type}" STREQUAL "xTEST_P") + string(REGEX REPLACE ${gtest_case_name_regex} "*/\\1.\\2/*" gtest_test_name ${hit}) + elseif("x${test_type}" STREQUAL "xTEST_F" OR "x${test_type}" STREQUAL "xTEST") + string(REGEX REPLACE ${gtest_case_name_regex} "\\1.\\2" gtest_test_name ${hit}) + elseif("x${test_type}" STREQUAL "xTYPED_TEST") + string(REGEX REPLACE ${gtest_case_name_regex} "\\1/*.\\2" gtest_test_name ${hit}) + else() + message(WARNING "Could not parse GTest ${hit} for adding to CTest.") + continue() + endif() + + # Make sure tests disabled in GTest get disabled in CTest + if(gtest_test_name MATCHES "(^|\\.)DISABLED_") + # Add the disabled test if CMake is new enough + # Note that this check is to allow backwards compatibility so this + # module can be copied locally in projects to use with older CMake + # versions + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.8.20170401) + string(REGEX REPLACE + "(^|\\.)DISABLED_" "\\1" + orig_test_name "${gtest_test_name}" + ) + set(ctest_test_name + ${ARGS_TEST_PREFIX}${orig_test_name}${ARGS_TEST_SUFFIX} + ) + add_test(NAME ${ctest_test_name} + ${workDir} + COMMAND ${ARGS_TARGET} + --gtest_also_run_disabled_tests + --gtest_filter=${gtest_test_name} + ${ARGS_EXTRA_ARGS} + ) + set_tests_properties(${ctest_test_name} PROPERTIES DISABLED TRUE) + list(APPEND testList ${ctest_test_name}) + endif() + else() + set(ctest_test_name ${ARGS_TEST_PREFIX}${gtest_test_name}${ARGS_TEST_SUFFIX}) + add_test(NAME ${ctest_test_name} + ${workDir} + COMMAND ${ARGS_TARGET} + --gtest_filter=${gtest_test_name} + ${ARGS_EXTRA_ARGS} + ) + list(APPEND testList ${ctest_test_name}) + endif() + endforeach() + endforeach() + + if(ARGS_TEST_LIST) + set(${ARGS_TEST_LIST} ${testList} PARENT_SCOPE) + endif() + +endfunction() + +#------------------------------------------------------------------------------ + +function(gtest_discover_tests TARGET) + cmake_parse_arguments( + "" + "NO_PRETTY_TYPES;NO_PRETTY_VALUES" + "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE" + "EXTRA_ARGS;PROPERTIES" + ${ARGN} + ) + + if(NOT _WORKING_DIRECTORY) + set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endif() + if(NOT _TEST_LIST) + set(_TEST_LIST ${TARGET}_TESTS) + endif() + if(NOT _DISCOVERY_TIMEOUT) + set(_DISCOVERY_TIMEOUT 5) + endif() + if(NOT _DISCOVERY_MODE) + if(NOT CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE) + set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD") + endif() + set(_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE}) + endif() + + get_property( + has_counter + TARGET ${TARGET} + PROPERTY CTEST_DISCOVERED_TEST_COUNTER + SET + ) + if(has_counter) + get_property( + counter + TARGET ${TARGET} + PROPERTY CTEST_DISCOVERED_TEST_COUNTER + ) + math(EXPR counter "${counter} + 1") + else() + set(counter 1) + endif() + set_property( + TARGET ${TARGET} + PROPERTY CTEST_DISCOVERED_TEST_COUNTER + ${counter} + ) + + # Define rule to generate test list for aforementioned test executable + # Blender: use _ instead of [] to avoid problems with zsh regex. + set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_${counter}_") + set(ctest_include_file "${ctest_file_base}_include.cmake") + set(ctest_tests_file "${ctest_file_base}_tests.cmake") + get_property(crosscompiling_emulator + TARGET ${TARGET} + PROPERTY CROSSCOMPILING_EMULATOR + ) + + if(_DISCOVERY_MODE STREQUAL "POST_BUILD") + add_custom_command( + TARGET ${TARGET} POST_BUILD + BYPRODUCTS "${ctest_tests_file}" + COMMAND "${CMAKE_COMMAND}" + -D "TEST_TARGET=${TARGET}" + -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>" + -D "TEST_EXECUTOR=${crosscompiling_emulator}" + -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" + -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${_PROPERTIES}" + -D "TEST_PREFIX=${_TEST_PREFIX}" + -D "TEST_SUFFIX=${_TEST_SUFFIX}" + -D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}" + -D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}" + -D "TEST_LIST=${_TEST_LIST}" + -D "CTEST_FILE=${ctest_tests_file}" + -D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}" + -D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}" + -P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}" + VERBATIM + ) + + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${ctest_tests_file}\")\n" + " include(\"${ctest_tests_file}\")\n" + "else()\n" + " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n" + "endif()\n" + ) + elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST") + + get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL + PROPERTY GENERATOR_IS_MULTI_CONFIG + ) + + if(GENERATOR_IS_MULTI_CONFIG) + set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake") + endif() + + string(CONCAT ctest_include_content + "if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n" + " if(\"$<TARGET_FILE:${TARGET}>\" IS_NEWER_THAN \"${ctest_tests_file}\")" "\n" + " include(\"${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}\")" "\n" + " gtest_discover_tests_impl(" "\n" + " TEST_EXECUTABLE" " [==[" "$<TARGET_FILE:${TARGET}>" "]==]" "\n" + " TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n" + " TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n" + " TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n" + " TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n" + " TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n" + " TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n" + " NO_PRETTY_TYPES" " [==[" "${_NO_PRETTY_TYPES}" "]==]" "\n" + " NO_PRETTY_VALUES" " [==[" "${_NO_PRETTY_VALUES}" "]==]" "\n" + " TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n" + " CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n" + " TEST_DISCOVERY_TIMEOUT" " [==[" "${_DISCOVERY_TIMEOUT}" "]==]" "\n" + " TEST_XML_OUTPUT_DIR" " [==[" "${_XML_OUTPUT_DIR}" "]==]" "\n" + " )" "\n" + " endif()" "\n" + " include(\"${ctest_tests_file}\")" "\n" + "else()" "\n" + " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n" + "endif()" "\n" + ) + + if(GENERATOR_IS_MULTI_CONFIG) + foreach(_config ${CMAKE_CONFIGURATION_TYPES}) + file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>) + endforeach() + file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")") + else() + file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}") + file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")") + endif() + + else() + message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}") + endif() + + # Add discovered tests to directory TEST_INCLUDE_FILES + set_property(DIRECTORY + APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" + ) + +endfunction() + +############################################################################### + +set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT + ${CMAKE_CURRENT_LIST_DIR}/GTestAddTests.cmake +) + +# Restore project's policies +cmake_policy(POP) diff --git a/build_files/cmake/Modules/GTestAddTests.cmake b/build_files/cmake/Modules/GTestAddTests.cmake new file mode 100644 index 00000000000..8be07b8e2e5 --- /dev/null +++ b/build_files/cmake/Modules/GTestAddTests.cmake @@ -0,0 +1,191 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Blender: disable ASAN leak detection when trying to discover tests. +set(ENV{ASAN_OPTIONS} "detect_leaks=0") + +cmake_minimum_required(VERSION ${CMAKE_VERSION}) + +# Overwrite possibly existing ${_CTEST_FILE} with empty file +set(flush_tests_MODE WRITE) + +# Flushes script to ${_CTEST_FILE} +macro(flush_script) + file(${flush_tests_MODE} "${_CTEST_FILE}" "${script}") + set(flush_tests_MODE APPEND) + + set(script "") +endmacro() + +# Flushes tests_buffer to tests +macro(flush_tests_buffer) + list(APPEND tests "${tests_buffer}") + set(tests_buffer "") +endmacro() + +macro(add_command NAME) + set(_args "") + foreach(_arg ${ARGN}) + if(_arg MATCHES "[^-./:a-zA-Z0-9_]") + string(APPEND _args " [==[${_arg}]==]") + else() + string(APPEND _args " ${_arg}") + endif() + endforeach() + string(APPEND script "${NAME}(${_args})\n") + string(LENGTH "${script}" _script_len) + if(${_script_len} GREATER "50000") + flush_script() + endif() + # Unsets macro local variables to prevent leakage outside of this macro. + unset(_args) + unset(_script_len) +endmacro() + +function(gtest_discover_tests_impl) + + cmake_parse_arguments( + "" + "" + "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_EXECUTOR;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR" + "TEST_EXTRA_ARGS;TEST_PROPERTIES" + ${ARGN} + ) + + set(prefix "${_TEST_PREFIX}") + set(suffix "${_TEST_SUFFIX}") + set(extra_args ${_TEST_EXTRA_ARGS}) + set(properties ${_TEST_PROPERTIES}) + set(script) + set(suite) + set(tests) + set(tests_buffer) + + # Run test executable to get list of available tests + if(NOT EXISTS "${_TEST_EXECUTABLE}") + message(FATAL_ERROR + "Specified test executable does not exist.\n" + " Path: '${_TEST_EXECUTABLE}'" + ) + endif() + execute_process( + COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + TIMEOUT ${_TEST_DISCOVERY_TIMEOUT} + OUTPUT_VARIABLE output + RESULT_VARIABLE result + ) + if(NOT ${result} EQUAL 0) + string(REPLACE "\n" "\n " output "${output}") + message(FATAL_ERROR + "Error running test executable.\n" + " Path: '${_TEST_EXECUTABLE}'\n" + " Result: ${result}\n" + " Output:\n" + " ${output}\n" + ) + endif() + + # Preserve semicolon in test-parameters + string(REPLACE [[;]] [[\;]] output "${output}") + string(REPLACE "\n" ";" output "${output}") + + # Parse output + foreach(line ${output}) + # Skip header + if(NOT line MATCHES "gtest_main\\.cc") + # Do we have a module name or a test name? + if(NOT line MATCHES "^ ") + # Module; remove trailing '.' to get just the name... + string(REGEX REPLACE "\\.( *#.*)?" "" suite "${line}") + if(line MATCHES "#" AND NOT _NO_PRETTY_TYPES) + string(REGEX REPLACE "/[0-9]\\.+ +#.*= +" "/" pretty_suite "${line}") + else() + set(pretty_suite "${suite}") + endif() + string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}") + else() + # Test name; strip spaces and comments to get just the name... + string(REGEX REPLACE " +" "" test "${line}") + if(test MATCHES "#" AND NOT _NO_PRETTY_VALUES) + string(REGEX REPLACE "/[0-9]+#GetParam..=" "/" pretty_test "${test}") + else() + string(REGEX REPLACE "#.*" "" pretty_test "${test}") + endif() + string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}") + string(REGEX REPLACE "#.*" "" test "${test}") + if(NOT "${_TEST_XML_OUTPUT_DIR}" STREQUAL "") + set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${_TEST_XML_OUTPUT_DIR}/${prefix}${suite}.${test}${suffix}.xml") + else() + unset(TEST_XML_OUTPUT_PARAM) + endif() + + # sanitize test name for further processing downstream + set(testname "${prefix}${pretty_suite}.${pretty_test}${suffix}") + # escape \ + string(REPLACE [[\]] [[\\]] testname "${testname}") + # escape ; + string(REPLACE [[;]] [[\;]] testname "${testname}") + # escape $ + string(REPLACE [[$]] [[\$]] testname "${testname}") + + # ...and add to script + add_command(add_test + "${testname}" + ${_TEST_EXECUTOR} + "${_TEST_EXECUTABLE}" + "--gtest_filter=${suite}.${test}" + "--gtest_also_run_disabled_tests" + ${TEST_XML_OUTPUT_PARAM} + ${extra_args} + ) + if(suite MATCHES "^DISABLED" OR test MATCHES "^DISABLED") + add_command(set_tests_properties + "${testname}" + PROPERTIES DISABLED TRUE + ) + endif() + add_command(set_tests_properties + "${testname}" + PROPERTIES + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + SKIP_REGULAR_EXPRESSION "\\\\[ SKIPPED \\\\]" + ${properties} + ) + list(APPEND tests_buffer "${testname}") + list(LENGTH tests_buffer tests_buffer_length) + if(${tests_buffer_length} GREATER "250") + flush_tests_buffer() + endif() + endif() + endif() + endforeach() + + + # Create a list of all discovered tests, which users may use to e.g. set + # properties on the tests + flush_tests_buffer() + add_command(set ${_TEST_LIST} ${tests}) + + # Write CTest script + flush_script() + +endfunction() + +if(CMAKE_SCRIPT_MODE_FILE) + gtest_discover_tests_impl( + NO_PRETTY_TYPES ${NO_PRETTY_TYPES} + NO_PRETTY_VALUES ${NO_PRETTY_VALUES} + TEST_EXECUTABLE ${TEST_EXECUTABLE} + TEST_EXECUTOR ${TEST_EXECUTOR} + TEST_WORKING_DIR ${TEST_WORKING_DIR} + TEST_PREFIX ${TEST_PREFIX} + TEST_SUFFIX ${TEST_SUFFIX} + TEST_LIST ${TEST_LIST} + CTEST_FILE ${CTEST_FILE} + TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT} + TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR} + TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS} + TEST_PROPERTIES ${TEST_PROPERTIES} + ) +endif() diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake index 41bee263e22..7d3284af158 100644 --- a/build_files/cmake/config/blender_full.cmake +++ b/build_files/cmake/config/blender_full.cmake @@ -15,6 +15,7 @@ set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE) set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE) set(WITH_DRACO ON CACHE BOOL "" FORCE) set(WITH_FFTW3 ON CACHE BOOL "" FORCE) +set(WITH_GMP OFF CACHE BOOL "" FORCE) set(WITH_LIBMV ON CACHE BOOL "" FORCE) set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE) set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 68b9bd1d94d..16c15961c59 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -20,6 +20,7 @@ set(WITH_CYCLES_OSL OFF CACHE BOOL "" FORCE) set(WITH_CYCLES_DEVICE_OPTIX OFF CACHE BOOL "" FORCE) set(WITH_DRACO OFF CACHE BOOL "" FORCE) set(WITH_FFTW3 OFF CACHE BOOL "" FORCE) +set(WITH_GMP OFF CACHE BOOL "" FORCE) set(WITH_LIBMV OFF CACHE BOOL "" FORCE) set(WITH_LLVM OFF CACHE BOOL "" FORCE) set(WITH_COMPOSITOR OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index e6fc73a75ed..ddd9aa1d766 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -16,6 +16,7 @@ set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE) set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE) set(WITH_DRACO ON CACHE BOOL "" FORCE) set(WITH_FFTW3 ON CACHE BOOL "" FORCE) +set(WITH_GMP OFF CACHE BOOL "" FORCE) set(WITH_LIBMV ON CACHE BOOL "" FORCE) set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE) set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 77959f2cf5f..51cfadecc3e 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -169,6 +169,26 @@ function(blender_include_dirs_sys include_directories(SYSTEM ${_ALL_INCS}) endfunction() +# Set include paths for header files included with "*.h" syntax. +# This enables auto-complete suggestions for user header files on Xcode. +# Build process is not affected since the include paths are the same +# as in HEADER_SEARCH_PATHS. +function(blender_user_header_search_paths + name + includes + ) + + if(XCODE) + set(_ALL_INCS "") + foreach(_INC ${includes}) + get_filename_component(_ABS_INC ${_INC} ABSOLUTE) + # _ALL_INCS is a space-separated string of file paths in quotes. + set(_ALL_INCS "${_ALL_INCS} \"${_ABS_INC}\"") + endforeach() + set_target_properties(${name} PROPERTIES XCODE_ATTRIBUTE_USER_HEADER_SEARCH_PATHS "${_ALL_INCS}") + endif() +endfunction() + function(blender_source_group name sources @@ -317,6 +337,7 @@ function(blender_add_lib__impl # works fine without having the includes # listed is helpful for IDE's (QtCreator/MSVC) blender_source_group("${name}" "${sources}") + blender_user_header_search_paths("${name}" "${includes}") list_assert_duplicates("${sources}") list_assert_duplicates("${includes}") diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index a80b0b56901..c5c46a3b394 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -437,6 +437,14 @@ if(WITH_XR_OPENXR) endif() endif() +if(WITH_GMP) + find_package(GMP) + if(NOT GMP_FOUND) + message(WARNING "GMP not found, disabling WITH_GMP") + set(WITH_GMP OFF) + endif() +endif() + set(EXETYPE MACOSX_BUNDLE) set(CMAKE_C_FLAGS_DEBUG "-fno-strict-aliasing -g") diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index c5e8893424b..b623200a159 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -435,6 +435,14 @@ if(WITH_XR_OPENXR) endif() endif() +if(WITH_GMP) + find_package_wrapper(GMP) + if(NOT GMP_FOUND) + message(WARNING "GMP not found, disabling WITH_GMP") + set(WITH_GMP OFF) + endif() +endif() + if(EXISTS ${LIBDIR}) without_system_libs_end() endif() diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 01d48364435..dfcd5d75444 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -750,3 +750,10 @@ if(WITH_XR_OPENXR) set(WITH_XR_OPENXR OFF) endif() endif() + +if(WITH_GMP) + set(GMP_INCLUDE_DIRS ${LIBDIR}/gmp/include) + set(GMP_LIBRARIES ${LIBDIR}/gmp/lib/libgmp-10.lib optimized ${LIBDIR}/gmp/lib/libgmpxx.lib debug ${LIBDIR}/gmp/lib/libgmpxx_d.lib) + set(GMP_ROOT_DIR ${LIBDIR}/gmp) + set(GMP_FOUND On) +endif() diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 1e430823b10..6e4a087fa36 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "V2.90" +PROJECT_NUMBER = "V2.91" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/extern/bullet2/patches/btPolyhedralConvexShape_Inertia_fix.patch b/extern/bullet2/patches/btPolyhedralConvexShape_Inertia_fix.patch new file mode 100644 index 00000000000..abafb5855dd --- /dev/null +++ b/extern/bullet2/patches/btPolyhedralConvexShape_Inertia_fix.patch @@ -0,0 +1,41 @@ +diff --git a/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp b/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp +index 9095c592d87..b831e20c2f9 100644 +--- a/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp ++++ b/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp +@@ -406,17 +406,17 @@ void btPolyhedralConvexShape::calculateLocalInertia(btScalar mass,btVector3& ine + #ifndef __SPU__ + //not yet, return box inertia + +- btScalar margin = getMargin(); ++ //btScalar margin = getMargin(); + + btTransform ident; + ident.setIdentity(); + btVector3 aabbMin,aabbMax; +- getAabb(ident,aabbMin,aabbMax); ++ getAabb(ident,aabbMin,aabbMax); // This already contains the margin + btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5); + +- btScalar lx=btScalar(2.)*(halfExtents.x()+margin); +- btScalar ly=btScalar(2.)*(halfExtents.y()+margin); +- btScalar lz=btScalar(2.)*(halfExtents.z()+margin); ++ btScalar lx=btScalar(2.)*(halfExtents.x()); ++ btScalar ly=btScalar(2.)*(halfExtents.y()); ++ btScalar lz=btScalar(2.)*(halfExtents.z()); + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; +@@ -476,10 +476,10 @@ void btPolyhedralConvexAabbCachingShape::recalcLocalAabb() + + for ( int i = 0; i < 3; ++i ) + { +- m_localAabbMax[i] = _supporting[i][i] + m_collisionMargin; +- m_localAabbMin[i] = _supporting[i + 3][i] - m_collisionMargin; ++ m_localAabbMax[i] = _supporting[i][i]; ++ m_localAabbMin[i] = _supporting[i + 3][i]; + } +- ++ + #else + + for (int i=0;i<3;i++) diff --git a/extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp b/extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp index e8c8c336cd2..1e7f36e0a17 100644 --- a/extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp +++ b/extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp @@ -180,7 +180,7 @@ void btCompoundShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVect localHalfExtents += btVector3(getMargin(),getMargin(),getMargin()); - btMatrix3x3 abs_b = trans.getBasis().absolute(); + btMatrix3x3 abs_b = trans.getBasis().absolute(); btVector3 center = trans(localCenter); diff --git a/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp b/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp index 9095c592d87..b831e20c2f9 100644 --- a/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp +++ b/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp @@ -406,17 +406,17 @@ void btPolyhedralConvexShape::calculateLocalInertia(btScalar mass,btVector3& ine #ifndef __SPU__ //not yet, return box inertia - btScalar margin = getMargin(); + //btScalar margin = getMargin(); btTransform ident; ident.setIdentity(); btVector3 aabbMin,aabbMax; - getAabb(ident,aabbMin,aabbMax); + getAabb(ident,aabbMin,aabbMax); // This already contains the margin btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5); - btScalar lx=btScalar(2.)*(halfExtents.x()+margin); - btScalar ly=btScalar(2.)*(halfExtents.y()+margin); - btScalar lz=btScalar(2.)*(halfExtents.z()+margin); + btScalar lx=btScalar(2.)*(halfExtents.x()); + btScalar ly=btScalar(2.)*(halfExtents.y()); + btScalar lz=btScalar(2.)*(halfExtents.z()); const btScalar x2 = lx*lx; const btScalar y2 = ly*ly; const btScalar z2 = lz*lz; @@ -476,10 +476,10 @@ void btPolyhedralConvexAabbCachingShape::recalcLocalAabb() for ( int i = 0; i < 3; ++i ) { - m_localAabbMax[i] = _supporting[i][i] + m_collisionMargin; - m_localAabbMin[i] = _supporting[i + 3][i] - m_collisionMargin; + m_localAabbMax[i] = _supporting[i][i]; + m_localAabbMin[i] = _supporting[i + 3][i]; } - + #else for (int i=0;i<3;i++) diff --git a/extern/cuew/src/cuew.c b/extern/cuew/src/cuew.c index f477ec48a18..7a1b0018a24 100644 --- a/extern/cuew/src/cuew.c +++ b/extern/cuew/src/cuew.c @@ -879,7 +879,7 @@ int cuewCompilerVersion(void) { } /* get --version output */ - strncat(command, "\"", 1); + strcat(command, "\""); strncat(command, path, sizeof(command) - 1); strncat(command, "\" --version", sizeof(command) - strlen(path) - 1); pipe = popen(command, "r"); diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 49407799fcd..f4354d5166e 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -923,6 +923,74 @@ static void create_subd_mesh(Scene *scene, /* Sync */ +static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob, + BL::Scene /*b_scene*/) +{ + BL::Object::modifiers_iterator b_mod; + + for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) { + if (!b_mod->is_a(&RNA_MeshSequenceCacheModifier)) { + continue; + } + + BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(*b_mod); + + if (MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) { + return mesh_cache; + } + } + + return BL::MeshSequenceCacheModifier(PointerRNA_NULL); +} + +static void sync_mesh_cached_velocities(BL::Object &b_ob, + BL::Scene b_scene, + Scene *scene, + Mesh *mesh) +{ + if (scene->need_motion() == Scene::MOTION_NONE) + return; + + BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, b_scene); + + if (!b_mesh_cache) { + return; + } + + /* Find or add attribute */ + float3 *P = &mesh->verts[0]; + Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (!attr_mP) { + attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); + } + + if (!MeshSequenceCacheModifier_read_velocity_get(&b_mesh_cache.ptr)) { + return; + } + + const size_t numverts = mesh->verts.size(); + + if (b_mesh_cache.vertex_velocities.length() != numverts) { + return; + } + + /* Only export previous and next frame, we don't have any in between data. */ + float motion_times[2] = {-1.0f, 1.0f}; + for (int step = 0; step < 2; step++) { + const float relative_time = motion_times[step] * scene->motion_shutter_time() * 0.5f; + float3 *mP = attr_mP->data_float3() + step * numverts; + + BL::MeshSequenceCacheModifier::vertex_velocities_iterator vvi; + int i = 0; + + for (b_mesh_cache.vertex_velocities.begin(vvi); vvi != b_mesh_cache.vertex_velocities.end(); + ++vvi, ++i) { + mP[i] = P[i] + get_float3(vvi->velocity()) * relative_time; + } + } +} + static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh) { if (scene->need_motion() == Scene::MOTION_NONE) @@ -1002,6 +1070,9 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, } } + /* cached velocities (e.g. from alembic archive) */ + sync_mesh_cached_velocities(b_ob, b_depsgraph.scene(), scene, mesh); + /* mesh fluid motion mantaflow */ sync_mesh_fluid_motion(b_ob, scene, mesh); @@ -1023,6 +1094,12 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, return; } + /* Cached motion blur already exported. */ + BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, b_scene); + if (mesh_cache) { + return; + } + /* Skip if no vertices were exported. */ size_t numverts = mesh->verts.size(); if (numverts == 0) { diff --git a/intern/cycles/kernel/kernel_light_background.h b/intern/cycles/kernel/kernel_light_background.h index 30e336f0f80..5fa25069fcd 100644 --- a/intern/cycles/kernel/kernel_light_background.h +++ b/intern/cycles/kernel/kernel_light_background.h @@ -445,4 +445,4 @@ ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direct #endif -CCL_NAMESPACE_END
\ No newline at end of file +CCL_NAMESPACE_END diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index 436324b00ba..1b138455515 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -484,8 +484,8 @@ void ShaderGraph::remove_proxy_nodes() vector<ShaderInput *> links(output->links); foreach (ShaderInput *to, links) { - /* remove any autoconvert nodes too if they lead to - * sockets with an automatically set default value */ + /* Remove any auto-convert nodes too if they lead to + * sockets with an automatically set default value. */ ShaderNode *tonode = to->parent; if (tonode->special_type == SHADER_SPECIAL_TYPE_AUTOCONVERT) { diff --git a/intern/cycles/util/util_transform.cpp b/intern/cycles/util/util_transform.cpp index 6417752f704..e8233b7fe6d 100644 --- a/intern/cycles/util/util_transform.cpp +++ b/intern/cycles/util/util_transform.cpp @@ -317,7 +317,7 @@ void transform_motion_decompose(DecomposedTransform *decomp, const Transform *mo * * Note that this is very simple and naive implementation, which only deals with degenerated * scale happening only on one frame. It is possible to improve it further by interpolating - * rotation into s degenerated range using rotation from timesteps from adjacent non-degenerated + * rotation into s degenerated range using rotation from time-steps from adjacent non-degenerated * time steps. */ for (size_t i = 0; i < size; i++) { const float3 scale = make_float3(decomp[i].y.w, decomp[i].z.w, decomp[i].w.w); diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp index 02daad2111a..bac7057d953 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.cpp +++ b/intern/ghost/intern/GHOST_ContextEGL.cpp @@ -118,7 +118,7 @@ static const char *get_egl_error_message_string(EGLint error) case EGL_CONTEXT_LOST: return ( "A power management event has occurred. " - "The application must destroy all contexts and reinitialise OpenGL ES state " + "The application must destroy all contexts and reinitialize OpenGL ES state " "and objects to continue rendering."); default: diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 96073c21e79..74bdff3812e 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -1951,7 +1951,7 @@ void GHOST_SystemX11::getClipboard_xcout(const XEvent *evt, switch (*context) { /* There is no context, do an XConvertSelection() */ case XCLIB_XCOUT_NONE: - /* Initialise return length to 0 */ + /* Initialize return length to 0. */ if (*len > 0) { free(*txt); *len = 0; diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index 604330bd1d3..9c62b2396f6 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -213,7 +213,7 @@ extern const char *(*MEM_name_ptr)(void *vmemh); /** This should be called as early as possible in the program. When it has been called, information * about memory leaks will be printed on exit. */ -void MEM_initialize_memleak_detection(void); +void MEM_init_memleak_detection(void); /* Switch allocator to slower but fully guarded mode. */ void MEM_use_guarded_allocator(void); diff --git a/intern/guardedalloc/intern/leak_detector.cc b/intern/guardedalloc/intern/leak_detector.cc index 4b2689ee28c..d7b6f749742 100644 --- a/intern/guardedalloc/intern/leak_detector.cc +++ b/intern/guardedalloc/intern/leak_detector.cc @@ -46,7 +46,7 @@ class MemLeakPrinter { }; } // namespace -void MEM_initialize_memleak_detection(void) +void MEM_init_memleak_detection(void) { /** * This variable is constructed when this function is first called. This should happen as soon as diff --git a/intern/mantaflow/extern/manta_fluid_API.h b/intern/mantaflow/extern/manta_fluid_API.h index 3da1d8f53f0..124671467f7 100644 --- a/intern/mantaflow/extern/manta_fluid_API.h +++ b/intern/mantaflow/extern/manta_fluid_API.h @@ -33,10 +33,10 @@ struct MANTA; /* Fluid functions */ struct MANTA *manta_init(int *res, struct FluidModifierData *fmd); void manta_free(struct MANTA *fluid); -void manta_ensure_obstacle(struct MANTA *fluid, struct FluidModifierData *fmd); -void manta_ensure_guiding(struct MANTA *fluid, struct FluidModifierData *fmd); -void manta_ensure_invelocity(struct MANTA *fluid, struct FluidModifierData *fmd); -void manta_ensure_outflow(struct MANTA *fluid, struct FluidModifierData *fmd); +int manta_ensure_obstacle(struct MANTA *fluid, struct FluidModifierData *fmd); +int manta_ensure_guiding(struct MANTA *fluid, struct FluidModifierData *fmd); +int manta_ensure_invelocity(struct MANTA *fluid, struct FluidModifierData *fmd); +int manta_ensure_outflow(struct MANTA *fluid, struct FluidModifierData *fmd); int manta_write_config(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr); int manta_write_data(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr); int manta_write_noise(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr); @@ -77,6 +77,7 @@ int manta_get_frame(struct MANTA *fluid); float manta_get_timestep(struct MANTA *fluid); void manta_adapt_timestep(struct MANTA *fluid); bool manta_needs_realloc(struct MANTA *fluid, struct FluidModifierData *fmd); +void manta_update_pointers(struct MANTA *fluid, struct FluidModifierData *fmd, bool flush); /* Fluid accessors */ size_t manta_get_index(int x, int max_x, int y, int max_y, int z /*, int max_z */); @@ -121,9 +122,9 @@ void manta_noise_get_rgba_fixed_color(struct MANTA *smoke, float color[3], float *data, int sequential); -void manta_smoke_ensure_heat(struct MANTA *smoke, struct FluidModifierData *fmd); -void manta_smoke_ensure_fire(struct MANTA *smoke, struct FluidModifierData *fmd); -void manta_smoke_ensure_colors(struct MANTA *smoke, struct FluidModifierData *fmd); +int manta_smoke_ensure_heat(struct MANTA *smoke, struct FluidModifierData *fmd); +int manta_smoke_ensure_fire(struct MANTA *smoke, struct FluidModifierData *fmd); +int manta_smoke_ensure_colors(struct MANTA *smoke, struct FluidModifierData *fmd); /* Smoke accessors */ float *manta_smoke_get_density(struct MANTA *smoke); @@ -167,7 +168,7 @@ int manta_noise_get_cells(struct MANTA *smoke); /* Liquid functions */ void manta_liquid_export_script(struct MANTA *smoke, struct FluidModifierData *fmd); -void manta_liquid_ensure_sndparts(struct MANTA *fluid, struct FluidModifierData *fmd); +int manta_liquid_ensure_sndparts(struct MANTA *fluid, struct FluidModifierData *fmd); /* Liquid accessors */ int manta_liquid_get_particle_res_x(struct MANTA *liquid); diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp index 5b2cbb09979..586c413b044 100644 --- a/intern/mantaflow/intern/MANTA_main.cpp +++ b/intern/mantaflow/intern/MANTA_main.cpp @@ -27,10 +27,6 @@ #include <sstream> #include <zlib.h> -#if OPENVDB == 1 -# include "openvdb/openvdb.h" -#endif - #include "MANTA_main.h" #include "Python.h" #include "fluid_script.h" @@ -60,13 +56,6 @@ using std::to_string; atomic<int> MANTA::solverID(0); int MANTA::with_debug(0); -/* Number of particles that the cache reads at once (with zlib). */ -#define PARTICLE_CHUNK 20000 -/* Number of mesh nodes that the cache reads at once (with zlib). */ -#define NODE_CHUNK 20000 -/* Number of mesh triangles that the cache reads at once (with zlib). */ -#define TRIANGLE_CHUNK 20000 - MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) { if (with_debug) @@ -96,8 +85,7 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) mUsingInvel = (fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL); mUsingOutflow = (fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW); - // Simulation constants - mTempAmb = 0; // TODO: Maybe use this later for buoyancy calculation + /* Simulation constants. */ mResX = res[0]; mResY = res[1]; mResZ = res[2]; @@ -105,7 +93,7 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) mTotalCells = mResX * mResY * mResZ; mResGuiding = fds->res; - // Smoke low res grids + /* Smoke low res grids. */ mDensity = nullptr; mShadow = nullptr; mHeat = nullptr; @@ -131,7 +119,7 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) mReactIn = nullptr; mEmissionIn = nullptr; - // Smoke high res grids + /* Smoke high res grids. */ mDensityHigh = nullptr; mFlameHigh = nullptr; mFuelHigh = nullptr; @@ -146,19 +134,19 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) mTextureV2 = nullptr; mTextureW2 = nullptr; - // Fluid low res grids + /* Fluid low res grids. */ mPhiIn = nullptr; mPhiStaticIn = nullptr; mPhiOutIn = nullptr; mPhiOutStaticIn = nullptr; mPhi = nullptr; - // Mesh + /* Mesh. */ mMeshNodes = nullptr; mMeshTriangles = nullptr; mMeshVelocities = nullptr; - // Fluid obstacle + /* Fluid obstacle. */ mPhiObsIn = nullptr; mPhiObsStaticIn = nullptr; mNumObstacle = nullptr; @@ -166,47 +154,48 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) mObVelocityY = nullptr; mObVelocityZ = nullptr; - // Fluid guiding + /* Fluid guiding. */ mPhiGuideIn = nullptr; mNumGuide = nullptr; mGuideVelocityX = nullptr; mGuideVelocityY = nullptr; mGuideVelocityZ = nullptr; - // Fluid initial velocity + /* Fluid initial velocity. */ mInVelocityX = nullptr; mInVelocityY = nullptr; mInVelocityZ = nullptr; - // Secondary particles + /* Secondary particles. */ mFlipParticleData = nullptr; mFlipParticleVelocity = nullptr; - mSndParticleData = nullptr; - mSndParticleVelocity = nullptr; - mSndParticleLife = nullptr; + mParticleData = nullptr; + mParticleVelocity = nullptr; + mParticleLife = nullptr; - // Cache read success indicators + /* Cache read success indicators. */ mFlipFromFile = false; mMeshFromFile = false; mParticlesFromFile = false; - // Setup Mantaflow in Python + /* Setup Mantaflow in Python. */ initializeMantaflow(); - // Initializa RNA map with values that Python will need + /* Initializa RNA map with values that Python will need. */ initializeRNAMap(fmd); - // Initialize Mantaflow variables in Python - // Liquid + bool initSuccess = true; + /* Initialize Mantaflow variables in Python. */ + /* Liquid. */ if (mUsingLiquid) { - initDomain(); - initLiquid(); + initSuccess &= initDomain(); + initSuccess &= initLiquid(); if (mUsingObstacle) - initObstacle(); + initSuccess &= initObstacle(); if (mUsingInvel) - initInVelocity(); + initSuccess &= initInVelocity(); if (mUsingOutflow) - initOutflow(); + initSuccess &= initOutflow(); if (mUsingDrops || mUsingBubbles || mUsingFloats || mUsingTracers) { mUpresParticle = fds->particle_scale; @@ -215,8 +204,8 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) mResZParticle = mUpresParticle * mResZ; mTotalCellsParticles = mResXParticle * mResYParticle * mResZParticle; - initSndParts(); - initLiquidSndParts(); + initSuccess &= initSndParts(); + initSuccess &= initLiquidSndParts(); } if (mUsingMesh) { @@ -226,44 +215,44 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) mResZMesh = mUpresMesh * mResZ; mTotalCellsMesh = mResXMesh * mResYMesh * mResZMesh; - // Initialize Mantaflow variables in Python - initMesh(); - initLiquidMesh(); + /* Initialize Mantaflow variables in Python. */ + initSuccess &= initMesh(); + initSuccess &= initLiquidMesh(); } if (mUsingDiffusion) { - initCurvature(); + initSuccess &= initCurvature(); } if (mUsingGuiding) { mResGuiding = (fds->guide_parent) ? fds->guide_res : fds->res; - initGuiding(); + initSuccess &= initGuiding(); } if (mUsingFractions) { - initFractions(); + initSuccess &= initFractions(); } } - // Smoke + /* Smoke. */ if (mUsingSmoke) { - initDomain(); - initSmoke(); + initSuccess &= initDomain(); + initSuccess &= initSmoke(); if (mUsingHeat) - initHeat(); + initSuccess &= initHeat(); if (mUsingFire) - initFire(); + initSuccess &= initFire(); if (mUsingColors) - initColors(); + initSuccess &= initColors(); if (mUsingObstacle) - initObstacle(); + initSuccess &= initObstacle(); if (mUsingInvel) - initInVelocity(); + initSuccess &= initInVelocity(); if (mUsingOutflow) - initOutflow(); + initSuccess &= initOutflow(); if (mUsingGuiding) { mResGuiding = (fds->guide_parent) ? fds->guide_res : fds->res; - initGuiding(); + initSuccess &= initGuiding(); } if (mUsingNoise) { @@ -273,31 +262,33 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID) mResZNoise = amplify * mResZ; mTotalCellsHigh = mResXNoise * mResYNoise * mResZNoise; - // Initialize Mantaflow variables in Python - initNoise(); - initSmokeNoise(); + /* Initialize Mantaflow variables in Python. */ + initSuccess &= initNoise(); + initSuccess &= initSmokeNoise(); if (mUsingFire) - initFireHigh(); + initSuccess &= initFireHigh(); if (mUsingColors) - initColorsHigh(); + initSuccess &= initColorsHigh(); } } - updatePointers(); + /* All requested initializations must not fail in constructor. */ + BLI_assert(initSuccess); + updatePointers(fmd); } -void MANTA::initDomain(FluidModifierData *fmd) +bool MANTA::initDomain(FluidModifierData *fmd) { - // Vector will hold all python commands that are to be executed + /* Vector will hold all python commands that are to be executed. */ vector<string> pythonCommands; - // Set manta debug level first + /* Set manta debug level first. */ pythonCommands.push_back(manta_import + manta_debuglevel); ostringstream ss; ss << "set_manta_debuglevel(" << with_debug << ")"; pythonCommands.push_back(ss.str()); - // Now init basic fluid domain + /* Now init basic fluid domain. */ string tmpString = fluid_variables + fluid_solver + fluid_alloc + fluid_cache_helper + fluid_bake_multiprocessing + fluid_bake_data + fluid_bake_noise + fluid_bake_mesh + fluid_bake_particles + fluid_bake_guiding + @@ -305,20 +296,20 @@ void MANTA::initDomain(FluidModifierData *fmd) fluid_adapt_time_step + fluid_time_stepping; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); + return runPythonString(pythonCommands); } -void MANTA::initNoise(FluidModifierData *fmd) +bool MANTA::initNoise(FluidModifierData *fmd) { vector<string> pythonCommands; string tmpString = fluid_variables_noise + fluid_solver_noise; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); + return runPythonString(pythonCommands); } -void MANTA::initSmoke(FluidModifierData *fmd) +bool MANTA::initSmoke(FluidModifierData *fmd) { vector<string> pythonCommands; string tmpString = smoke_variables + smoke_alloc + smoke_adaptive_step + smoke_save_data + @@ -326,10 +317,10 @@ void MANTA::initSmoke(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); + return runPythonString(pythonCommands); } -void MANTA::initSmokeNoise(FluidModifierData *fmd) +bool MANTA::initSmokeNoise(FluidModifierData *fmd) { vector<string> pythonCommands; string tmpString = smoke_variables_noise + smoke_alloc_noise + smoke_wavelet_noise + @@ -337,11 +328,11 @@ void MANTA::initSmokeNoise(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingNoise = true; + return runPythonString(pythonCommands); } -void MANTA::initHeat(FluidModifierData *fmd) +bool MANTA::initHeat(FluidModifierData *fmd) { if (!mHeat) { vector<string> pythonCommands; @@ -349,12 +340,13 @@ void MANTA::initHeat(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingHeat = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initFire(FluidModifierData *fmd) +bool MANTA::initFire(FluidModifierData *fmd) { if (!mFuel) { vector<string> pythonCommands; @@ -362,12 +354,13 @@ void MANTA::initFire(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingFire = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initFireHigh(FluidModifierData *fmd) +bool MANTA::initFireHigh(FluidModifierData *fmd) { if (!mFuelHigh) { vector<string> pythonCommands; @@ -375,12 +368,13 @@ void MANTA::initFireHigh(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingFire = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initColors(FluidModifierData *fmd) +bool MANTA::initColors(FluidModifierData *fmd) { if (!mColorR) { vector<string> pythonCommands; @@ -388,12 +382,13 @@ void MANTA::initColors(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingColors = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initColorsHigh(FluidModifierData *fmd) +bool MANTA::initColorsHigh(FluidModifierData *fmd) { if (!mColorRHigh) { vector<string> pythonCommands; @@ -401,12 +396,13 @@ void MANTA::initColorsHigh(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingColors = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initLiquid(FluidModifierData *fmd) +bool MANTA::initLiquid(FluidModifierData *fmd) { if (!mPhiIn) { vector<string> pythonCommands; @@ -415,44 +411,45 @@ void MANTA::initLiquid(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingLiquid = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initMesh(FluidModifierData *fmd) +bool MANTA::initMesh(FluidModifierData *fmd) { vector<string> pythonCommands; string tmpString = fluid_variables_mesh + fluid_solver_mesh + liquid_load_mesh; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingMesh = true; + return runPythonString(pythonCommands); } -void MANTA::initLiquidMesh(FluidModifierData *fmd) +bool MANTA::initLiquidMesh(FluidModifierData *fmd) { vector<string> pythonCommands; string tmpString = liquid_alloc_mesh + liquid_step_mesh + liquid_save_mesh; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingMesh = true; + return runPythonString(pythonCommands); } -void MANTA::initCurvature(FluidModifierData *fmd) +bool MANTA::initCurvature(FluidModifierData *fmd) { std::vector<std::string> pythonCommands; std::string finalString = parseScript(liquid_alloc_curvature, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingDiffusion = true; + return runPythonString(pythonCommands); } -void MANTA::initObstacle(FluidModifierData *fmd) +bool MANTA::initObstacle(FluidModifierData *fmd) { if (!mPhiObsIn) { vector<string> pythonCommands; @@ -460,12 +457,13 @@ void MANTA::initObstacle(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingObstacle = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initGuiding(FluidModifierData *fmd) +bool MANTA::initGuiding(FluidModifierData *fmd) { if (!mPhiGuideIn) { vector<string> pythonCommands; @@ -474,23 +472,24 @@ void MANTA::initGuiding(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingGuiding = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initFractions(FluidModifierData *fmd) +bool MANTA::initFractions(FluidModifierData *fmd) { vector<string> pythonCommands; string tmpString = fluid_alloc_fractions + fluid_with_fractions; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingFractions = true; + return runPythonString(pythonCommands); } -void MANTA::initInVelocity(FluidModifierData *fmd) +bool MANTA::initInVelocity(FluidModifierData *fmd) { if (!mInVelocityX) { vector<string> pythonCommands; @@ -498,12 +497,13 @@ void MANTA::initInVelocity(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingInvel = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initOutflow(FluidModifierData *fmd) +bool MANTA::initOutflow(FluidModifierData *fmd) { if (!mPhiOutIn) { vector<string> pythonCommands; @@ -511,24 +511,25 @@ void MANTA::initOutflow(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); mUsingOutflow = true; + return runPythonString(pythonCommands); } + return false; } -void MANTA::initSndParts(FluidModifierData *fmd) +bool MANTA::initSndParts(FluidModifierData *fmd) { vector<string> pythonCommands; string tmpString = fluid_variables_particles + fluid_solver_particles; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); + return runPythonString(pythonCommands); } -void MANTA::initLiquidSndParts(FluidModifierData *fmd) +bool MANTA::initLiquidSndParts(FluidModifierData *fmd) { - if (!mSndParticleData) { + if (!mParticleData) { vector<string> pythonCommands; string tmpString = liquid_alloc_particles + liquid_variables_particles + liquid_step_particles + fluid_with_sndparts + liquid_load_particles + @@ -536,8 +537,9 @@ void MANTA::initLiquidSndParts(FluidModifierData *fmd) string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); - runPythonString(pythonCommands); + return runPythonString(pythonCommands); } + return false; } MANTA::~MANTA() @@ -546,7 +548,7 @@ MANTA::~MANTA() cout << "~FLUID: " << mCurrentID << " with res(" << mResX << ", " << mResY << ", " << mResZ << ")" << endl; - // Destruction string for Python + /* Destruction string for Python. */ string tmpString = ""; vector<string> pythonCommands; bool result = false; @@ -554,10 +556,10 @@ MANTA::~MANTA() tmpString += manta_import; tmpString += fluid_delete_all; - // Initializa RNA map with values that Python will need + /* Initializa RNA map with values that Python will need. */ initializeRNAMap(); - // Leave out fmd argument in parseScript since only looking up IDs + /* Leave out fmd argument in parseScript since only looking up IDs. */ string finalString = parseScript(tmpString); pythonCommands.push_back(finalString); result = runPythonString(pythonCommands); @@ -619,10 +621,10 @@ void MANTA::initializeMantaflow() string filename = "manta_scene_" + to_string(mCurrentID) + ".py"; vector<string> fill = vector<string>(); - // Initialize extension classes and wrappers + /* Initialize extension classes and wrappers. */ srand(0); PyGILState_STATE gilstate = PyGILState_Ensure(); - Pb::setup(filename, fill); // Namespace from Mantaflow (registry) + Pb::setup(filename, fill); /* Namespace from Mantaflow (registry). */ PyGILState_Release(gilstate); } @@ -632,7 +634,7 @@ void MANTA::terminateMantaflow() cout << "Fluid: Releasing Mantaflow framework" << endl; PyGILState_STATE gilstate = PyGILState_Ensure(); - Pb::finalize(); // Namespace from Mantaflow (registry) + Pb::finalize(); /* Namespace from Mantaflow (registry). */ PyGILState_Release(gilstate); } @@ -1073,7 +1075,7 @@ string MANTA::parseScript(const string &setup_string, FluidModifierData *fmd) ostringstream res; string line = ""; - // Update RNA map if modifier data is handed over + /* Update RNA map if modifier data is handed over. */ if (fmd) { initializeRNAMap(fmd); } @@ -1111,7 +1113,8 @@ bool MANTA::writeConfiguration(FluidModifierData *fmd, int framenr) /* Create 'config' subdir if it does not exist already. */ BLI_dir_create_recursive(directory.c_str()); - gzFile gzf = (gzFile)BLI_gzopen(file.c_str(), "wb1"); // do some compression + /* Open new file with some compression. */ + gzFile gzf = (gzFile)BLI_gzopen(file.c_str(), "wb1"); if (!gzf) { cerr << "Fluid Error -- Cannot open file " << file << endl; return false; @@ -1615,10 +1618,10 @@ void MANTA::exportSmokeScript(FluidModifierData *fmd) string manta_script; - // Libraries + /* Libraries. */ manta_script += header_libraries + manta_import; - // Variables + /* Variables. */ manta_script += header_variables + fluid_variables + smoke_variables; if (noise) { manta_script += fluid_variables_noise + smoke_variables_noise; @@ -1626,14 +1629,14 @@ void MANTA::exportSmokeScript(FluidModifierData *fmd) if (guiding) manta_script += fluid_variables_guiding; - // Solvers + /* Solvers. */ manta_script += header_solvers + fluid_solver; if (noise) manta_script += fluid_solver_noise; if (guiding) manta_script += fluid_solver_guiding; - // Grids + /* Grids. */ manta_script += header_grids + fluid_alloc + smoke_alloc; if (noise) { manta_script += smoke_alloc_noise; @@ -1657,36 +1660,36 @@ void MANTA::exportSmokeScript(FluidModifierData *fmd) if (outflow) manta_script += fluid_alloc_outflow; - // Noise field + /* Noise field. */ if (noise) manta_script += smoke_wavelet_noise; - // Time + /* Time. */ manta_script += header_time + fluid_time_stepping + fluid_adapt_time_step; - // Import + /* Import. */ manta_script += header_import + fluid_file_import + fluid_cache_helper + smoke_load_data; if (noise) manta_script += smoke_load_noise; if (guiding) manta_script += fluid_load_guiding; - // Pre/Post Steps + /* Pre/Post Steps. */ manta_script += header_prepost + fluid_pre_step + fluid_post_step; - // Steps + /* Steps. */ manta_script += header_steps + smoke_adaptive_step + smoke_step; if (noise) { manta_script += smoke_step_noise; } - // Main + /* Main. */ manta_script += header_main + smoke_standalone + fluid_standalone; - // Fill in missing variables in script + /* Fill in missing variables in script. */ string final_script = MANTA::parseScript(manta_script, fmd); - // Write script + /* Write script. */ ofstream myfile; myfile.open(cacheDirScript); myfile << final_script; @@ -1725,10 +1728,10 @@ void MANTA::exportLiquidScript(FluidModifierData *fmd) string manta_script; - // Libraries + /* Libraries. */ manta_script += header_libraries + manta_import; - // Variables + /* Variables. */ manta_script += header_variables + fluid_variables + liquid_variables; if (mesh) manta_script += fluid_variables_mesh; @@ -1737,7 +1740,7 @@ void MANTA::exportLiquidScript(FluidModifierData *fmd) if (guiding) manta_script += fluid_variables_guiding; - // Solvers + /* Solvers. */ manta_script += header_solvers + fluid_solver; if (mesh) manta_script += fluid_solver_mesh; @@ -1746,7 +1749,7 @@ void MANTA::exportLiquidScript(FluidModifierData *fmd) if (guiding) manta_script += fluid_solver_guiding; - // Grids + /* Grids. */ manta_script += header_grids + fluid_alloc + liquid_alloc; if (mesh) manta_script += liquid_alloc_mesh; @@ -1763,13 +1766,13 @@ void MANTA::exportLiquidScript(FluidModifierData *fmd) if (outflow) manta_script += fluid_alloc_outflow; - // Domain init + /* Domain init. */ manta_script += header_gridinit + liquid_init_phi; - // Time + /* Time. */ manta_script += header_time + fluid_time_stepping + fluid_adapt_time_step; - // Import + /* Import. */ manta_script += header_import + fluid_file_import + fluid_cache_helper + liquid_load_data; if (mesh) manta_script += liquid_load_mesh; @@ -1778,23 +1781,23 @@ void MANTA::exportLiquidScript(FluidModifierData *fmd) if (guiding) manta_script += fluid_load_guiding; - // Pre/Post Steps + /* Pre/Post Steps. */ manta_script += header_prepost + fluid_pre_step + fluid_post_step; - // Steps + /* Steps. */ manta_script += header_steps + liquid_adaptive_step + liquid_step; if (mesh) manta_script += liquid_step_mesh; if (drops || bubble || floater || tracer) manta_script += liquid_step_particles; - // Main + /* Main. */ manta_script += header_main + liquid_standalone + fluid_standalone; - // Fill in missing variables in script + /* Fill in missing variables in script. */ string final_script = MANTA::parseScript(manta_script, fmd); - // Write script + /* Write script. */ ofstream myfile; myfile.open(cacheDirScript); myfile << final_script; @@ -1823,12 +1826,18 @@ static PyObject *callPythonFunction(string varName, string functionName, bool is /* Be sure to initialize Python before using it. */ Py_Initialize(); - // Get pyobject that holds result value + /* Get pyobject that holds result value. */ if (!manta_main_module) { PyGILState_Release(gilstate); return nullptr; } + /* Ensure that requested variable is present in module - avoid attribute errors later on. */ + if (!PyObject_HasAttrString(manta_main_module, varName.c_str())) { + PyGILState_Release(gilstate); + return nullptr; + } + var = PyObject_GetAttrString(manta_main_module, varName.c_str()); if (!var) { PyGILState_Release(gilstate); @@ -1911,6 +1920,11 @@ static long pyObjectToLong(PyObject *inputObject) return result; } +template<class T> static T *getPointer(string pyObjectName, string pyFunctionName) +{ + return static_cast<T *>(pyObjectToPointer(callPythonFunction(pyObjectName, pyFunctionName))); +} + int MANTA::getFrame() { if (with_debug) @@ -1955,137 +1969,137 @@ void MANTA::adaptTimestep() runPythonString(pythonCommands); } -void MANTA::updatePointers() +void MANTA::updatePointers(FluidModifierData *fmd, bool flush) { if (with_debug) cout << "MANTA::updatePointers()" << endl; + FluidDomainSettings *fds = fmd->domain; + + bool liquid = !flush && (fds->type == FLUID_DOMAIN_TYPE_LIQUID); + bool smoke = !flush && (fds->type == FLUID_DOMAIN_TYPE_GAS); + bool noise = !flush && smoke && fds->flags & FLUID_DOMAIN_USE_NOISE; + bool heat = !flush && smoke && fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT; + bool colors = !flush && smoke && fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS; + bool fire = !flush && smoke && fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE; + bool obstacle = !flush && fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE; + bool guiding = !flush && fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; + bool invel = !flush && fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; + bool outflow = !flush && fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; + bool drops = !flush && liquid && fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY; + bool bubble = !flush && liquid && fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE; + bool floater = !flush && liquid && fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM; + bool tracer = !flush && liquid && fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER; + bool parts = !flush && liquid && (drops | bubble | floater | tracer); + bool mesh = !flush && liquid && fds->flags & FLUID_DOMAIN_USE_MESH; + bool meshvel = !flush && liquid && mesh && fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS; + string func = "getDataPointer"; string funcNodes = "getNodesDataPointer"; string funcTris = "getTrisDataPointer"; string id = to_string(mCurrentID); - string solver = "s" + id; - string parts = "pp" + id; - string snd = "sp" + id; - string mesh = "sm" + id; - string mesh2 = "mesh" + id; - string noise = "sn" + id; - string solver_ext = "_" + solver; - string parts_ext = "_" + parts; - string snd_ext = "_" + snd; - string mesh_ext = "_" + mesh; - string mesh_ext2 = "_" + mesh2; - string noise_ext = "_" + noise; - - mFlags = (int *)pyObjectToPointer(callPythonFunction("flags" + solver_ext, func)); - mPhiIn = (float *)pyObjectToPointer(callPythonFunction("phiIn" + solver_ext, func)); - mPhiStaticIn = (float *)pyObjectToPointer(callPythonFunction("phiSIn" + solver_ext, func)); - mVelocityX = (float *)pyObjectToPointer(callPythonFunction("x_vel" + solver_ext, func)); - mVelocityY = (float *)pyObjectToPointer(callPythonFunction("y_vel" + solver_ext, func)); - mVelocityZ = (float *)pyObjectToPointer(callPythonFunction("z_vel" + solver_ext, func)); - mForceX = (float *)pyObjectToPointer(callPythonFunction("x_force" + solver_ext, func)); - mForceY = (float *)pyObjectToPointer(callPythonFunction("y_force" + solver_ext, func)); - mForceZ = (float *)pyObjectToPointer(callPythonFunction("z_force" + solver_ext, func)); - - if (mUsingOutflow) { - mPhiOutIn = (float *)pyObjectToPointer(callPythonFunction("phiOutIn" + solver_ext, func)); - mPhiOutStaticIn = (float *)pyObjectToPointer( - callPythonFunction("phiOutSIn" + solver_ext, func)); - } - if (mUsingObstacle) { - mPhiObsIn = (float *)pyObjectToPointer(callPythonFunction("phiObsIn" + solver_ext, func)); - mPhiObsStaticIn = (float *)pyObjectToPointer( - callPythonFunction("phiObsSIn" + solver_ext, func)); - mObVelocityX = (float *)pyObjectToPointer(callPythonFunction("x_obvel" + solver_ext, func)); - mObVelocityY = (float *)pyObjectToPointer(callPythonFunction("y_obvel" + solver_ext, func)); - mObVelocityZ = (float *)pyObjectToPointer(callPythonFunction("z_obvel" + solver_ext, func)); - mNumObstacle = (float *)pyObjectToPointer(callPythonFunction("numObs" + solver_ext, func)); - } - if (mUsingGuiding) { - mPhiGuideIn = (float *)pyObjectToPointer(callPythonFunction("phiGuideIn" + solver_ext, func)); - mGuideVelocityX = (float *)pyObjectToPointer( - callPythonFunction("x_guidevel" + solver_ext, func)); - mGuideVelocityY = (float *)pyObjectToPointer( - callPythonFunction("y_guidevel" + solver_ext, func)); - mGuideVelocityZ = (float *)pyObjectToPointer( - callPythonFunction("z_guidevel" + solver_ext, func)); - mNumGuide = (float *)pyObjectToPointer(callPythonFunction("numGuides" + solver_ext, func)); - } - if (mUsingInvel) { - mInVelocityX = (float *)pyObjectToPointer(callPythonFunction("x_invel" + solver_ext, func)); - mInVelocityY = (float *)pyObjectToPointer(callPythonFunction("y_invel" + solver_ext, func)); - mInVelocityZ = (float *)pyObjectToPointer(callPythonFunction("z_invel" + solver_ext, func)); - } - if (mUsingSmoke) { - mDensity = (float *)pyObjectToPointer(callPythonFunction("density" + solver_ext, func)); - mDensityIn = (float *)pyObjectToPointer(callPythonFunction("densityIn" + solver_ext, func)); - mShadow = (float *)pyObjectToPointer(callPythonFunction("shadow" + solver_ext, func)); - mEmissionIn = (float *)pyObjectToPointer(callPythonFunction("emissionIn" + solver_ext, func)); - } - if (mUsingSmoke && mUsingHeat) { - mHeat = (float *)pyObjectToPointer(callPythonFunction("heat" + solver_ext, func)); - mHeatIn = (float *)pyObjectToPointer(callPythonFunction("heatIn" + solver_ext, func)); - } - if (mUsingSmoke && mUsingFire) { - mFlame = (float *)pyObjectToPointer(callPythonFunction("flame" + solver_ext, func)); - mFuel = (float *)pyObjectToPointer(callPythonFunction("fuel" + solver_ext, func)); - mReact = (float *)pyObjectToPointer(callPythonFunction("react" + solver_ext, func)); - mFuelIn = (float *)pyObjectToPointer(callPythonFunction("fuelIn" + solver_ext, func)); - mReactIn = (float *)pyObjectToPointer(callPythonFunction("reactIn" + solver_ext, func)); - } - if (mUsingSmoke && mUsingColors) { - mColorR = (float *)pyObjectToPointer(callPythonFunction("color_r" + solver_ext, func)); - mColorG = (float *)pyObjectToPointer(callPythonFunction("color_g" + solver_ext, func)); - mColorB = (float *)pyObjectToPointer(callPythonFunction("color_b" + solver_ext, func)); - mColorRIn = (float *)pyObjectToPointer(callPythonFunction("color_r_in" + solver_ext, func)); - mColorGIn = (float *)pyObjectToPointer(callPythonFunction("color_g_in" + solver_ext, func)); - mColorBIn = (float *)pyObjectToPointer(callPythonFunction("color_b_in" + solver_ext, func)); - } - if (mUsingSmoke && mUsingNoise) { - mDensityHigh = (float *)pyObjectToPointer(callPythonFunction("density" + noise_ext, func)); - mTextureU = (float *)pyObjectToPointer(callPythonFunction("texture_u" + solver_ext, func)); - mTextureV = (float *)pyObjectToPointer(callPythonFunction("texture_v" + solver_ext, func)); - mTextureW = (float *)pyObjectToPointer(callPythonFunction("texture_w" + solver_ext, func)); - mTextureU2 = (float *)pyObjectToPointer(callPythonFunction("texture_u2" + solver_ext, func)); - mTextureV2 = (float *)pyObjectToPointer(callPythonFunction("texture_v2" + solver_ext, func)); - mTextureW2 = (float *)pyObjectToPointer(callPythonFunction("texture_w2" + solver_ext, func)); - } - if (mUsingSmoke && mUsingNoise && mUsingFire) { - mFlameHigh = (float *)pyObjectToPointer(callPythonFunction("flame" + noise_ext, func)); - mFuelHigh = (float *)pyObjectToPointer(callPythonFunction("fuel" + noise_ext, func)); - mReactHigh = (float *)pyObjectToPointer(callPythonFunction("react" + noise_ext, func)); - } - if (mUsingSmoke && mUsingNoise && mUsingColors) { - mColorRHigh = (float *)pyObjectToPointer(callPythonFunction("color_r" + noise_ext, func)); - mColorGHigh = (float *)pyObjectToPointer(callPythonFunction("color_g" + noise_ext, func)); - mColorBHigh = (float *)pyObjectToPointer(callPythonFunction("color_b" + noise_ext, func)); - } - if (mUsingLiquid) { - mPhi = (float *)pyObjectToPointer(callPythonFunction("phi" + solver_ext, func)); - mFlipParticleData = (vector<pData> *)pyObjectToPointer( - callPythonFunction("pp" + solver_ext, func)); - mFlipParticleVelocity = (vector<pVel> *)pyObjectToPointer( - callPythonFunction("pVel" + parts_ext, func)); - } - if (mUsingLiquid && mUsingMesh) { - mMeshNodes = (vector<Node> *)pyObjectToPointer( - callPythonFunction("mesh" + mesh_ext, funcNodes)); - mMeshTriangles = (vector<Triangle> *)pyObjectToPointer( - callPythonFunction("mesh" + mesh_ext, funcTris)); - } - if (mUsingLiquid && mUsingMVel) { - mMeshVelocities = (vector<pVel> *)pyObjectToPointer( - callPythonFunction("mVel" + mesh_ext2, func)); - } - if (mUsingLiquid && (mUsingDrops | mUsingBubbles | mUsingFloats | mUsingTracers)) { - mSndParticleData = (vector<pData> *)pyObjectToPointer( - callPythonFunction("ppSnd" + snd_ext, func)); - mSndParticleVelocity = (vector<pVel> *)pyObjectToPointer( - callPythonFunction("pVelSnd" + parts_ext, func)); - mSndParticleLife = (vector<float> *)pyObjectToPointer( - callPythonFunction("pLifeSnd" + parts_ext, func)); - } + string s_ext = "_s" + id; + string pp_ext = "_pp" + id; + string snd_ext = "_sp" + id; + string sm_ext = "_sm" + id; + string mesh_ext = "_mesh" + id; + string sn_ext = "_sn" + id; + + mFlags = (smoke || liquid) ? getPointer<int>("flags" + s_ext, func) : nullptr; + mPhiIn = (smoke || liquid) ? getPointer<float>("phiIn" + s_ext, func) : nullptr; + mPhiStaticIn = (smoke || liquid) ? getPointer<float>("phiSIn" + s_ext, func) : nullptr; + mVelocityX = (smoke || liquid) ? getPointer<float>("x_vel" + s_ext, func) : nullptr; + mVelocityY = (smoke || liquid) ? getPointer<float>("y_vel" + s_ext, func) : nullptr; + mVelocityZ = (smoke || liquid) ? getPointer<float>("z_vel" + s_ext, func) : nullptr; + mForceX = (smoke || liquid) ? getPointer<float>("x_force" + s_ext, func) : nullptr; + mForceY = (smoke || liquid) ? getPointer<float>("y_force" + s_ext, func) : nullptr; + mForceZ = (smoke || liquid) ? getPointer<float>("z_force" + s_ext, func) : nullptr; + + /* Outflow. */ + mPhiOutIn = (outflow) ? getPointer<float>("phiOutIn" + s_ext, func) : nullptr; + mPhiOutStaticIn = (outflow) ? getPointer<float>("phiOutSIn" + s_ext, func) : nullptr; + + /* Obstacles. */ + mPhiObsIn = (obstacle) ? getPointer<float>("phiObsIn" + s_ext, func) : nullptr; + mPhiObsStaticIn = (obstacle) ? getPointer<float>("phiObsSIn" + s_ext, func) : nullptr; + mObVelocityX = (obstacle) ? getPointer<float>("x_obvel" + s_ext, func) : nullptr; + mObVelocityY = (obstacle) ? getPointer<float>("y_obvel" + s_ext, func) : nullptr; + mObVelocityZ = (obstacle) ? getPointer<float>("z_obvel" + s_ext, func) : nullptr; + mNumObstacle = (obstacle) ? getPointer<float>("numObs" + s_ext, func) : nullptr; + + /* Guiding. */ + mPhiGuideIn = (guiding) ? getPointer<float>("phiGuideIn" + s_ext, func) : nullptr; + mGuideVelocityX = (guiding) ? getPointer<float>("x_guidevel" + s_ext, func) : nullptr; + mGuideVelocityY = (guiding) ? getPointer<float>("y_guidevel" + s_ext, func) : nullptr; + mGuideVelocityZ = (guiding) ? getPointer<float>("z_guidevel" + s_ext, func) : nullptr; + mNumGuide = (guiding) ? getPointer<float>("numGuides" + s_ext, func) : nullptr; + + /* Initial velocities. */ + mInVelocityX = (invel) ? getPointer<float>("x_invel" + s_ext, func) : nullptr; + mInVelocityY = (invel) ? getPointer<float>("y_invel" + s_ext, func) : nullptr; + mInVelocityZ = (invel) ? getPointer<float>("z_invel" + s_ext, func) : nullptr; + + /* Smoke. */ + mDensity = (smoke) ? getPointer<float>("density" + s_ext, func) : nullptr; + mDensityIn = (smoke) ? getPointer<float>("densityIn" + s_ext, func) : nullptr; + mShadow = (smoke) ? getPointer<float>("shadow" + s_ext, func) : nullptr; + mEmissionIn = (smoke) ? getPointer<float>("emissionIn" + s_ext, func) : nullptr; + + /* Heat. */ + mHeat = (heat) ? getPointer<float>("heat" + s_ext, func) : nullptr; + mHeatIn = (heat) ? getPointer<float>("heatIn" + s_ext, func) : nullptr; + + /* Fire. */ + mFlame = (fire) ? getPointer<float>("flame" + s_ext, func) : nullptr; + mFuel = (fire) ? getPointer<float>("fuel" + s_ext, func) : nullptr; + mReact = (fire) ? getPointer<float>("react" + s_ext, func) : nullptr; + mFuelIn = (fire) ? getPointer<float>("fuelIn" + s_ext, func) : nullptr; + mReactIn = (fire) ? getPointer<float>("reactIn" + s_ext, func) : nullptr; + + /* Colors. */ + mColorR = (colors) ? getPointer<float>("color_r" + s_ext, func) : nullptr; + mColorG = (colors) ? getPointer<float>("color_g" + s_ext, func) : nullptr; + mColorB = (colors) ? getPointer<float>("color_b" + s_ext, func) : nullptr; + mColorRIn = (colors) ? getPointer<float>("color_r_in" + s_ext, func) : nullptr; + mColorGIn = (colors) ? getPointer<float>("color_g_in" + s_ext, func) : nullptr; + mColorBIn = (colors) ? getPointer<float>("color_b_in" + s_ext, func) : nullptr; + + /* Noise. */ + mDensityHigh = (noise) ? getPointer<float>("density" + sn_ext, func) : nullptr; + mTextureU = (noise) ? getPointer<float>("texture_u" + s_ext, func) : nullptr; + mTextureV = (noise) ? getPointer<float>("texture_v" + s_ext, func) : nullptr; + mTextureW = (noise) ? getPointer<float>("texture_w" + s_ext, func) : nullptr; + mTextureU2 = (noise) ? getPointer<float>("texture_u2" + s_ext, func) : nullptr; + mTextureV2 = (noise) ? getPointer<float>("texture_v2" + s_ext, func) : nullptr; + mTextureW2 = (noise) ? getPointer<float>("texture_w2" + s_ext, func) : nullptr; + + /* Fire with noise. */ + mFlameHigh = (noise && fire) ? getPointer<float>("flame" + sn_ext, func) : nullptr; + mFuelHigh = (noise && fire) ? getPointer<float>("fuel" + sn_ext, func) : nullptr; + mReactHigh = (noise && fire) ? getPointer<float>("react" + sn_ext, func) : nullptr; + + /* Colors with noise. */ + mColorRHigh = (noise && colors) ? getPointer<float>("color_r" + sn_ext, func) : nullptr; + mColorGHigh = (noise && colors) ? getPointer<float>("color_g" + sn_ext, func) : nullptr; + mColorBHigh = (noise && colors) ? getPointer<float>("color_b" + sn_ext, func) : nullptr; + + /* Liquid. */ + mPhi = (liquid) ? getPointer<float>("phi" + s_ext, func) : nullptr; + mFlipParticleData = (liquid) ? getPointer<vector<pData>>("pp" + s_ext, func) : nullptr; + mFlipParticleVelocity = (liquid) ? getPointer<vector<pVel>>("pVel" + pp_ext, func) : nullptr; + + /* Mesh. */ + mMeshNodes = (mesh) ? getPointer<vector<Node>>("mesh" + sm_ext, funcNodes) : nullptr; + mMeshTriangles = (mesh) ? getPointer<vector<Triangle>>("mesh" + sm_ext, funcTris) : nullptr; + + /* Mesh velocities. */ + mMeshVelocities = (meshvel) ? getPointer<vector<pVel>>("mVel" + mesh_ext, func) : nullptr; + + /* Secondary particles. */ + mParticleData = (parts) ? getPointer<vector<pData>>("ppSnd" + snd_ext, func) : nullptr; + mParticleVelocity = (parts) ? getPointer<vector<pVel>>("pVelSnd" + pp_ext, func) : nullptr; + mParticleLife = (parts) ? getPointer<vector<float>>("pLifeSnd" + pp_ext, func) : nullptr; mFlipFromFile = false; mMeshFromFile = false; diff --git a/intern/mantaflow/intern/MANTA_main.h b/intern/mantaflow/intern/MANTA_main.h index dae2aea4e08..5fd94ca01bc 100644 --- a/intern/mantaflow/intern/MANTA_main.h +++ b/intern/mantaflow/intern/MANTA_main.h @@ -41,7 +41,7 @@ struct MANTA { MANTA(){}; virtual ~MANTA(); - // Mirroring Mantaflow structures for particle data (pVel also used for mesh vert vels) + /* Mirroring Mantaflow structures for particle data (pVel also used for mesh vert vels). */ typedef struct PData { float pos[3]; int flag; @@ -50,7 +50,7 @@ struct MANTA { float pos[3]; } pVel; - // Mirroring Mantaflow structures for meshes + /* Mirroring Mantaflow structures for meshes. */ typedef struct Node { int flags; float pos[3], normal[3]; @@ -60,36 +60,33 @@ struct MANTA { int flags; } Triangle; - // Manta step, handling everything - void step(struct FluidModifierData *fmd, int startFrame); - - // Grid initialization functions - void initHeat(struct FluidModifierData *fmd = NULL); - void initFire(struct FluidModifierData *fmd = NULL); - void initColors(struct FluidModifierData *fmd = NULL); - void initFireHigh(struct FluidModifierData *fmd = NULL); - void initColorsHigh(struct FluidModifierData *fmd = NULL); - void initLiquid(FluidModifierData *fmd = NULL); - void initLiquidMesh(FluidModifierData *fmd = NULL); - void initObstacle(FluidModifierData *fmd = NULL); - void initCurvature(FluidModifierData *fmd = NULL); - void initGuiding(FluidModifierData *fmd = NULL); - void initFractions(FluidModifierData *fmd = NULL); - void initInVelocity(FluidModifierData *fmd = NULL); - void initOutflow(FluidModifierData *fmd = NULL); - void initSndParts(FluidModifierData *fmd = NULL); - void initLiquidSndParts(FluidModifierData *fmd = NULL); - - // Pointer transfer: Mantaflow -> Blender - void updatePointers(); - - // Write cache + /* Grid initialization functions. */ + bool initHeat(struct FluidModifierData *fmd = nullptr); + bool initFire(struct FluidModifierData *fmd = nullptr); + bool initColors(struct FluidModifierData *fmd = nullptr); + bool initFireHigh(struct FluidModifierData *fmd = nullptr); + bool initColorsHigh(struct FluidModifierData *fmd = nullptr); + bool initLiquid(FluidModifierData *fmd = nullptr); + bool initLiquidMesh(FluidModifierData *fmd = nullptr); + bool initObstacle(FluidModifierData *fmd = nullptr); + bool initCurvature(FluidModifierData *fmd = nullptr); + bool initGuiding(FluidModifierData *fmd = nullptr); + bool initFractions(FluidModifierData *fmd = nullptr); + bool initInVelocity(FluidModifierData *fmd = nullptr); + bool initOutflow(FluidModifierData *fmd = nullptr); + bool initSndParts(FluidModifierData *fmd = nullptr); + bool initLiquidSndParts(FluidModifierData *fmd = nullptr); + + /* Pointer transfer: Mantaflow -> Blender. Use flush to reset all pointers to nullptr. */ + void updatePointers(FluidModifierData *fmd, bool flush = false); + + /* Write cache. */ bool writeConfiguration(FluidModifierData *fmd, int framenr); bool writeData(FluidModifierData *fmd, int framenr); bool writeNoise(FluidModifierData *fmd, int framenr); - // write calls for mesh and particles were left in bake calls for now + /* Write calls for mesh and particles were left in bake calls for now. */ - // Read cache (via Manta save/load) + /* Read cache (via Python). */ bool readConfiguration(FluidModifierData *fmd, int framenr); bool readData(FluidModifierData *fmd, int framenr, bool resumable); bool readNoise(FluidModifierData *fmd, int framenr, bool resumable); @@ -97,26 +94,21 @@ struct MANTA { bool readParticles(FluidModifierData *fmd, int framenr, bool resumable); bool readGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain); - // Read cache (via file read functions in MANTA - e.g. read .bobj.gz meshes, .uni particles) - bool updateMeshStructures(FluidModifierData *fmd, int framenr); - bool updateFlipStructures(FluidModifierData *fmd, int framenr); - bool updateParticleStructures(FluidModifierData *fmd, int framenr); - bool updateSmokeStructures(FluidModifierData *fmd, int framenr); - bool updateNoiseStructures(FluidModifierData *fmd, int framenr); + /* Propagate variable changes from RNA to Python. */ bool updateVariables(FluidModifierData *fmd); - // Bake cache + /* Bake cache. */ bool bakeData(FluidModifierData *fmd, int framenr); bool bakeNoise(FluidModifierData *fmd, int framenr); bool bakeMesh(FluidModifierData *fmd, int framenr); bool bakeParticles(FluidModifierData *fmd, int framenr); bool bakeGuiding(FluidModifierData *fmd, int framenr); - // IO for Mantaflow scene script + /* IO for Mantaflow scene script. */ void exportSmokeScript(struct FluidModifierData *fmd); void exportLiquidScript(struct FluidModifierData *fmd); - // Check cache status by frame + /* Check cache status by frame. */ bool hasConfig(FluidModifierData *fmd, int framenr); bool hasData(FluidModifierData *fmd, int framenr); bool hasNoise(FluidModifierData *fmd, int framenr); @@ -193,7 +185,7 @@ struct MANTA { return mUpresParticle; } - // Smoke getters + /* Smoke getters. */ inline float *getDensity() { return mDensity; @@ -422,9 +414,9 @@ struct MANTA { } static atomic<int> solverID; - static int with_debug; // on or off (1 or 0), also sets manta debug level + static int with_debug; /* On or off (1 or 0), also sets manta debug level. */ - // Mesh getters + /* Mesh getters. */ inline int getNumVertices() { return (mMeshNodes && !mMeshNodes->empty()) ? mMeshNodes->size() : 0; @@ -563,9 +555,9 @@ struct MANTA { inline int getSndParticleFlagAt(int i) { assert(i >= 0); - if (mSndParticleData && !mSndParticleData->empty()) { - assert(i < mSndParticleData->size()); - return (*mSndParticleData)[i].flag; + if (mParticleData && !mParticleData->empty()) { + assert(i < mParticleData->size()); + return (*mParticleData)[i].flag; } return 0; } @@ -601,27 +593,27 @@ struct MANTA { inline float getSndParticlePositionXAt(int i) { assert(i >= 0); - if (mSndParticleData && !mSndParticleData->empty()) { - assert(i < mSndParticleData->size()); - return (*mSndParticleData)[i].pos[0]; + if (mParticleData && !mParticleData->empty()) { + assert(i < mParticleData->size()); + return (*mParticleData)[i].pos[0]; } return 0.0f; } inline float getSndParticlePositionYAt(int i) { assert(i >= 0); - if (mSndParticleData && !mSndParticleData->empty()) { - assert(i < mSndParticleData->size()); - return (*mSndParticleData)[i].pos[1]; + if (mParticleData && !mParticleData->empty()) { + assert(i < mParticleData->size()); + return (*mParticleData)[i].pos[1]; } return 0.0f; } inline float getSndParticlePositionZAt(int i) { assert(i >= 0); - if (mSndParticleData && !mSndParticleData->empty()) { - assert(i < mSndParticleData->size()); - return (*mSndParticleData)[i].pos[2]; + if (mParticleData && !mParticleData->empty()) { + assert(i < mParticleData->size()); + return (*mParticleData)[i].pos[2]; } return 0.0f; } @@ -657,27 +649,27 @@ struct MANTA { inline float getSndParticleVelocityXAt(int i) { assert(i >= 0); - if (mSndParticleVelocity && !mSndParticleVelocity->empty()) { - assert(i < mSndParticleVelocity->size()); - return (*mSndParticleVelocity)[i].pos[0]; + if (mParticleVelocity && !mParticleVelocity->empty()) { + assert(i < mParticleVelocity->size()); + return (*mParticleVelocity)[i].pos[0]; } return 0.0f; } inline float getSndParticleVelocityYAt(int i) { assert(i >= 0); - if (mSndParticleVelocity && !mSndParticleVelocity->empty()) { - assert(i < mSndParticleVelocity->size()); - return (*mSndParticleVelocity)[i].pos[1]; + if (mParticleVelocity && !mParticleVelocity->empty()) { + assert(i < mParticleVelocity->size()); + return (*mParticleVelocity)[i].pos[1]; } return 0.0f; } inline float getSndParticleVelocityZAt(int i) { assert(i >= 0); - if (mSndParticleVelocity && !mSndParticleVelocity->empty()) { - assert(i < mSndParticleVelocity->size()); - return (*mSndParticleVelocity)[i].pos[2]; + if (mParticleVelocity && !mParticleVelocity->empty()) { + assert(i < mParticleVelocity->size()); + return (*mParticleVelocity)[i].pos[2]; } return 0.0f; } @@ -686,30 +678,28 @@ struct MANTA { { return (mFlipParticleData && !mFlipParticleData->empty()) ? (float *)&mFlipParticleData->front() : - NULL; + nullptr; } inline float *getSndParticleData() { - return (mSndParticleData && !mSndParticleData->empty()) ? (float *)&mSndParticleData->front() : - NULL; + return (mParticleData && !mParticleData->empty()) ? (float *)&mParticleData->front() : nullptr; } inline float *getFlipParticleVelocity() { return (mFlipParticleVelocity && !mFlipParticleVelocity->empty()) ? (float *)&mFlipParticleVelocity->front() : - NULL; + nullptr; } inline float *getSndParticleVelocity() { - return (mSndParticleVelocity && !mSndParticleVelocity->empty()) ? - (float *)&mSndParticleVelocity->front() : - NULL; + return (mParticleVelocity && !mParticleVelocity->empty()) ? + (float *)&mParticleVelocity->front() : + nullptr; } inline float *getSndParticleLife() { - return (mSndParticleLife && !mSndParticleLife->empty()) ? (float *)&mSndParticleLife->front() : - NULL; + return (mParticleLife && !mParticleLife->empty()) ? (float *)&mParticleLife->front() : nullptr; } inline int getNumFlipParticles() @@ -718,7 +708,7 @@ struct MANTA { } inline int getNumSndParticles() { - return (mSndParticleData && !mSndParticleData->empty()) ? mSndParticleData->size() : 0; + return (mParticleData && !mParticleData->empty()) ? mParticleData->size() : 0; } inline bool usingFlipFromFile() @@ -734,7 +724,7 @@ struct MANTA { return mParticlesFromFile; } - // Direct access to solver time attributes + /* Direct access to solver time attributes. */ int getFrame(); float getTimestep(); void adaptTimestep(); @@ -742,7 +732,7 @@ struct MANTA { bool needsRealloc(FluidModifierData *fmd); private: - // simulation constants + /* Simulation constants. */ size_t mTotalCells; size_t mTotalCellsHigh; size_t mTotalCellsMesh; @@ -750,6 +740,7 @@ struct MANTA { unordered_map<string, string> mRNAMap; + /* The ID of the solver objects will be incremented for every new object. */ int mCurrentID; bool mUsingHeat; @@ -796,10 +787,7 @@ struct MANTA { int mUpresMesh; int mUpresParticle; - float mTempAmb; /* ambient temperature */ - float mConstantScaling; - - // Fluid grids + /* Fluid grids. */ float *mVelocityX; float *mVelocityY; float *mVelocityZ; @@ -819,7 +807,7 @@ struct MANTA { float *mNumObstacle; float *mNumGuide; - // Smoke grids + /* Smoke grids. */ float *mDensity; float *mHeat; float *mFlame; @@ -851,7 +839,7 @@ struct MANTA { float *mTextureV2; float *mTextureW2; - // Liquid grids + /* Liquid grids. */ float *mPhiIn; float *mPhiStaticIn; float *mPhiObsIn; @@ -861,31 +849,31 @@ struct MANTA { float *mPhiOutStaticIn; float *mPhi; - // Mesh fields + /* Mesh fields. */ vector<Node> *mMeshNodes; vector<Triangle> *mMeshTriangles; vector<pVel> *mMeshVelocities; - // Particle fields + /* Particle fields. */ vector<pData> *mFlipParticleData; vector<pVel> *mFlipParticleVelocity; - vector<pData> *mSndParticleData; - vector<pVel> *mSndParticleVelocity; - vector<float> *mSndParticleLife; + vector<pData> *mParticleData; + vector<pVel> *mParticleVelocity; + vector<float> *mParticleLife; - void initializeRNAMap(struct FluidModifierData *fmd = NULL); - void initDomain(struct FluidModifierData *fmd = NULL); - void initNoise(struct FluidModifierData *fmd = NULL); - void initMesh(struct FluidModifierData *fmd = NULL); - void initSmoke(struct FluidModifierData *fmd = NULL); - void initSmokeNoise(struct FluidModifierData *fmd = NULL); + void initializeRNAMap(struct FluidModifierData *doRnaRefresh = nullptr); + bool initDomain(struct FluidModifierData *doRnaRefresh = nullptr); + bool initNoise(struct FluidModifierData *doRnaRefresh = nullptr); + bool initMesh(struct FluidModifierData *doRnaRefresh = nullptr); + bool initSmoke(struct FluidModifierData *doRnaRefresh = nullptr); + bool initSmokeNoise(struct FluidModifierData *doRnaRefresh = nullptr); void initializeMantaflow(); void terminateMantaflow(); bool runPythonString(vector<string> commands); string getRealValue(const string &varName); string parseLine(const string &line); - string parseScript(const string &setup_string, FluidModifierData *fmd = NULL); + string parseScript(const string &setup_string, FluidModifierData *fmd = nullptr); string getDirectory(struct FluidModifierData *fmd, string subdirectory); string getFile(struct FluidModifierData *fmd, string subdirectory, diff --git a/intern/mantaflow/intern/manta_fluid_API.cpp b/intern/mantaflow/intern/manta_fluid_API.cpp index 60546bc1183..530dbd49b7c 100644 --- a/intern/mantaflow/intern/manta_fluid_API.cpp +++ b/intern/mantaflow/intern/manta_fluid_API.cpp @@ -37,33 +37,29 @@ void manta_free(MANTA *fluid) fluid = nullptr; } -void manta_ensure_obstacle(MANTA *fluid, struct FluidModifierData *fmd) +int manta_ensure_obstacle(MANTA *fluid, struct FluidModifierData *fmd) { - if (!fluid) - return; - fluid->initObstacle(fmd); - fluid->updatePointers(); + if (!fluid || !fmd) + return 0; + return fluid->initObstacle(fmd); } -void manta_ensure_guiding(MANTA *fluid, struct FluidModifierData *fmd) +int manta_ensure_guiding(MANTA *fluid, struct FluidModifierData *fmd) { - if (!fluid) - return; - fluid->initGuiding(fmd); - fluid->updatePointers(); + if (!fluid || !fmd) + return 0; + return fluid->initGuiding(fmd); } -void manta_ensure_invelocity(MANTA *fluid, struct FluidModifierData *fmd) +int manta_ensure_invelocity(MANTA *fluid, struct FluidModifierData *fmd) { - if (!fluid) - return; - fluid->initInVelocity(fmd); - fluid->updatePointers(); + if (!fluid || !fmd) + return 0; + return fluid->initInVelocity(fmd); } -void manta_ensure_outflow(MANTA *fluid, struct FluidModifierData *fmd) +int manta_ensure_outflow(MANTA *fluid, struct FluidModifierData *fmd) { - if (!fluid) - return; - fluid->initOutflow(fmd); - fluid->updatePointers(); + if (!fluid || !fmd) + return 0; + return fluid->initOutflow(fmd); } int manta_write_config(MANTA *fluid, FluidModifierData *fmd, int framenr) @@ -229,11 +225,18 @@ void manta_adapt_timestep(MANTA *fluid) bool manta_needs_realloc(MANTA *fluid, FluidModifierData *fmd) { - if (!fluid) + if (!fluid || !fmd) return false; return fluid->needsRealloc(fmd); } +void manta_update_pointers(struct MANTA *fluid, struct FluidModifierData *fmd, bool flush) +{ + if (!fluid || !fmd) + return; + fluid->updatePointers(fmd, flush); +} + /* Fluid accessors */ size_t manta_get_index(int x, int max_x, int y, int max_y, int z /*, int max_z */) { @@ -441,34 +444,35 @@ void manta_noise_get_rgba_fixed_color(MANTA *smoke, float color[3], float *data, get_rgba_fixed_color(color, smoke->getTotalCellsHigh(), data, sequential); } -void manta_smoke_ensure_heat(MANTA *smoke, struct FluidModifierData *fmd) +int manta_smoke_ensure_heat(MANTA *smoke, struct FluidModifierData *fmd) { - if (smoke) { - smoke->initHeat(fmd); - smoke->updatePointers(); - } + if (!smoke || !fmd) + return 0; + return smoke->initHeat(fmd); } -void manta_smoke_ensure_fire(MANTA *smoke, struct FluidModifierData *fmd) +int manta_smoke_ensure_fire(MANTA *smoke, struct FluidModifierData *fmd) { - if (smoke) { - smoke->initFire(fmd); - if (smoke->usingNoise()) { - smoke->initFireHigh(fmd); - } - smoke->updatePointers(); + if (!smoke || !fmd) + return 0; + + int result = smoke->initFire(fmd); + if (smoke->usingNoise()) { + result &= smoke->initFireHigh(fmd); } + return result; } -void manta_smoke_ensure_colors(MANTA *smoke, struct FluidModifierData *fmd) +int manta_smoke_ensure_colors(MANTA *smoke, struct FluidModifierData *fmd) { - if (smoke) { - smoke->initColors(fmd); - if (smoke->usingNoise()) { - smoke->initColorsHigh(fmd); - } - smoke->updatePointers(); + if (!smoke || !fmd) + return 0; + + int result = smoke->initColors(fmd); + if (smoke->usingNoise()) { + result &= smoke->initColorsHigh(fmd); } + return result; } /* Smoke accessors */ @@ -645,12 +649,11 @@ void manta_liquid_export_script(MANTA *liquid, FluidModifierData *fmd) liquid->exportLiquidScript(fmd); } -void manta_liquid_ensure_sndparts(MANTA *liquid, struct FluidModifierData *fmd) +int manta_liquid_ensure_sndparts(MANTA *liquid, struct FluidModifierData *fmd) { - if (liquid) { - liquid->initLiquidSndParts(fmd); - liquid->updatePointers(); - } + if (!liquid || !fmd) + return 0; + return liquid->initLiquidSndParts(fmd); } /* Liquid accessors */ diff --git a/intern/mantaflow/intern/strings/fluid_script.h b/intern/mantaflow/intern/strings/fluid_script.h index 0045d839be4..a01e333ab5e 100644 --- a/intern/mantaflow/intern/strings/fluid_script.h +++ b/intern/mantaflow/intern/strings/fluid_script.h @@ -418,19 +418,6 @@ const std::string fluid_post_step = "\n\ def fluid_post_step_$ID$():\n\ mantaMsg('Fluid post step')\n\ - forces_s$ID$.clear()\n\ - x_force_s$ID$.clear()\n\ - y_force_s$ID$.clear()\n\ - z_force_s$ID$.clear()\n\ - \n\ - if using_guiding_s$ID$:\n\ - weightGuide_s$ID$.clear()\n\ - if using_invel_s$ID$:\n\ - x_invel_s$ID$.clear()\n\ - y_invel_s$ID$.clear()\n\ - z_invel_s$ID$.clear()\n\ - invel_s$ID$.clear()\n\ - invelC_s$ID$.clear()\n\ \n\ # Copy vel grid to reals grids (which Blender internal will in turn use for vel access)\n\ copyVec3ToReal(source=vel_s$ID$, targetX=x_vel_s$ID$, targetY=y_vel_s$ID$, targetZ=z_vel_s$ID$)\n"; diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc index 43416f734c5..f766f57732a 100644 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ b/intern/opencolorio/ocio_impl_glsl.cc @@ -45,9 +45,8 @@ # pragma warning(pop) #endif -extern "C" { #include "GPU_immediate.h" -} +#include "GPU_shader.h" using namespace OCIO_NAMESPACE; @@ -94,18 +93,15 @@ struct OCIO_GLSLCurveMappingParameters { struct OCIO_GLSLShader { /** Cache IDs */ std::string cacheId; - /** TODO(fclem): Remove. IMM shader interface. */ - struct GPUShaderInterface *interface; - /** OpenGL Shader objects handles. */ - GLuint frag; - GLuint vert; - GLuint program; + + struct GPUShader *shader; /** Uniform locations. */ GLint dither_loc; GLint overlay_loc; GLint overlay_tex_loc; GLint predivide_loc; GLint curve_mapping_loc; + GLint ubo_bind; /** Error checking. */ bool valid; }; @@ -152,56 +148,6 @@ static OCIO_GLSLDrawState *allocateOpenGLState(void) /** \name Shader * \{ */ -static GLuint compileShaderText(GLenum shader_type, const char *text) -{ - GLuint shader; - GLint stat; - - shader = glCreateShader(shader_type); - glShaderSource(shader, 1, (const GLchar **)&text, NULL); - glCompileShader(shader); - glGetShaderiv(shader, GL_COMPILE_STATUS, &stat); - - if (!stat) { - GLchar log[1000]; - GLsizei len; - glGetShaderInfoLog(shader, 1000, &len, log); - fprintf(stderr, "Shader compile error:\n%s\n", log); - return 0; - } - - return shader; -} - -static GLuint linkShaders(GLuint frag, GLuint vert) -{ - if (!frag || !vert) { - return 0; - } - - GLuint program = glCreateProgram(); - - glAttachShader(program, frag); - glAttachShader(program, vert); - - glLinkProgram(program); - - /* check link */ - { - GLint stat; - glGetProgramiv(program, GL_LINK_STATUS, &stat); - if (!stat) { - GLchar log[1000]; - GLsizei len; - glGetProgramInfoLog(program, 1000, &len, log); - fprintf(stderr, "Shader link error:\n%s\n", log); - return 0; - } - } - - return program; -} - static void updateGLSLShader(OCIO_GLSLShader *shader, ConstProcessorRcPtr *processor_scene_to_ui, ConstProcessorRcPtr *processpr_ui_to_display, @@ -213,28 +159,14 @@ static void updateGLSLShader(OCIO_GLSLShader *shader, } /* Delete any previous shader. */ - glDeleteProgram(shader->program); - glDeleteShader(shader->frag); - glDeleteShader(shader->vert); - - if (shader->interface) { - GPU_shaderinterface_discard(shader->interface); + if (shader->shader) { + GPU_shader_free(shader->shader); } - { - /* Vertex shader */ - std::ostringstream osv; - - osv << "#version 330\n"; - osv << datatoc_gpu_shader_display_transform_vertex_glsl; - - shader->vert = compileShaderText(GL_VERTEX_SHADER, osv.str().c_str()); - } + std::ostringstream os; { /* Fragment shader */ - std::ostringstream os; - os << "#version 330\n"; /* Work around OpenColorIO not supporting latest GLSL yet. */ os << "#define texture2D texture\n"; os << "#define texture3D texture\n"; @@ -246,41 +178,36 @@ static void updateGLSLShader(OCIO_GLSLShader *shader, os << (*processpr_ui_to_display)->getGpuShaderText(*shader_desc) << "\n"; os << datatoc_gpu_shader_display_transform_glsl; - - shader->frag = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str()); } - /* shader_Program */ - if (shader->frag && shader->vert) { - shader->program = linkShaders(shader->frag, shader->vert); - } + shader->shader = GPU_shader_create(datatoc_gpu_shader_display_transform_vertex_glsl, + os.str().c_str(), + NULL, + NULL, + NULL, + __func__); - if (shader->program) { - shader->dither_loc = glGetUniformLocation(shader->program, "dither"); - shader->overlay_tex_loc = glGetUniformLocation(shader->program, "overlay_texture"); - shader->overlay_loc = glGetUniformLocation(shader->program, "overlay"); - shader->predivide_loc = glGetUniformLocation(shader->program, "predivide"); - shader->curve_mapping_loc = glGetUniformLocation(shader->program, "curve_mapping"); + if (shader->shader) { + shader->dither_loc = GPU_shader_get_uniform(shader->shader, "dither"); + shader->overlay_tex_loc = GPU_shader_get_uniform(shader->shader, "overlay_texture"); + shader->overlay_loc = GPU_shader_get_uniform(shader->shader, "overlay"); + shader->predivide_loc = GPU_shader_get_uniform(shader->shader, "predivide"); + shader->curve_mapping_loc = GPU_shader_get_uniform(shader->shader, "curve_mapping"); + shader->ubo_bind = GPU_shader_get_uniform_block_binding(shader->shader, + "OCIO_GLSLCurveMappingParameters"); - glUseProgram(shader->program); - - /* TODO(fclem) Remove this. Make caller always assume viewport space and - * specify texco via vertex attribs. */ - shader->interface = GPU_shaderinterface_create(shader->program); - - /* Set UBO binding location. */ - GLuint index = glGetUniformBlockIndex(shader->program, "OCIO_GLSLCurveMappingParameters"); - glUniformBlockBinding(shader->program, index, UBO_BIND_LOC); + GPU_shader_bind(shader->shader); /* Set texture bind point uniform once. This is saved by the shader. */ - glUniform1i(glGetUniformLocation(shader->program, "image_texture"), 0); - glUniform1i(glGetUniformLocation(shader->program, "lut3d_texture"), 2); - glUniform1i(glGetUniformLocation(shader->program, "lut3d_display_texture"), 3); - glUniform1i(glGetUniformLocation(shader->program, "curve_mapping_texture"), 4); + GPUShader *sh = shader->shader; + GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "image_texture"), 0); + GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "lut3d_texture"), 2); + GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "lut3d_display_texture"), 3); + GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "curve_mapping_texture"), 4); } shader->cacheId = cache_id; - shader->valid = (shader->program != 0); + shader->valid = (shader->shader != NULL); } static void ensureGLSLShader(OCIO_GLSLShader **shader_ptr, @@ -302,12 +229,8 @@ static void ensureGLSLShader(OCIO_GLSLShader **shader_ptr, static void freeGLSLShader(OCIO_GLSLShader *shader) { - glDeleteProgram(shader->program); - glDeleteShader(shader->frag); - glDeleteShader(shader->vert); - - if (shader->interface) { - GPU_shaderinterface_discard(shader->interface); + if (shader->shader) { + GPU_shader_free(shader->shader); } OBJECT_GUARDED_DELETE(shader, OCIO_GLSLShader); @@ -674,10 +597,10 @@ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, glActiveTexture(GL_TEXTURE0); /* Bind UBO. */ - glBindBufferBase(GL_UNIFORM_BUFFER, 0, shader_curvemap->buffer); + glBindBufferBase(GL_UNIFORM_BUFFER, shader->ubo_bind, shader_curvemap->buffer); /* TODO(fclem) remove remains of IMM. */ - immBindProgram(shader->program, shader->interface); + immBindShader(shader->shader); /* Bind Shader and set uniforms. */ // glUseProgram(shader->program); diff --git a/intern/rigidbody/RBI_api.h b/intern/rigidbody/RBI_api.h index d46cb5a7eed..07cda49e04b 100644 --- a/intern/rigidbody/RBI_api.h +++ b/intern/rigidbody/RBI_api.h @@ -238,6 +238,14 @@ rbCollisionShape *RB_shape_new_trimesh(rbMeshData *mesh); /* 2b - GImpact Meshes */ rbCollisionShape *RB_shape_new_gimpact_mesh(rbMeshData *mesh); +/* Compound Shape ---------------- */ + +rbCollisionShape *RB_shape_new_compound(void); +void RB_compound_add_child_shape(rbCollisionShape *collisionShape, + rbCollisionShape *shape, + const float loc[3], + const float rot[4]); + /* Cleanup --------------------------- */ void RB_shape_delete(rbCollisionShape *shape); diff --git a/intern/rigidbody/rb_bullet_api.cpp b/intern/rigidbody/rb_bullet_api.cpp index a8bf1420523..db8c062990c 100644 --- a/intern/rigidbody/rb_bullet_api.cpp +++ b/intern/rigidbody/rb_bullet_api.cpp @@ -98,6 +98,8 @@ struct rbMeshData { struct rbCollisionShape { btCollisionShape *cshape; rbMeshData *mesh; + rbCollisionShape **compoundChildShapes; + int compoundChilds; }; struct rbFilterCallback : public btOverlapFilterCallback { @@ -331,6 +333,7 @@ rbRigidBody *RB_body_new(rbCollisionShape *shape, const float loc[3], const floa rbRigidBody *object = new rbRigidBody; /* current transform */ btTransform trans; + trans.setIdentity(); trans.setOrigin(btVector3(loc[0], loc[1], loc[2])); trans.setRotation(btQuaternion(rot[1], rot[2], rot[3], rot[0])); @@ -413,6 +416,10 @@ void RB_body_set_mass(rbRigidBody *object, float value) shape->calculateLocalInertia(value, localInertia); } + btVector3 minAabb, maxAabb; + btTransform ident; + ident.setIdentity(); + body->getCollisionShape()->getAabb(ident, minAabb, maxAabb); body->setMassProps(value, localInertia); body->updateInertiaTensor(); } @@ -597,6 +604,7 @@ void RB_body_set_loc_rot(rbRigidBody *object, const float loc[3], const float ro /* set transform matrix */ btTransform trans; + trans.setIdentity(); trans.setOrigin(btVector3(loc[0], loc[1], loc[2])); trans.setRotation(btQuaternion(rot[1], rot[2], rot[3], rot[0])); @@ -655,6 +663,8 @@ rbCollisionShape *RB_shape_new_box(float x, float y, float z) rbCollisionShape *shape = new rbCollisionShape; shape->cshape = new btBoxShape(btVector3(x, y, z)); shape->mesh = NULL; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; return shape; } @@ -663,6 +673,8 @@ rbCollisionShape *RB_shape_new_sphere(float radius) rbCollisionShape *shape = new rbCollisionShape; shape->cshape = new btSphereShape(radius); shape->mesh = NULL; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; return shape; } @@ -671,6 +683,8 @@ rbCollisionShape *RB_shape_new_capsule(float radius, float height) rbCollisionShape *shape = new rbCollisionShape; shape->cshape = new btCapsuleShapeZ(radius, height); shape->mesh = NULL; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; return shape; } @@ -679,6 +693,8 @@ rbCollisionShape *RB_shape_new_cone(float radius, float height) rbCollisionShape *shape = new rbCollisionShape; shape->cshape = new btConeShapeZ(radius, height); shape->mesh = NULL; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; return shape; } @@ -687,6 +703,8 @@ rbCollisionShape *RB_shape_new_cylinder(float radius, float height) rbCollisionShape *shape = new rbCollisionShape; shape->cshape = new btCylinderShapeZ(btVector3(radius, radius, height)); shape->mesh = NULL; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; return shape; } @@ -709,6 +727,8 @@ rbCollisionShape *RB_shape_new_convex_hull( shape->cshape = hull_shape; shape->mesh = NULL; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; return shape; } @@ -773,6 +793,8 @@ rbCollisionShape *RB_shape_new_trimesh(rbMeshData *mesh) shape->cshape = new btScaledBvhTriangleMeshShape(unscaledShape, btVector3(1.0f, 1.0f, 1.0f)); shape->mesh = mesh; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; return shape; } @@ -813,9 +835,46 @@ rbCollisionShape *RB_shape_new_gimpact_mesh(rbMeshData *mesh) shape->cshape = gimpactShape; shape->mesh = mesh; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; return shape; } +/* Compound Shape ---------------- */ + +rbCollisionShape *RB_shape_new_compound() +{ + rbCollisionShape *shape = new rbCollisionShape; + btCompoundShape *compoundShape = new btCompoundShape(); + + shape->cshape = compoundShape; + shape->mesh = NULL; + shape->compoundChilds = 0; + shape->compoundChildShapes = NULL; + return shape; +} + +void RB_compound_add_child_shape(rbCollisionShape *parentShape, + rbCollisionShape *shape, + const float loc[3], + const float rot[4]) +{ + /* set transform matrix */ + btTransform trans; + trans.setIdentity(); + trans.setOrigin(btVector3(loc[0], loc[1], loc[2])); + trans.setRotation(btQuaternion(rot[1], rot[2], rot[3], rot[0])); + + btCompoundShape *compoundShape = (btCompoundShape *)(parentShape->cshape); + compoundShape->addChildShape(trans, shape->cshape); + + /* Store shapes for deletion later */ + parentShape->compoundChildShapes = (rbCollisionShape **)(realloc( + parentShape->compoundChildShapes, + sizeof(rbCollisionShape *) * (++parentShape->compoundChilds))); + parentShape->compoundChildShapes[parentShape->compoundChilds - 1] = shape; +} + /* Cleanup --------------------------- */ void RB_shape_delete(rbCollisionShape *shape) @@ -829,6 +888,15 @@ void RB_shape_delete(rbCollisionShape *shape) if (shape->mesh) RB_trimesh_data_delete(shape->mesh); delete shape->cshape; + + /* Delete compound child shapes if there are any */ + for (int i = 0; i < shape->compoundChilds; i++) { + RB_shape_delete(shape->compoundChildShapes[i]); + } + if (shape->compoundChildShapes != NULL) { + free(shape->compoundChildShapes); + } + delete shape; } @@ -873,6 +941,7 @@ static void make_constraint_transforms(btTransform &transform1, float orn[4]) { btTransform pivot_transform = btTransform(); + pivot_transform.setIdentity(); pivot_transform.setOrigin(btVector3(pivot[0], pivot[1], pivot[2])); pivot_transform.setRotation(btQuaternion(orn[1], orn[2], orn[3], orn[0])); diff --git a/intern/sky/include/sky_model.h b/intern/sky/include/sky_model.h index 021086e1e02..983b90fed35 100644 --- a/intern/sky/include/sky_model.h +++ b/intern/sky/include/sky_model.h @@ -112,7 +112,7 @@ bands, you would need something like ArHosekSkyModelState * skymodel_state[num_channels]; -You then have to allocate and initialise these states. In the following code +You then have to allocate and initialize these states. In the following code snippet, we assume that 'albedo' is defined as double albedo[num_channels]; @@ -228,7 +228,7 @@ actually not altered at all in this release. All we did was to add some support functionality for doing this more easily with the existing data and functions, and to add some explanations. -Just use 'arhosekskymodelstate_alienworld_alloc_init()' to initialise the sky +Just use 'arhosekskymodelstate_alienworld_alloc_init()' to initialize the sky model states (you will have to provide values for star temperature and solar intensity compared to the terrestrial sun), and do everything else as you did before. @@ -351,7 +351,7 @@ typedef struct SKY_ArHosekSkyModelState { arhosekskymodelstate_alloc_init() function ------------------------------------------ - Initialises an ArHosekSkyModelState struct for a terrestrial setting. + Initializes an #ArHosekSkyModelState struct for a terrestrial setting. ---------------------------------------------------------------------------- */ @@ -364,7 +364,7 @@ SKY_ArHosekSkyModelState *SKY_arhosekskymodelstate_alloc_init(const double solar arhosekskymodelstate_alienworld_alloc_init() function ----------------------------------------------------- - Initialises an ArHosekSkyModelState struct for an "alien world" setting + Initializes an ArHosekSkyModelState struct for an "alien world" setting with a sun of a surface temperature given in 'kelvin'. The parameter 'solar_intensity' controls the overall brightness of the sky, relative to the solar irradiance on Earth. A value of 1.0 yields a sky dome that diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 2b3c19f5f61fc72dba56a7edfdc4e55e2327dc1 +Subproject a7bbfac76c00edd0fb79a4766b7ac7c5dcbcac5 diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index fbdb226ab6d..df1be29f642 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -109,7 +109,7 @@ const UserDef U_default = { .keyconfigstr = "blender", .undosteps = 32, .undomemory = 0, - .gp_manhattendist = 1, + .gp_manhattandist = 1, .gp_euclideandist = 2, .gp_eraser = 25, .gp_settings = 0, diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 49c39f59fbc464dd34388990123f271c39eacbf +Subproject 82ed41ec632483fa9260d90dae7afdf3192c509 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject a52733b58d95ce60ecde95a9eca242e7319c285 +Subproject f2f4a8b3bfa36ee49f7bdb3a1acb40ef4b39ee3 diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 6ce3ab6becb..efa4b9b00a0 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6287,10 +6287,10 @@ def km_3d_view_tool_sculpt_box_mask(params): "3D View Tool: Sculpt, Box Mask", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("view3d.select_box", {"type": params.tool_tweak, "value": 'ANY'}, - {"properties": [("mode", 'ADD')]}), - ("view3d.select_box", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True}, - {"properties": [("mode", 'SUB')]}), + ("paint.mask_box_gesture", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("value", 1.0)]}), + ("paint.mask_box_gesture", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True}, + {"properties": [("value", 0.0)]}), ]}, ) diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index c39a7afcff9..c927cc184a3 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -46,7 +46,6 @@ _modules = [ "userpref", "uvcalc_follow_active", "uvcalc_lightmap", - "uvcalc_smart_project", "vertexpaint_dirt", "view3d", "gpencil_mesh_bake", diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index 311631ac65f..d0344b88be8 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -561,9 +561,61 @@ class QuickLiquid(Operator): return {'FINISHED'} +class QuickParticles(Operator): + """Use active object as particle emitter""" + bl_idname = "object.quick_particles" + bl_label = "Quick Particles" + + @classmethod + def poll(cls, context): + if not context.preferences.experimental.use_new_particle_system: + return False + if context.mode != 'OBJECT': + return False + if context.active_object is None: + return False + if context.active_object.type != 'MESH': + return False + return True + + def execute(self, context): + pointcloud = bpy.data.pointclouds.new("Particles") + pointcloud_object = bpy.data.objects.new("Particles", pointcloud) + modifier = pointcloud_object.modifiers.new("Simulation", 'SIMULATION') + simulation = bpy.data.simulations.new("Particle Simulation") + tree = simulation.node_tree + + default_name = "Particles" + particle_simulation_node = tree.nodes.new('SimulationNodeParticleSimulation') + particle_simulation_node.name = default_name + emitter_node = tree.nodes.new('SimulationNodeParticleMeshEmitter') + emitter_node.location.x -= 200 + emitter_node.location.y += 50 + emitter_node.inputs["Object"].default_value = context.active_object + force_node = tree.nodes.new('SimulationNodeForce') + force_node.location.x -= 200 + force_node.location.y -= 100 + force_node.inputs["Force"].default_value = (0, 0, -1) + + tree.links.new(particle_simulation_node.inputs["Emitters"], emitter_node.outputs["Emitter"]) + tree.links.new(particle_simulation_node.inputs["Forces"], force_node.outputs["Force"]) + + modifier.simulation = simulation + modifier.data_path = default_name + + for obj in context.selected_objects: + obj.select_set(False) + + context.collection.objects.link(pointcloud_object) + pointcloud_object.select_set(True) + context.view_layer.objects.active = pointcloud_object + pointcloud_object.show_bounds = True + return {'FINISHED'} + classes = ( QuickExplode, QuickFur, QuickSmoke, QuickLiquid, + QuickParticles, ) diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 2e14df1920f..e92f493960a 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -119,8 +119,11 @@ class PREFERENCES_OT_copy_prev(Operator): # Find config folder from previous version. import os version = bpy.app.version + version_new = ((version[0] * 100) + version[1]) version_old = ((version[0] * 100) + version[1]) - 1 - while version_old % 10 > 0: + # Ensure we only try to copy files from a point release. + # The check below ensures the second numbers match. + while (version_new % 100) // 10 == (version_old % 100) // 10: version_split = version_old // 100, version_old % 100 if os.path.isdir(cls._old_version_path(version_split)): return version_split diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 2befb7c73e2..7af5cd5ee5f 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -613,7 +613,7 @@ class LightMapPack(Operator): # Image & UVs... PREF_PACK_IN_ONE: BoolProperty( - name="Share Tex Space", + name="Share Texture Space", description=( "Objects Share texture space, map all objects " "into 1 uvmap" diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py deleted file mode 100644 index 1f56cbe6d57..00000000000 --- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py +++ /dev/null @@ -1,1053 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# TODO <pep8 compliant> - -from mathutils import ( - Matrix, - Vector, - geometry, -) -import bpy -from bpy.types import Operator - -DEG_TO_RAD = 0.017453292519943295 # pi/180.0 -# see bugs: -# - T31598 (when too small). -# - T48086 (when too big). -SMALL_NUM = 1e-12 - - -global USER_FILL_HOLES -global USER_FILL_HOLES_QUALITY -USER_FILL_HOLES = None -USER_FILL_HOLES_QUALITY = None - - -def pointInTri2D(v, v1, v2, v3): - key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y - - # Commented because its slower to do the bounds check, we should really cache the bounds info for each face. - ''' - # BOUNDS CHECK - xmin= 1000000 - ymin= 1000000 - - xmax= -1000000 - ymax= -1000000 - - for i in (0,2,4): - x= key[i] - y= key[i+1] - - if xmax<x: xmax= x - if ymax<y: ymax= y - if xmin>x: xmin= x - if ymin>y: ymin= y - - x= v.x - y= v.y - - if x<xmin or x>xmax or y < ymin or y > ymax: - return False - # Done with bounds check - ''' - try: - mtx = dict_matrix[key] - if not mtx: - return False - except: - side1 = v2 - v1 - side2 = v3 - v1 - - nor = side1.cross(side2) - - mtx = Matrix((side1, side2, nor)) - - # Zero area 2d tri, even tho we throw away zero area faces - # the projection UV can result in a zero area UV. - if not mtx.determinant(): - dict_matrix[key] = None - return False - - mtx.invert() - - dict_matrix[key] = mtx - - uvw = (v - v1) @ mtx - return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1 - - -def boundsIsland(faces): - minx = maxx = faces[0].uv[0][0] # Set initial bounds. - miny = maxy = faces[0].uv[0][1] - # print len(faces), minx, maxx, miny , maxy - for f in faces: - for uv in f.uv: - x = uv.x - y = uv.y - if x < minx: - minx = x - if y < miny: - miny = y - if x > maxx: - maxx = x - if y > maxy: - maxy = y - - return minx, miny, maxx, maxy - - -""" -def boundsEdgeLoop(edges): - minx = maxx = edges[0][0] # Set initial bounds. - miny = maxy = edges[0][1] - # print len(faces), minx, maxx, miny , maxy - for ed in edges: - for pt in ed: - x= pt[0] - y= pt[1] - if x<minx: x= minx - if y<miny: y= miny - if x>maxx: x= maxx - if y>maxy: y= maxy - - return minx, miny, maxx, maxy -""" - -# Turns the islands into a list of unpordered edges (Non internal) -# Only for UV's -# only returns outline edges for intersection tests. and unique points. - - -def island2Edge(island): - - # Vert index edges - edges = {} - - unique_points = {} - - for f in island: - f_uvkey = list(map(tuple, f.uv)) - - for vIdx in range(len(f_uvkey)): - unique_points[f_uvkey[vIdx]] = f.uv[vIdx] - - if f.v[vIdx].index > f.v[vIdx - 1].index: - i1 = vIdx - 1 - i2 = vIdx - else: - i1 = vIdx - i2 = vIdx - 1 - - try: - edges[f_uvkey[i1], f_uvkey[i2]] *= 0 # sets any edge with more than 1 user to 0 are not returned. - except: - edges[f_uvkey[i1], f_uvkey[i2]] = (f.uv[i1] - f.uv[i2]).length - - # If 2 are the same then they will be together, but full [a,b] order is not correct. - - # Sort by length - length_sorted_edges = [(Vector(key[0]), Vector(key[1]), value) for key, value in edges.items() if value != 0] - - length_sorted_edges.sort(key=lambda a: -a[2]) # largest first - - # Its okay to leave the length in there. - # for e in length_sorted_edges: - # e.pop(2) - - # return edges and unique points - return length_sorted_edges, [v.to_3d() for v in unique_points.values()] - - -def pointInIsland(pt, island): - vec1, vec2, vec3 = Vector(), Vector(), Vector() - for f in island: - vec1.x, vec1.y = f.uv[0] - vec2.x, vec2.y = f.uv[1] - vec3.x, vec3.y = f.uv[2] - - if pointInTri2D(pt, vec1, vec2, vec3): - return True - - if len(f.v) == 4: - vec1.x, vec1.y = f.uv[0] - vec2.x, vec2.y = f.uv[2] - vec3.x, vec3.y = f.uv[3] - if pointInTri2D(pt, vec1, vec2, vec3): - return True - return False - - -# box is (left,bottom, right, top) -def islandIntersectUvIsland(source, target, SourceOffset): - # Is 1 point in the box, inside the vertLoops - edgeLoopsSource = source[6] # Pretend this is offset - edgeLoopsTarget = target[6] - - # Edge intersect test - for ed in edgeLoopsSource: - for seg in edgeLoopsTarget: - i = geometry.intersect_line_line_2d(seg[0], - seg[1], - SourceOffset + ed[0], - SourceOffset + ed[1], - ) - if i: - return 1 # LINE INTERSECTION - - # 1 test for source being totally inside target - SourceOffset.resize_3d() - for pv in source[7]: - if pointInIsland(pv + SourceOffset, target[0]): - return 2 # SOURCE INSIDE TARGET - - # 2 test for a part of the target being totally inside the source. - for pv in target[7]: - if pointInIsland(pv - SourceOffset, source[0]): - return 3 # PART OF TARGET INSIDE SOURCE. - - return 0 # NO INTERSECTION - - -def rotate_uvs(uv_points, angle): - - if angle != 0.0: - mat = Matrix.Rotation(angle, 2) - for uv in uv_points: - uv[:] = mat @ uv - - -def optiRotateUvIsland(faces): - uv_points = [uv for f in faces for uv in f.uv] - angle = geometry.box_fit_2d(uv_points) - - if angle != 0.0: - rotate_uvs(uv_points, angle) - - # orient them vertically (could be an option) - minx, miny, maxx, maxy = boundsIsland(faces) - w, h = maxx - minx, maxy - miny - # use epsilon so we don't randomly rotate (almost) perfect squares. - if h + 0.00001 < w: - from math import pi - angle = pi / 2.0 - rotate_uvs(uv_points, angle) - - -# Takes an island list and tries to find concave, hollow areas to pack smaller islands into. -def mergeUvIslands(islandList): - global USER_FILL_HOLES - global USER_FILL_HOLES_QUALITY - - # Pack islands to bottom LHS - # Sync with island - - # islandTotFaceArea = [] # A list of floats, each island area - # islandArea = [] # a list of tuples ( area, w,h) - - decoratedIslandList = [] - - islandIdx = len(islandList) - while islandIdx: - islandIdx -= 1 - minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx]) - w, h = maxx - minx, maxy - miny - - totFaceArea = 0 - offset = Vector((minx, miny)) - for f in islandList[islandIdx]: - for uv in f.uv: - uv -= offset - - totFaceArea += f.area - - islandBoundsArea = w * h - efficiency = abs(islandBoundsArea - totFaceArea) - - # UV Edge list used for intersections as well as unique points. - edges, uniqueEdgePoints = island2Edge(islandList[islandIdx]) - - decoratedIslandList.append([ - islandList[islandIdx], - totFaceArea, - efficiency, - islandBoundsArea, - w, - h, - edges, - uniqueEdgePoints, - ]) - - # Sort by island bounding box area, smallest face area first. - # no.. chance that to most simple edge loop first. - decoratedIslandListAreaSort = decoratedIslandList[:] - - decoratedIslandListAreaSort.sort(key=lambda A: A[3]) - - # sort by efficiency, Least Efficient first. - decoratedIslandListEfficSort = decoratedIslandList[:] - # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2])) - - decoratedIslandListEfficSort.sort(key=lambda A: -A[2]) - - # ================================================== THESE CAN BE TWEAKED. - # This is a quality value for the number of tests. - # from 1 to 4, generic quality value is from 1 to 100 - USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1 - - # If 100 will test as long as there is enough free space. - # this is rarely enough, and testing takes a while, so lower quality speeds this up. - - # 1 means they have the same quality - USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY) / 100.0) * 5) - - # print 'USER_STEP_QUALITY', USER_STEP_QUALITY - # print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY - - removedCount = 0 - - areaIslandIdx = 0 - ctrl = Window.Qual.CTRL - BREAK = False - while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK: - sourceIsland = decoratedIslandListAreaSort[areaIslandIdx] - # Already packed? - if not sourceIsland[0]: - areaIslandIdx += 1 - else: - efficIslandIdx = 0 - while efficIslandIdx < len(decoratedIslandListEfficSort) and not BREAK: - - if Window.GetKeyQualifiers() & ctrl: - BREAK = True - break - - # Now we have 2 islands, if the efficiency of the islands lowers there's an - # increasing likely hood that we can fit merge into the bigger UV island. - # this ensures a tight fit. - - # Just use figures we have about user/unused area to see if they might fit. - - targetIsland = decoratedIslandListEfficSort[efficIslandIdx] - - if sourceIsland[0] == targetIsland[0] or\ - not targetIsland[0] or\ - not sourceIsland[0]: - pass - else: - - #~ ([island, totFaceArea, efficiency, islandArea, w,h]) - # Wasted space on target is greater then UV bounding island area. - - #~ if targetIsland[3] > (sourceIsland[2]) and\ # - # ~ print USER_FREE_SPACE_TO_TEST_QUALITY - if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\ - targetIsland[4] > sourceIsland[4] and\ - targetIsland[5] > sourceIsland[5]: - - # DEBUG # print '%.10f %.10f' % (targetIsland[3], sourceIsland[1]) - - # These enough spare space lets move the box until it fits - - # How many times does the source fit into the target x/y - blockTestXUnit = targetIsland[4] / sourceIsland[4] - blockTestYUnit = targetIsland[5] / sourceIsland[5] - - boxLeft = 0 - - # Distance we can move between whilst staying inside the targets bounds. - testWidth = targetIsland[4] - sourceIsland[4] - testHeight = targetIsland[5] - sourceIsland[5] - - # Increment we move each test. x/y - xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY / 50) + 0.1))) - yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY / 50) + 0.1))) - - # Make sure were not moving less then a 3rg of our width/height - if xIncrement < sourceIsland[4] / 3: - xIncrement = sourceIsland[4] - if yIncrement < sourceIsland[5] / 3: - yIncrement = sourceIsland[5] - - boxLeft = 0 # Start 1 back so we can jump into the loop. - boxBottom = 0 # -yIncrement - - # ~ testcount= 0 - - while boxBottom <= testHeight: - # Should we use this? - not needed for now. - # ~ if Window.GetKeyQualifiers() & ctrl: - # ~ BREAK= True - # ~ break - - # testcount+=1 - # print 'Testing intersect' - Intersect = islandIntersectUvIsland( - sourceIsland, targetIsland, Vector((boxLeft, boxBottom))) - # print 'Done', Intersect - if Intersect == 1: # Line intersect, don't bother with this any more - pass - - if Intersect == 2: # Source inside target - """ - We have an intersection, if we are inside the target - then move us 1 whole width across, - Its possible this is a bad idea since 2 skinny Angular faces - could join without 1 whole move, but its a lot more optimal to speed this up - since we have already tested for it. - - It gives about 10% speedup with minimal errors. - """ - # Move the test along its width + SMALL_NUM - #boxLeft += sourceIsland[4] + SMALL_NUM - boxLeft += sourceIsland[4] - elif Intersect == 0: # No intersection?? Place it. - # Progress - removedCount += 1 -# XXX Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount) - - # Move faces into new island and offset - targetIsland[0].extend(sourceIsland[0]) - offset = Vector((boxLeft, boxBottom)) - - for f in sourceIsland[0]: - for uv in f.uv: - uv += offset - - del sourceIsland[0][:] # Empty - - # Move edge loop into new and offset. - # targetIsland[6].extend(sourceIsland[6]) - # while sourceIsland[6]: - targetIsland[6].extend([( - (e[0] + offset, e[1] + offset, e[2]) - ) for e in sourceIsland[6]]) - - del sourceIsland[6][:] # Empty - - # Sort by edge length, reverse so biggest are first. - - try: - targetIsland[6].sort(key=lambda A: A[2]) - except: - targetIsland[6].sort(lambda B, A: cmp(A[2], B[2])) - - targetIsland[7].extend(sourceIsland[7]) - offset = Vector((boxLeft, boxBottom, 0.0)) - for p in sourceIsland[7]: - p += offset - - del sourceIsland[7][:] - - # Decrement the efficiency - targetIsland[1] += sourceIsland[1] # Increment totFaceArea - targetIsland[2] -= sourceIsland[1] # Decrement efficiency - # IF we ever used these again, should set to 0, eg - sourceIsland[2] = 0 # No area if anyone wants to know - - break - - # INCREMENT NEXT LOCATION - if boxLeft > testWidth: - boxBottom += yIncrement - boxLeft = 0.0 - else: - boxLeft += xIncrement - # print testcount - - efficIslandIdx += 1 - areaIslandIdx += 1 - - # Remove empty islands - i = len(islandList) - while i: - i -= 1 - if not islandList[i]: - del islandList[i] # Can increment islands removed here. - -# Takes groups of faces. assumes face groups are UV groups. - - -def getUvIslands(faceGroups, me): - - # Get seams so we don't cross over seams - edge_seams = {} # should be a set - for ed in me.edges: - if ed.use_seam: - edge_seams[ed.key] = None # dummy var- use sets! - # Done finding seams - - islandList = [] - -# XXX Window.DrawProgressBar(0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups)) - # print '\tSplitting %d projection groups into UV islands:' % len(faceGroups), - # Find grouped faces - - faceGroupIdx = len(faceGroups) - - while faceGroupIdx: - faceGroupIdx -= 1 - faces = faceGroups[faceGroupIdx] - - if not faces: - continue - - # Build edge dict - edge_users = {} - - for i, f in enumerate(faces): - for ed_key in f.edge_keys: - if ed_key in edge_seams: # DELIMIT SEAMS! ;) - edge_users[ed_key] = [] # so as not to raise an error - else: - try: - edge_users[ed_key].append(i) - except: - edge_users[ed_key] = [i] - - # Modes - # 0 - face not yet touched. - # 1 - added to island list, and need to search - # 2 - touched and searched - don't touch again. - face_modes = [0] * len(faces) # initialize zero - untested. - - face_modes[0] = 1 # start the search with face 1 - - newIsland = [] - - newIsland.append(faces[0]) - - ok = True - while ok: - - ok = True - while ok: - ok = False - for i in range(len(faces)): - if face_modes[i] == 1: # search - for ed_key in faces[i].edge_keys: - for ii in edge_users[ed_key]: - if i != ii and face_modes[ii] == 0: - face_modes[ii] = ok = 1 # mark as searched - newIsland.append(faces[ii]) - - # mark as searched, don't look again. - face_modes[i] = 2 - - islandList.append(newIsland) - - ok = False - for i in range(len(faces)): - if face_modes[i] == 0: - newIsland = [] - newIsland.append(faces[i]) - - face_modes[i] = ok = 1 - break - # if not ok will stop looping - -# XXX Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList)) - - for island in islandList: - optiRotateUvIsland(island) - - return islandList - - -def packIslands(islandList): - if USER_FILL_HOLES: - # XXX Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...') - mergeUvIslands(islandList) # Modify in place - - # Now we have UV islands, we need to pack them. - - # Make a synchronized list with the islands - # so we can box pack the islands. - packBoxes = [] - - # Keep a list of X/Y offset so we can save time by writing the - # uv's and packed data in one pass. - islandOffsetList = [] - - islandIdx = 0 - - while islandIdx < len(islandList): - minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx]) - - w, h = maxx - minx, maxy - miny - - if USER_ISLAND_MARGIN: - minx -= USER_ISLAND_MARGIN * w / 2 - miny -= USER_ISLAND_MARGIN * h / 2 - maxx += USER_ISLAND_MARGIN * w / 2 - maxy += USER_ISLAND_MARGIN * h / 2 - - # recalc width and height - w, h = maxx - minx, maxy - miny - - if w < SMALL_NUM: - w = SMALL_NUM - if h < SMALL_NUM: - h = SMALL_NUM - - """Save the offset to be applied later, - we could apply to the UVs now and align them to the bottom left hand area - of the UV coords like the box packer imagines they are - but, its quicker just to remember their offset and - apply the packing and offset in 1 pass """ - islandOffsetList.append((minx, miny)) - - # Add to boxList. use the island idx for the BOX id. - packBoxes.append([0, 0, w, h]) - islandIdx += 1 - - # Now we have a list of boxes to pack that syncs - # with the islands. - - # print '\tPacking UV Islands...' -# XXX Window.DrawProgressBar(0.7, "Packing %i UV Islands..." % len(packBoxes) ) - - # time1 = time.time() - packWidth, packHeight = geometry.box_pack_2d(packBoxes) - - # print 'Box Packing Time:', time.time() - time1 - - # if len(packedLs) != len(islandList): - # raise ValueError("Packed boxes differs from original length") - - # print '\tWriting Packed Data to faces' -# XXX Window.DrawProgressBar(0.8, "Writing Packed Data to faces") - - # Sort by ID, so there in sync again - islandIdx = len(islandList) - # Having these here avoids divide by 0 - if islandIdx: - - if USER_STRETCH_ASPECT: - # Maximize to uv area?? Will write a normalize function. - xfactor = 1.0 / packWidth - yfactor = 1.0 / packHeight - else: - # Keep proportions. - xfactor = yfactor = 1.0 / max(packWidth, packHeight) - - while islandIdx: - islandIdx -= 1 - # Write the packed values to the UV's - - xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0] - yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1] - - for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box - for uv in f.uv: - uv.x = (uv.x + xoffset) * xfactor - uv.y = (uv.y + yoffset) * yfactor - - -def VectoQuat(vec): - vec = vec.normalized() - return vec.to_track_quat('Z', 'X' if abs(vec.x) > 0.5 else 'Y').inverted() - - -class thickface: - __slost__ = "v", "uv", "no", "area", "edge_keys" - - def __init__(self, face, uv_layer, mesh_verts): - self.v = [mesh_verts[i] for i in face.vertices] - self.uv = [uv_layer[i].uv for i in face.loop_indices] - - self.no = face.normal.copy() - self.area = face.area - self.edge_keys = face.edge_keys - - -def main_consts(): - from math import radians - - global ROTMAT_2D_POS_90D - global ROTMAT_2D_POS_45D - global RotMatStepRotation - - ROTMAT_2D_POS_90D = Matrix.Rotation(radians(90.0), 2) - ROTMAT_2D_POS_45D = Matrix.Rotation(radians(45.0), 2) - - RotMatStepRotation = [] - rot_angle = 22.5 # 45.0/2 - while rot_angle > 0.1: - RotMatStepRotation.append([ - Matrix.Rotation(radians(+rot_angle), 2), - Matrix.Rotation(radians(-rot_angle), 2), - ]) - - rot_angle = rot_angle / 2.0 - - -global ob -ob = None - - -def main(context, - island_margin, - projection_limit, - user_area_weight, - use_aspect, - stretch_to_bounds, - ): - global USER_FILL_HOLES - global USER_FILL_HOLES_QUALITY - global USER_STRETCH_ASPECT - global USER_ISLAND_MARGIN - - from math import cos - import time - - global dict_matrix - dict_matrix = {} - - # Constants: - # Takes a list of faces that make up a UV island and rotate - # until they optimally fit inside a square. - global ROTMAT_2D_POS_90D - global ROTMAT_2D_POS_45D - global RotMatStepRotation - main_consts() - - # Create the variables. - USER_PROJECTION_LIMIT = projection_limit - USER_ONLY_SELECTED_FACES = True - USER_SHARE_SPACE = 1 # Only for hole filling. - USER_STRETCH_ASPECT = stretch_to_bounds - USER_ISLAND_MARGIN = island_margin # Only for hole filling. - USER_FILL_HOLES = 0 - USER_FILL_HOLES_QUALITY = 50 # Only for hole filling. - USER_VIEW_INIT = 0 # Only for hole filling. - - is_editmode = (context.mode == 'EDIT_MESH') - if is_editmode: - obList = context.objects_in_mode_unique_data - else: - obList = [ - ob for ob in context.selected_editable_objects - if ob.type == 'MESH' and ob.data.library is None - ] - - if not is_editmode: - USER_ONLY_SELECTED_FACES = False - - if not obList: - raise Exception("error, no selected mesh objects") - - # Convert from being button types - USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD) - USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT / 2) * DEG_TO_RAD) - - # Toggle Edit mode - if is_editmode: - bpy.ops.object.mode_set(mode='OBJECT') - # Assume face select mode! an annoying hack to toggle face select mode because Mesh doesn't like faceSelectMode. - - if USER_SHARE_SPACE: - # Sort by data name so we get consistent results - obList.sort(key=lambda ob: ob.data.name) - collected_islandList = [] - - time1 = time.time() - - # Tag as False so we don't operate on the same mesh twice. - for me in bpy.data.meshes: - me.tag = False - - for ob in obList: - me = ob.data - - if me.tag or me.library: - continue - - # Tag as used - me.tag = True - - if not me.uv_layers: # Mesh has no UV Coords, don't bother. - me.uv_layers.new() - - uv_layer = me.uv_layers.active.data - me_verts = list(me.vertices) - - if USER_ONLY_SELECTED_FACES: - meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons) if f.select] - else: - meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons)] - - # ======= - # Generate a projection list from face normals, this is meant to be smart :) - - # make a list of face props that are in sync with meshFaces - # Make a Face List that is sorted by area. - # meshFaces = [] - - # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first. - meshFaces.sort(key=lambda a: -a.area) - - # remove all zero area faces - while meshFaces and meshFaces[-1].area <= SMALL_NUM: - # Set their UV's to 0,0 - for uv in meshFaces[-1].uv: - uv.zero() - meshFaces.pop() - - if not meshFaces: - continue - - # Smallest first is slightly more efficient, - # but if the user cancels early then its better we work on the larger data. - - # Generate Projection Vecs - # 0d is 1.0 - # 180 IS -0.59846 - - # Initialize projectVecs - if USER_VIEW_INIT: - # Generate Projection - - # We add to this along the way - projectVecs = [Vector(Window.GetViewVector()) @ ob.matrix_world.inverted().to_3x3()] - else: - projectVecs = [] - - newProjectVec = meshFaces[0].no - newProjectMeshFaces = [] # Popping stuffs it up. - - # Pretend that the most unique angle is ages away to start the loop off - mostUniqueAngle = -1.0 - - # This is popped - tempMeshFaces = meshFaces[:] - - # This while only gathers projection vecs, faces are assigned later on. - while 1: - # If there's none there then start with the largest face - - # add all the faces that are close. - for fIdx in range(len(tempMeshFaces) - 1, -1, -1): - # Use half the angle limit so we don't overweight faces towards this - # normal and hog all the faces. - if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED: - newProjectMeshFaces.append(tempMeshFaces.pop(fIdx)) - - # Add the average of all these faces normals as a projectionVec - averageVec = Vector((0.0, 0.0, 0.0)) - if user_area_weight == 0.0: - for fprop in newProjectMeshFaces: - averageVec += fprop.no - elif user_area_weight == 1.0: - for fprop in newProjectMeshFaces: - averageVec += fprop.no * fprop.area - else: - for fprop in newProjectMeshFaces: - averageVec += fprop.no * ((fprop.area * user_area_weight) + (1.0 - user_area_weight)) - - if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN - projectVecs.append(averageVec.normalized()) - - # Get the next vec! - # Pick the face that's most different to all existing angles :) - mostUniqueAngle = 1.0 # 1.0 is 0d. no difference. - mostUniqueIndex = 0 # dummy - - for fIdx in range(len(tempMeshFaces) - 1, -1, -1): - angleDifference = -1.0 # 180d difference. - - # Get the closest vec angle we are to. - for p in projectVecs: - temp_angle_diff = p.dot(tempMeshFaces[fIdx].no) - - if angleDifference < temp_angle_diff: - angleDifference = temp_angle_diff - - if angleDifference < mostUniqueAngle: - # We have a new most different angle - mostUniqueIndex = fIdx - mostUniqueAngle = angleDifference - - if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED: - # print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces) - # Now weight the vector to all its faces, will give a more direct projection - # if the face its self was not representative of the normal from surrounding faces. - - newProjectVec = tempMeshFaces[mostUniqueIndex].no - newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)] - - else: - if len(projectVecs) >= 1: # Must have at least 2 projections - break - - # If there are only zero area faces then its possible - # there are no projectionVecs - if not len(projectVecs): - Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.') - return - - faceProjectionGroupList = [[] for i in range(len(projectVecs))] - - # MAP and Arrange # We know there are 3 or 4 faces here - - for fIdx in range(len(meshFaces) - 1, -1, -1): - fvec = meshFaces[fIdx].no - i = len(projectVecs) - - # Initialize first - bestAng = fvec.dot(projectVecs[0]) - bestAngIdx = 0 - - # Cycle through the remaining, first already done - while i - 1: - i -= 1 - - newAng = fvec.dot(projectVecs[i]) - if newAng > bestAng: # Reverse logic for dotvecs - bestAng = newAng - bestAngIdx = i - - # Store the area for later use. - faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx]) - - # Cull faceProjectionGroupList, - - # Now faceProjectionGroupList is full of faces that face match the project Vecs list - for i in range(len(projectVecs)): - # Account for projectVecs having no faces. - if not faceProjectionGroupList[i]: - continue - - # Make a projection matrix from a unit length vector. - MatQuat = VectoQuat(projectVecs[i]) - - # Get the faces UV's from the projected vertex. - for f in faceProjectionGroupList[i]: - f_uv = f.uv - for j, v in enumerate(f.v): - f_uv[j][:] = (MatQuat @ v.co).xy - - if USER_SHARE_SPACE: - # Should we collect and pack later? - islandList = getUvIslands(faceProjectionGroupList, me) - collected_islandList.extend(islandList) - - else: - # Should we pack the islands for this 1 object? - islandList = getUvIslands(faceProjectionGroupList, me) - packIslands(islandList) - - # update the mesh here if we need to. - - # We want to pack all in 1 go, so pack now - if USER_SHARE_SPACE: - packIslands(collected_islandList) - - print("Smart Projection time: %.2f" % (time.time() - time1)) - - # aspect correction is only done in edit mode - and only smart unwrap supports currently - if is_editmode: - bpy.ops.object.mode_set(mode='EDIT') - - if use_aspect: - import bmesh - aspect = context.scene.uvedit_aspect(context.active_object) - if aspect[0] > aspect[1]: - aspect[0] = aspect[1] / aspect[0] - aspect[1] = 1.0 - else: - aspect[1] = aspect[0] / aspect[1] - aspect[0] = 1.0 - - bm = bmesh.from_edit_mesh(me) - - uv_act = bm.loops.layers.uv.active - - faces = [f for f in bm.faces if f.select] - - for f in faces: - for l in f.loops: - l[uv_act].uv[0] *= aspect[0] - l[uv_act].uv[1] *= aspect[1] - - dict_matrix.clear() - - -from bpy.props import FloatProperty, BoolProperty - - -class SmartProject(Operator): - """This script projection unwraps the selected faces of a mesh """ \ - """(it operates on all selected mesh objects, and can be used """ \ - """to unwrap selected faces, or all faces)""" - bl_idname = "uv.smart_project" - bl_label = "Smart UV Project" - bl_options = {'REGISTER', 'UNDO'} - - angle_limit: FloatProperty( - name="Angle Limit", - description="Lower for more projection groups, higher for less distortion", - min=1.0, max=89.0, - default=66.0, - ) - island_margin: FloatProperty( - name="Island Margin", - description="Margin to reduce bleed from adjacent islands", - min=0.0, max=1.0, - default=0.0, - ) - user_area_weight: FloatProperty( - name="Area Weight", - description="Weight projections vector by faces with larger areas", - min=0.0, max=1.0, - default=0.0, - ) - use_aspect: BoolProperty( - name="Correct Aspect", - description="Map UVs taking image aspect ratio into account", - default=True, - ) - stretch_to_bounds: BoolProperty( - name="Stretch to UV Bounds", - description="Stretch the final output to texture bounds", - default=True, - ) - - @classmethod - def poll(cls, context): - return context.active_object is not None - - def execute(self, context): - main(context, - self.island_margin, - self.angle_limit, - self.user_area_weight, - self.use_aspect, - self.stretch_to_bounds, - ) - return {'FINISHED'} - - def invoke(self, context, _event): - wm = context.window_manager - return wm.invoke_props_dialog(self) - - -classes = ( - SmartProject, -) diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 215c96a5975..b47d4223372 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -1029,7 +1029,7 @@ class ConstraintButtonsSubPanel(Panel): col = layout.column(align=True) col.prop(con, "map_to_z_from", expand=False, text="Z Source Axis") col.prop(con, "to_min_z" + ext, text="Min") - col.prop(con, "to_max_z" + ext, text="Max") + col.prop(con, "to_max_z" + ext, text="Max") layout.prop(con, "mix_mode" + ext, text="Mix") @@ -1396,7 +1396,7 @@ class BONE_PT_bTransformConstraint_from(BoneConstraintPanel, ConstraintButtonsSu def draw(self, context): self.draw_transform_from(context) - + class OBJECT_PT_bTransformConstraint_destination(ObjectConstraintPanel, ConstraintButtonsSubPanel): bl_parent_id = "OBJECT_PT_bTransformConstraint" bl_label = "Map To" diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 4d6ddb55f24..f16528103ff 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -405,6 +405,7 @@ class GPENCIL_MT_cleanup(Menu): layout = self.layout layout.operator("gpencil.frame_clean_loose", text="Delete Loose Points") + layout.operator("gpencil.frame_clean_duplicate", text="Delete Duplicated Frames") if ob.mode != 'PAINT_GPENCIL': layout.operator("gpencil.stroke_merge_by_distance", text="Merge by Distance") diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 6dde1e715bb..01f9eedd96e 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -646,6 +646,8 @@ def brush_settings(layout, context, brush, popover=False): layout.prop(brush, "pose_smooth_iterations") if brush.pose_deform_type == 'ROTATE_TWIST' and brush.pose_origin_type in {'TOPOLOGY', 'FACE_SETS'}: layout.prop(brush, "pose_ik_segments") + if brush.pose_deform_type == 'SCALE_TRANSLATE': + layout.prop(brush, "use_pose_lock_rotation") layout.prop(brush, "use_pose_ik_anchored") layout.prop(brush, "use_connected_only") layout.prop(brush, "disconnected_distance_max") @@ -654,14 +656,21 @@ def brush_settings(layout, context, brush, popover=False): elif sculpt_tool == 'CLOTH': layout.separator() - layout.prop(brush, "cloth_sim_limit") - layout.prop(brush, "cloth_sim_falloff") + layout.prop(brush, "cloth_simulation_area_type") + if brush.cloth_simulation_area_type == 'LOCAL': + layout.prop(brush, "cloth_sim_limit") + layout.prop(brush, "cloth_sim_falloff") + layout.prop(brush, "use_cloth_pin_simulation_boundary") + layout.separator() layout.prop(brush, "cloth_deform_type") layout.prop(brush, "cloth_force_falloff_type") layout.separator() layout.prop(brush, "cloth_mass") layout.prop(brush, "cloth_damping") + layout.prop(brush, "cloth_constraint_softbody_strength") + layout.separator() + layout.prop(brush, "use_cloth_collision") layout.separator() elif sculpt_tool == 'SCRAPE': diff --git a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py index b03f80bd600..a55bd89ca18 100644 --- a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py +++ b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py @@ -23,10 +23,10 @@ from bpy.types import ( ) -def rigid_body_warning(layout): +def rigid_body_warning(layout, text): row = layout.row(align=True) row.alignment = 'RIGHT' - row.label(text="Object does not have a Rigid Body") + row.label(text=text, icon='ERROR') class PHYSICS_PT_rigidbody_panel: @@ -49,13 +49,24 @@ class PHYSICS_PT_rigid_body(PHYSICS_PT_rigidbody_panel, Panel): layout.use_property_split = True ob = context.object + parent = ob.parent rbo = ob.rigid_body if rbo is None: - rigid_body_warning(layout) + rigid_body_warning(layout, "Object does not have a Rigid Body") return - layout.prop(rbo, "type", text="Type") + if parent is not None and parent.rigid_body is not None: + if parent.rigid_body.collision_shape == 'COMPOUND': + row = layout.row(align=True) + row.alignment = 'RIGHT' + row.label(text="This object is part of a compound shape", icon='INFO') + else: + rigid_body_warning(layout, "Rigid Body can't be child of a non compound Rigid Body") + return + + if parent is None or parent.rigid_body is None: + layout.prop(rbo, "type", text="Type") class PHYSICS_PT_rigid_body_settings(PHYSICS_PT_rigidbody_panel, Panel): @@ -66,6 +77,8 @@ class PHYSICS_PT_rigid_body_settings(PHYSICS_PT_rigidbody_panel, Panel): @classmethod def poll(cls, context): obj = context.object + if obj.parent is not None and obj.parent.rigid_body is not None: + return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) def draw(self, context): @@ -76,7 +89,7 @@ class PHYSICS_PT_rigid_body_settings(PHYSICS_PT_rigidbody_panel, Panel): rbo = ob.rigid_body if rbo is None: - rigid_body_warning(layout) + rigid_body_warning(layout, "Object does not have a Rigid Body") return col = layout.column() @@ -96,17 +109,32 @@ class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel): @classmethod def poll(cls, context): obj = context.object + if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND': + return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) def draw(self, context): layout = self.layout ob = context.object + parent = ob.parent rbo = ob.rigid_body layout.use_property_split = True layout.prop(rbo, "collision_shape", text="Shape") + if rbo.collision_shape == 'COMPOUND': + if parent is not None and parent.rigid_body is not None and parent.rigid_body.collision_shape == 'COMPOUND': + rigid_body_warning(layout, "Sub compound shapes are not allowed") + else: + found = False + for child in ob.children: + if child.rigid_body is not None: + found = True + break + if not found: + rigid_body_warning(layout, "There are no child rigid bodies") + if rbo.collision_shape in {'MESH', 'CONVEX_HULL'}: layout.prop(rbo, "mesh_source", text="Source") @@ -123,6 +151,8 @@ class PHYSICS_PT_rigid_body_collisions_surface(PHYSICS_PT_rigidbody_panel, Panel @classmethod def poll(cls, context): obj = context.object + if obj.parent is not None and obj.parent.rigid_body is not None: + return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) def draw(self, context): @@ -149,6 +179,8 @@ class PHYSICS_PT_rigid_body_collisions_sensitivity(PHYSICS_PT_rigidbody_panel, P @classmethod def poll(cls, context): obj = context.object + if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND': + return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) def draw(self, context): @@ -180,6 +212,8 @@ class PHYSICS_PT_rigid_body_collisions_collections(PHYSICS_PT_rigidbody_panel, P @classmethod def poll(cls, context): obj = context.object + if obj.parent is not None and obj.parent.rigid_body is not None: + return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) def draw(self, context): @@ -200,6 +234,8 @@ class PHYSICS_PT_rigid_body_dynamics(PHYSICS_PT_rigidbody_panel, Panel): @classmethod def poll(cls, context): obj = context.object + if obj.parent is not None and obj.parent.rigid_body is not None: + return False return (obj and obj.rigid_body and obj.rigid_body.type == 'ACTIVE' and (context.engine in cls.COMPAT_ENGINES)) diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index c251d55714f..078b1c9ff38 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -293,7 +293,7 @@ class GRAPH_MT_key(Menu): # Using the modal operation doesn't make sense for this variant # as we do not have a modal mode for it, so just execute it. - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("graph.decimate", text="Decimate (Allowed Change)").mode = 'ERROR' layout.operator_context = operator_context diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index b5926692324..faf4036f9b3 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -275,7 +275,7 @@ class NODE_MT_select(Menu): layout.separator() layout.operator("node.select_all").action = 'TOGGLE' - layout.operator("node.select_all", text="Inverse").action = 'INVERT' + layout.operator("node.select_all", text="Invert").action = 'INVERT' layout.operator("node.select_linked_from") layout.operator("node.select_linked_to") @@ -433,7 +433,7 @@ class NODE_MT_context_menu(Menu): layout.operator("node.delete") layout.operator("node.clipboard_copy", text="Copy") layout.operator("node.clipboard_paste", text="Paste") - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("node.delete_reconnect") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 8dfa182f52d..9f251a9abad 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -796,7 +796,7 @@ class SEQUENCER_MT_context_menu(Menu): props = layout.operator("wm.call_panel", text="Rename...") props.name = "TOPBAR_PT_name" props.keep_open = False - layout.operator("sequencer.delete", text="Delete...") + layout.operator("sequencer.delete", text="Delete") 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 e4cecf3378f..00ae884eeb9 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1257,6 +1257,8 @@ class _defs_sculpt: layout.prop(props, "surface_smooth_current_vertex", expand=False) elif props.type == 'SHARPEN': layout.prop(props, "sharpen_smooth_ratio", expand=False) + layout.prop(props, "sharpen_intensify_detail_strength", expand=False) + layout.prop(props, "sharpen_curvature_smooth_iterations", expand=False) return dict( idname="builtin.mesh_filter", @@ -1276,6 +1278,7 @@ class _defs_sculpt: layout.prop(props, "cloth_mass") layout.prop(props, "cloth_damping") layout.prop(props, "use_face_sets") + layout.prop(props, "use_collisions") return dict( idname="builtin.cloth_filter", @@ -2385,9 +2388,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): 'OBJECT': [ *_tools_default, - # Disable for 2.90 release. - # None, - # _tools_view3d_add, + None, + _tools_view3d_add, ], 'POSE': [ *_tools_default, @@ -2415,9 +2417,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'EDIT_MESH': [ *_tools_default, - # Disable for 2.90 release. - # None, - # _tools_view3d_add, + None, + _tools_view3d_add, None, ( _defs_edit_mesh.extrude, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 8880d8c5378..a9e1b933338 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -986,7 +986,7 @@ class VIEW3D_MT_transform_base(Menu): if context.mode != 'OBJECT': layout.operator("transform.vertex_warp", text="Warp") - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("transform.vertex_random", text="Randomize").offset = 0.1 layout.operator_context = 'INVOKE_REGION_WIN' @@ -1551,7 +1551,7 @@ class VIEW3D_MT_edit_mesh_select_by_trait(Menu): layout.separator() - layout.operator("mesh.select_ungrouped", text="Ungrouped Verts") + layout.operator("mesh.select_ungrouped", text="Ungrouped Vertices") class VIEW3D_MT_edit_mesh_select_more_less(Menu): @@ -1813,7 +1813,7 @@ class VIEW3D_MT_select_edit_lattice(Menu): layout.separator() - layout.operator("lattice.select_ungrouped", text="Ungrouped Verts") + layout.operator("lattice.select_ungrouped", text="Ungrouped Vertices") class VIEW3D_MT_select_edit_armature(Menu): @@ -1940,7 +1940,7 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu): layout.separator() - layout.operator("paint.vert_select_ungrouped", text="Ungrouped Verts") + layout.operator("paint.vert_select_ungrouped", text="Ungrouped Vertices") class VIEW3D_MT_angle_control(Menu): @@ -2058,7 +2058,7 @@ class VIEW3D_MT_edit_metaball_context_menu(Menu): layout.separator() # Remove - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("mball.delete_metaelems", text="Delete") @@ -2333,7 +2333,7 @@ class VIEW3D_MT_object(Menu): layout.separator() - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("object.delete", text="Delete").use_global = False layout.operator("object.delete", text="Delete Global").use_global = True @@ -2597,7 +2597,7 @@ class VIEW3D_MT_object_context_menu(Menu): layout.separator() - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("object.delete", text="Delete").use_global = False @@ -2683,7 +2683,7 @@ class VIEW3D_MT_object_parent(Menu): layout.separator() - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("object.parent_no_inverse_set") layout.operator_context = operator_context_default @@ -2751,6 +2751,8 @@ class VIEW3D_MT_object_quick_effects(Menu): layout.operator("object.quick_explode") layout.operator("object.quick_smoke") layout.operator("object.quick_liquid") + if _context.preferences.experimental.use_new_particle_system: + layout.operator("object.quick_particles") class VIEW3D_MT_object_showhide(Menu): @@ -2772,7 +2774,7 @@ class VIEW3D_MT_make_single_user(Menu): def draw(self, _context): layout = self.layout - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' props = layout.operator("object.make_single_user", text="Object") props.object = True @@ -3686,7 +3688,7 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu): row = layout.row() if is_vert_mode: - col = row.column() + col = row.column(align=True) col.label(text="Vertex Context Menu", icon='VERTEXSEL') col.separator() @@ -3712,7 +3714,7 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu): col.operator("transform.shrink_fatten", text="Shrink/Fatten") col.operator("transform.shear", text="Shear") col.operator("transform.vert_slide", text="Slide Vertices") - col.operator_context = 'EXEC_DEFAULT' + col.operator_context = 'EXEC_REGION_WIN' col.operator("transform.vertex_random", text="Randomize Vertices").offset = 0.1 col.operator("mesh.vertices_smooth", text="Smooth Vertices").factor = 0.5 col.operator_context = 'INVOKE_REGION_WIN' @@ -3736,7 +3738,7 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu): if is_edge_mode: render = context.scene.render - col = row.column() + col = row.column(align=True) col.label(text="Edge Context Menu", icon='EDGESEL') col.separator() @@ -3804,7 +3806,7 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu): col.operator("mesh.delete", text="Delete Edges").type = 'EDGE' if is_face_mode: - col = row.column() + col = row.column(align=True) col.label(text="Face Context Menu", icon='FACESEL') col.separator() @@ -3939,7 +3941,7 @@ class VIEW3D_MT_edit_mesh_vertices(Menu): layout.separator() layout.operator("transform.vert_slide", text="Slide Vertices") - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("mesh.vertices_smooth", text="Smooth Vertices").factor = 0.5 layout.operator("mesh.vertices_smooth_laplacian", text="Smooth Vertices (Laplacian)") layout.operator_context = 'INVOKE_REGION_WIN' @@ -4147,7 +4149,7 @@ class VIEW3D_MT_edit_mesh_normals_select_strength(Menu): class VIEW3D_MT_edit_mesh_normals_set_strength(Menu): - bl_label = "Select by Face Strength" + bl_label = "Set Face Strength" def draw(self, _context): layout = self.layout @@ -4193,7 +4195,7 @@ class VIEW3D_MT_edit_mesh_normals(Menu): layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("transform.rotate_normal", text="Rotate...") layout.operator("mesh.point_normals", text="Point to Target...") - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("mesh.merge_normals", text="Merge") layout.operator("mesh.split_normals", text="Split") @@ -4209,8 +4211,8 @@ class VIEW3D_MT_edit_mesh_normals(Menu): layout.separator() - layout.menu("VIEW3D_MT_edit_mesh_normals_select_strength", text="Select by Face Strength") - layout.menu("VIEW3D_MT_edit_mesh_normals_set_strength", text="Set Face Strength") + layout.menu("VIEW3D_MT_edit_mesh_normals_select_strength") + layout.menu("VIEW3D_MT_edit_mesh_normals_set_strength") class VIEW3D_MT_edit_mesh_shading(Menu): @@ -4660,7 +4662,7 @@ class VIEW3D_MT_edit_meta(Menu): layout.menu("VIEW3D_MT_edit_meta_showhide") - layout.operator_context = 'EXEC_DEFAULT' + layout.operator_context = 'EXEC_REGION_WIN' layout.operator("mball.delete_metaelems", text="Delete") diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 6e31808d27d..1b8869d0bc8 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1375,6 +1375,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} + bl_ui_units_x = 12 @classmethod def poll(cls, context): @@ -1393,6 +1394,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): col = layout.column(align=True) if brush is not None: + col.prop(gp_settings, "brush_draw_mode") + col.separator() + if brush.gpencil_tool != 'FILL': col.prop(gp_settings, "input_samples") col.separator() @@ -1421,11 +1425,22 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): row = col.row(align=True) row.prop(gp_settings, "fill_draw_mode", text="Boundary") row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID') + + col.separator() + row = col.row(align=True) + row.prop(gp_settings, "fill_layer_mode", text="Layers") + col.separator() col.prop(gp_settings, "fill_factor", text="Resolution") if gp_settings.fill_draw_mode != 'STROKE': - col.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes") - col.prop(gp_settings, "fill_threshold", text="Threshold") + col = layout.column(align=False, heading="Ignore Transparent") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(gp_settings, "show_fill", text="") + sub = sub.row(align=True) + sub.active = gp_settings.show_fill + sub.prop(gp_settings, "fill_threshold", text="") class VIEW3D_PT_tools_grease_pencil_brush_stroke(Panel, View3DPanel): diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 60eb0194a9a..f62c2ead144 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -475,6 +475,12 @@ texture_node_categories = [ ]), ] +def not_implemented_node(idname): + NodeType = getattr(bpy.types, idname) + name = NodeType.bl_rna.name + label = f"{name} (mockup)" + return NodeItem(idname, label=label) + simulation_node_categories = [ # Simulation Nodes SimulationNodeCategory("SIM_OUTPUT", "Output", items=[ @@ -489,12 +495,13 @@ simulation_node_categories = [ ]), SimulationNodeCategory("SIM_EMITTERS", "Emitters", items=[ NodeItem("SimulationNodeParticleMeshEmitter"), - NodeItem("SimulationNodeEmitParticles"), + not_implemented_node("SimulationNodeEmitParticles"), ]), SimulationNodeCategory("SIM_EVENTS", "Events", items=[ NodeItem("SimulationNodeParticleBirthEvent"), NodeItem("SimulationNodeParticleTimeStepEvent"), - NodeItem("SimulationNodeParticleMeshCollisionEvent"), + NodeItem("SimulationNodeAgeReachedEvent"), + not_implemented_node("SimulationNodeParticleMeshCollisionEvent"), ]), SimulationNodeCategory("SIM_FORCES", "Forces", items=[ NodeItem("SimulationNodeForce"), @@ -502,18 +509,19 @@ simulation_node_categories = [ SimulationNodeCategory("SIM_EXECUTE", "Execute", items=[ NodeItem("SimulationNodeSetParticleAttribute"), NodeItem("SimulationNodeExecuteCondition"), - NodeItem("SimulationNodeMultiExecute"), + NodeItem("SimulationNodeKillParticle"), + not_implemented_node("SimulationNodeMultiExecute"), ]), SimulationNodeCategory("SIM_NOISE", "Noise", items=[ - NodeItem("ShaderNodeTexNoise"), - NodeItem("ShaderNodeTexWhiteNoise"), + not_implemented_node("ShaderNodeTexNoise"), + not_implemented_node("ShaderNodeTexWhiteNoise"), ]), SimulationNodeCategory("SIM_COLOR", "Color", items=[ - NodeItem("ShaderNodeMixRGB"), - NodeItem("ShaderNodeInvert"), - NodeItem("ShaderNodeHueSaturation"), - NodeItem("ShaderNodeGamma"), - NodeItem("ShaderNodeBrightContrast"), + not_implemented_node("ShaderNodeMixRGB"), + not_implemented_node("ShaderNodeInvert"), + not_implemented_node("ShaderNodeHueSaturation"), + not_implemented_node("ShaderNodeGamma"), + not_implemented_node("ShaderNodeBrightContrast"), ]), SimulationNodeCategory("SIM_CONVERTER", "Converter", items=[ NodeItem("ShaderNodeMapRange"), @@ -525,12 +533,13 @@ simulation_node_categories = [ NodeItem("ShaderNodeCombineRGB"), NodeItem("ShaderNodeSeparateXYZ"), NodeItem("ShaderNodeCombineXYZ"), - NodeItem("ShaderNodeSeparateHSV"), - NodeItem("ShaderNodeCombineHSV"), + not_implemented_node("ShaderNodeSeparateHSV"), + not_implemented_node("ShaderNodeCombineHSV"), NodeItem("FunctionNodeBooleanMath"), NodeItem("FunctionNodeFloatCompare"), - NodeItem("FunctionNodeSwitch"), + not_implemented_node("FunctionNodeSwitch"), NodeItem("FunctionNodeCombineStrings"), + NodeItem("FunctionNodeRandomFloat"), ]), SimulationNodeCategory("SIM_GROUP", "Group", items=node_group_items), SimulationNodeCategory("SIM_LAYOUT", "Layout", items=[ diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 50a8223a84c..ff31878a929 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -194,6 +194,8 @@ static GPUTexture *blf_batch_cache_texture_load(void) int offset_x = bitmap_len_landed % tex_width; int offset_y = bitmap_len_landed / tex_width; + GPU_texture_bind(gc->texture, 0); + /* TODO(germano): Update more than one row in a single call. */ while (remain) { int remain_row = tex_width - offset_x; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 797c4f9f355..ae0a669bfb3 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,15 +31,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 290 +#define BLENDER_VERSION 291 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 7 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index 5a68514a387..1ada83c0163 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -69,7 +69,7 @@ void BKE_curvemapping_changed(struct CurveMapping *cumap, const bool rem_doubles void BKE_curvemapping_changed_all(struct CurveMapping *cumap); /* call before _all_ evaluation functions */ -void BKE_curvemapping_initialize(struct CurveMapping *cumap); +void BKE_curvemapping_init(struct CurveMapping *cumap); /* keep these (const CurveMap) - to help with thread safety */ /* single curve, no table check */ diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h index 84dbcd25b32..9c72a866fa9 100644 --- a/source/blender/blenkernel/BKE_curveprofile.h +++ b/source/blender/blenkernel/BKE_curveprofile.h @@ -71,7 +71,7 @@ void BKE_curveprofile_create_samples(struct CurveProfile *profile, bool sample_straight_edges, struct CurveProfilePoint *r_samples); -void BKE_curveprofile_initialize(struct CurveProfile *profile, short segments_len); +void BKE_curveprofile_init(struct CurveProfile *profile, short segments_len); /* Called for a complete update of the widget after modifications */ enum { diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index a18084f8bbd..f052ba400fc 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -54,6 +54,7 @@ void BKE_image_free_packedfiles(struct Image *image); void BKE_image_free_views(struct Image *image); void BKE_image_free_buffers(struct Image *image); void BKE_image_free_buffers_ex(struct Image *image, bool do_lock); +void BKE_image_free_gputextures(struct Image *ima); /* call from library */ void BKE_image_free(struct Image *image); @@ -273,6 +274,10 @@ void BKE_image_free_anim_ibufs(struct Image *ima, int except_frame); /* does all images with type MOVIE or SEQUENCE */ void BKE_image_all_free_anim_ibufs(struct Main *bmain, int except_frame); +void BKE_image_free_all_gputextures(struct Main *bmain); +void BKE_image_free_anim_gputextures(struct Main *bmain); +void BKE_image_free_old_gputextures(struct Main *bmain); + bool BKE_image_memorypack(struct Image *ima); void BKE_image_packfiles(struct ReportList *reports, struct Image *ima, const char *basepath); void BKE_image_packfiles_from_mem(struct ReportList *reports, @@ -361,6 +366,30 @@ bool BKE_image_has_loaded_ibuf(struct Image *image); struct ImBuf *BKE_image_get_ibuf_with_name(struct Image *image, const char *name); struct ImBuf *BKE_image_get_first_ibuf(struct Image *image); +/* Not to be use directly. */ +struct GPUTexture *BKE_image_create_gpu_texture_from_ibuf(struct Image *image, struct ImBuf *ibuf); + +/* Get the GPUTexture for a given `Image`. + * + * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already + * available. It is also required when requesting the GPUTexture for a render result. */ +struct GPUTexture *BKE_image_get_gpu_texture(struct Image *image, + struct ImageUser *iuser, + struct ImBuf *ibuf); +struct GPUTexture *BKE_image_get_gpu_tiles(struct Image *image, + struct ImageUser *iuser, + struct ImBuf *ibuf); +struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image, + struct ImageUser *iuser, + struct ImBuf *ibuf); + +void BKE_image_update_gputexture( + struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); +void BKE_image_paint_set_mipmap(struct Main *bmain, bool mipmap); + +/* Delayed free of OpenGL buffers by main thread */ +void BKE_image_free_unused_gpu_textures(void); + struct RenderSlot *BKE_image_add_renderslot(struct Image *ima, const char *name); bool BKE_image_remove_renderslot(struct Image *ima, struct ImageUser *iuser, int slot); struct RenderSlot *BKE_image_get_renderslot(struct Image *ima, int slot); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 5af73a1579d..5843992b25c 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -140,7 +140,7 @@ void BKE_lib_override_library_main_update(struct Main *bmain); /* For now, we just use a temp main list. */ typedef struct Main OverrideLibraryStorage; -OverrideLibraryStorage *BKE_lib_override_library_operations_store_initialize(void); +OverrideLibraryStorage *BKE_lib_override_library_operations_store_init(void); struct ID *BKE_lib_override_library_operations_store_start( struct Main *bmain, OverrideLibraryStorage *override_storage, struct ID *local); void BKE_lib_override_library_operations_store_end(OverrideLibraryStorage *override_storage, diff --git a/source/blender/blenkernel/BKE_movieclip.h b/source/blender/blenkernel/BKE_movieclip.h index d1f6efc035b..958fbef1f97 100644 --- a/source/blender/blenkernel/BKE_movieclip.h +++ b/source/blender/blenkernel/BKE_movieclip.h @@ -112,6 +112,11 @@ bool BKE_movieclip_put_frame_if_possible(struct MovieClip *clip, struct MovieClipUser *user, struct ImBuf *ibuf); +struct GPUTexture *BKE_movieclip_get_gpu_texture(struct MovieClip *clip, + struct MovieClipUser *cuser); + +void BKE_movieclip_free_gputexture(struct MovieClip *clip); + /* Dependency graph evaluation. */ void BKE_movieclip_eval_update(struct Depsgraph *depsgraph, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index b9e2531755b..ef46bc0f202 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1330,6 +1330,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define SIM_NODE_EMIT_PARTICLES 1009 #define SIM_NODE_TIME 1010 #define SIM_NODE_PARTICLE_ATTRIBUTE 1011 +#define SIM_NODE_AGE_REACHED_EVENT 1012 +#define SIM_NODE_KILL_PARTICLE 1013 /** \} */ @@ -1343,6 +1345,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_GROUP_INSTANCE_ID 1203 #define FN_NODE_COMBINE_STRINGS 1204 #define FN_NODE_OBJECT_TRANSFORMS 1205 +#define FN_NODE_RANDOM_FLOAT 1206 /** \} */ diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h index 6d488cc3142..1f8cdf27eb1 100644 --- a/source/blender/blenkernel/BKE_ocean.h +++ b/source/blender/blenkernel/BKE_ocean.h @@ -69,8 +69,10 @@ typedef struct OceanCache { struct Ocean *BKE_ocean_add(void); void BKE_ocean_free_data(struct Ocean *oc); void BKE_ocean_free(struct Ocean *oc); -bool BKE_ocean_ensure(struct OceanModifierData *omd); -void BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData const *omd); +bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution); +void BKE_ocean_init_from_modifier(struct Ocean *ocean, + struct OceanModifierData const *omd, + const int resolution); void BKE_ocean_init(struct Ocean *o, int M, diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index e2381bf68ac..4e520948bcb 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -41,6 +41,7 @@ struct EdgeSet; struct GHash; struct GridPaintMask; struct ImagePool; +struct ListBase; struct MLoop; struct MLoopTri; struct MVert; @@ -259,10 +260,20 @@ typedef struct SculptPoseIKChain { /* Cloth Brush */ typedef struct SculptClothLengthConstraint { - int v1; - int v2; + /* Elements that are affected by the constraint. */ + /* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always + * deformed. Element b could be another vertex of the same mesh or any other position (arbitrary + * point, position for a previous state). In that case, elem_index_a and elem_index_b should be + * the same to avoid affecting two different vertices when solving the constraints. + * *elem_position points to the position which is owned by the element. */ + int elem_index_a; + float *elem_position_a; + + int elem_index_b; + float *elem_position_b; float length; + float strength; } SculptClothLengthConstraint; typedef struct SculptClothSimulation { @@ -272,6 +283,11 @@ typedef struct SculptClothSimulation { int capacity_length_constraints; float *length_constraint_tweak; + /* Position anchors for deformation brushes. These positions are modified by the brush and the + * final positions of the simulated vertices are updated with constraints that use these points + * as targets. */ + float (*deformation_pos)[3]; + float mass; float damping; @@ -279,7 +295,9 @@ typedef struct SculptClothSimulation { float (*pos)[3]; float (*init_pos)[3]; float (*prev_pos)[3]; + float (*last_iteration_pos)[3]; + struct ListBase *collider_list; } SculptClothSimulation; typedef struct SculptPersistentBase { @@ -317,6 +335,9 @@ typedef struct SculptSession { int level; } multires; + /* Depsgraph for the Cloth Brush solver to get the colliders. */ + struct Depsgraph *depsgraph; + /* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */ struct MVert *mvert; struct MPoly *mpoly; diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 1e7a4ae75fb..7d317538f44 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -579,7 +579,7 @@ void psys_particle_on_dm(struct Mesh *mesh_final, /* particle_system.c */ void distribute_particles(struct ParticleSimulationData *sim, int from); -void initialize_particle(struct ParticleSimulationData *sim, struct ParticleData *pa); +void init_particle(struct ParticleSimulationData *sim, struct ParticleData *pa); void psys_calc_dmcache(struct Object *ob, struct Mesh *mesh_final, struct Mesh *mesh_original, diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh index c8e02062f0b..bcc84f9c9d0 100644 --- a/source/blender/blenkernel/BKE_persistent_data_handle.hh +++ b/source/blender/blenkernel/BKE_persistent_data_handle.hh @@ -84,45 +84,45 @@ class PersistentObjectHandle : public PersistentIDHandle { class PersistentDataHandleMap { private: - Map<int32_t, const ID *> id_by_handle_; - Map<const ID *, int32_t> handle_by_id_; + Map<int32_t, ID *> id_by_handle_; + Map<ID *, int32_t> handle_by_id_; public: - void add(int32_t handle, const ID &id) + void add(int32_t handle, ID &id) { BLI_assert(handle >= 0); handle_by_id_.add(&id, handle); id_by_handle_.add(handle, &id); } - PersistentIDHandle lookup(const ID *id) const + PersistentIDHandle lookup(ID *id) const { const int handle = handle_by_id_.lookup_default(id, -1); return PersistentIDHandle(handle); } - PersistentObjectHandle lookup(const Object *object) const + PersistentObjectHandle lookup(Object *object) const { - const int handle = handle_by_id_.lookup_default((const ID *)object, -1); + const int handle = handle_by_id_.lookup_default((ID *)object, -1); return PersistentObjectHandle(handle); } - const ID *lookup(const PersistentIDHandle &handle) const + ID *lookup(const PersistentIDHandle &handle) const { - const ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr); + ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr); return id; } - const Object *lookup(const PersistentObjectHandle &handle) const + Object *lookup(const PersistentObjectHandle &handle) const { - const ID *id = this->lookup((const PersistentIDHandle &)handle); + ID *id = this->lookup((const PersistentIDHandle &)handle); if (id == nullptr) { return nullptr; } if (GS(id->name) != ID_OB) { return nullptr; } - return (const Object *)id; + return (Object *)id; } }; diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 735d6dc6e89..edab543fc37 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -72,7 +72,7 @@ typedef struct SpaceType { /* Initial allocation, after this WM will call init() too. Some editors need * area and scene data (e.g. frame range) to set their initial scrolling. */ - struct SpaceLink *(*new)(const struct ScrArea *area, const struct Scene *scene); + struct SpaceLink *(*create)(const struct ScrArea *area, const struct Scene *scene); /* not free spacelink itself */ void (*free)(struct SpaceLink *sl); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 03f14be8772..b6d901c8ef2 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -627,6 +627,10 @@ void BKE_sequencer_color_balance_apply(struct StripColorBalance *cb, void BKE_sequencer_all_free_anim_ibufs(struct Scene *scene, int cfra); bool BKE_sequencer_check_scene_recursion(struct Scene *scene, struct ReportList *reports); bool BKE_sequencer_render_loop_check(struct Sequence *seq_main, struct Sequence *seq); +void BKE_sequencer_flag_for_removal(struct Scene *scene, + struct ListBase *seqbase, + struct Sequence *seq); +void BKE_sequencer_remove_flagged_sequences(struct Scene *scene, struct ListBase *seqbase); /* A debug and development function which checks whether sequences have unique UUIDs. * Errors will be reported to the console. */ diff --git a/source/blender/blenkernel/BKE_simulation.h b/source/blender/blenkernel/BKE_simulation.h index 98d1cf1e9b5..2c436f2bff8 100644 --- a/source/blender/blenkernel/BKE_simulation.h +++ b/source/blender/blenkernel/BKE_simulation.h @@ -31,6 +31,7 @@ void *BKE_simulation_add(struct Main *bmain, const char *name); void BKE_simulation_data_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Simulation *simulation); +void BKE_simulation_update_dependencies(struct Simulation *simulation, struct Main *bmain); SimulationState *BKE_simulation_state_add(Simulation *simulation, const char *type, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index d702da55ea8..ada341ff570 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -100,6 +100,7 @@ set(SRC intern/context.c intern/crazyspace.c intern/curve.c + intern/curve_bevel.c intern/curve_decimate.c intern/curve_deform.c intern/curveprofile.c @@ -134,6 +135,7 @@ set(SRC intern/idtype.c intern/image.c intern/image_gen.c + intern/image_gpu.c intern/image_save.c intern/ipo.c intern/kelvinlet.c diff --git a/source/blender/blenkernel/intern/CCGSubSurf.c b/source/blender/blenkernel/intern/CCGSubSurf.c index e035d5cbb68..8d8301362b3 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf.c +++ b/source/blender/blenkernel/intern/CCGSubSurf.c @@ -32,8 +32,6 @@ #include "CCGSubSurf.h" #include "CCGSubSurf_intern.h" -#include "GPU_glew.h" - /***/ int BKE_ccg_gridsize(int level) diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 8031e3dadf8..af4829691c2 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -80,11 +80,9 @@ //#define USE_MODIFIER_VALIDATE #ifdef USE_MODIFIER_VALIDATE -# define ASSERT_IS_VALID_DM(dm) (BLI_assert((dm == NULL) || (DM_is_valid(dm) == true))) # define ASSERT_IS_VALID_MESH(mesh) \ (BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true))) #else -# define ASSERT_IS_VALID_DM(dm) # define ASSERT_IS_VALID_MESH(mesh) #endif diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index ea5a4bd99d1..5b5e32f1d81 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1150,7 +1150,7 @@ static int nlaevalchan_validate_index(NlaEvalChannel *nec, int index) return 0; } -/* Initialise default values for NlaEvalChannel from the property data. */ +/* Initialize default values for NlaEvalChannel from the property data. */ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values) { PointerRNA *ptr = &nec->key.ptr; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 985be4ac99f..631ce4edd20 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2582,7 +2582,7 @@ void BKE_pose_where_is(struct Depsgraph *depsgraph, Scene *scene, Object *ob) } /* 2a. construct the IK tree (standard IK) */ - BIK_initialize_tree(depsgraph, scene, ob, ctime); + BIK_init_tree(depsgraph, scene, ob, ctime); /* 2b. construct the Spline IK trees * - this is not integrated as an IK plugin, since it should be able diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index d66991aed70..97c717572bc 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -658,7 +658,7 @@ void BKE_pose_eval_init_ik(struct Depsgraph *depsgraph, Scene *scene, Object *ob return; } /* construct the IK tree (standard IK) */ - BIK_initialize_tree(depsgraph, scene, object, ctime); + BIK_init_tree(depsgraph, scene, object, ctime); /* construct the Spline IK trees * - this is not integrated as an IK plugin, since it should be able * to function in conjunction with standard IK. */ diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 7223187831e..39fbea66637 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -557,7 +557,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INK); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INK; @@ -594,7 +594,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INKNOISE); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INKNOISE; @@ -631,7 +631,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_MARKER); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_MARKER; @@ -667,12 +667,12 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_CHISEL_SENSIVITY); custom_curve = brush->gpencil_settings->curve_strength; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_CHISEL_STRENGTH); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_CHISEL; @@ -2274,7 +2274,7 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool int half = side / 2; int i, j; - BKE_curvemapping_initialize(br->curve); + BKE_curvemapping_init(br->curve); texcache = BKE_brush_gen_texture_cache(br, half, secondary); im->rect_float = MEM_callocN(sizeof(float) * side * side, "radial control rect"); im->x = im->y = side; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index da9dab36044..9ad6ae84c5c 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -61,6 +61,8 @@ static void cache_file_init_data(ID *id) BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(cache_file, id)); cache_file->scale = 1.0f; + cache_file->velocity_unit = CACHEFILE_VELOCITY_UNIT_SECOND; + BLI_strncpy(cache_file->velocity_name, ".velocities", sizeof(cache_file->velocity_name)); } static void cache_file_copy_data(Main *UNUSED(bmain), diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index 4f4eb8f9f9d..fc326ffb390 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1202,7 +1202,7 @@ int BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap) return 0; } -void BKE_curvemapping_initialize(CurveMapping *cumap) +void BKE_curvemapping_init(CurveMapping *cumap) { int a; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 0627d2005d5..e126fb7f632 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -223,7 +223,7 @@ void BKE_curve_init(Curve *cu, const short curve_type) cu->vfont->id.us += 4; cu->str = MEM_malloc_arrayN(12, sizeof(unsigned char), "str"); BLI_strncpy(cu->str, "Text", 12); - cu->len = cu->len_wchar = cu->pos = 4; + cu->len = cu->len_char32 = cu->pos = 4; cu->strinfo = MEM_calloc_arrayN(12, sizeof(CharInfo), "strinfo new"); cu->totbox = cu->actbox = 1; cu->tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "textbox"); @@ -1726,205 +1726,6 @@ static void forward_diff_bezier_cotangent(const float p0[3], } } -/* ***************** BEVEL ****************** */ - -void BKE_curve_bevel_make(Object *ob, ListBase *disp) -{ - DispList *dl, *dlnew; - Curve *bevcu, *cu; - float *fp, facx, facy, angle, dangle; - int nr, a; - - cu = ob->data; - BLI_listbase_clear(disp); - - /* if a font object is being edited, then do nothing */ - // XXX if ( ob == obedit && ob->type == OB_FONT ) return; - - if (cu->bevobj) { - if (cu->bevobj->type != OB_CURVE) { - return; - } - - bevcu = cu->bevobj->data; - if (bevcu->ext1 == 0.0f && bevcu->ext2 == 0.0f) { - ListBase bevdisp = {NULL, NULL}; - facx = cu->bevobj->scale[0]; - facy = cu->bevobj->scale[1]; - - if (cu->bevobj->runtime.curve_cache) { - dl = cu->bevobj->runtime.curve_cache->disp.first; - } - else { - BLI_assert(cu->bevobj->runtime.curve_cache != NULL); - dl = NULL; - } - - while (dl) { - if (ELEM(dl->type, DL_POLY, DL_SEGM)) { - dlnew = MEM_mallocN(sizeof(DispList), "makebevelcurve1"); - *dlnew = *dl; - dlnew->verts = MEM_malloc_arrayN( - dl->parts * dl->nr, 3 * sizeof(float), "makebevelcurve1"); - memcpy(dlnew->verts, dl->verts, 3 * sizeof(float) * dl->parts * dl->nr); - - if (dlnew->type == DL_SEGM) { - dlnew->flag |= (DL_FRONT_CURVE | DL_BACK_CURVE); - } - - BLI_addtail(disp, dlnew); - fp = dlnew->verts; - nr = dlnew->parts * dlnew->nr; - while (nr--) { - fp[2] = fp[1] * facy; - fp[1] = -fp[0] * facx; - fp[0] = 0.0; - fp += 3; - } - } - dl = dl->next; - } - - BKE_displist_free(&bevdisp); - } - } - else if (cu->ext1 == 0.0f && cu->ext2 == 0.0f) { - /* pass */ - } - else if (cu->ext2 == 0.0f) { - dl = MEM_callocN(sizeof(DispList), "makebevelcurve2"); - dl->verts = MEM_malloc_arrayN(2, sizeof(float[3]), "makebevelcurve2"); - BLI_addtail(disp, dl); - dl->type = DL_SEGM; - dl->parts = 1; - dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; - dl->nr = 2; - - fp = dl->verts; - fp[0] = fp[1] = 0.0; - fp[2] = -cu->ext1; - fp[3] = fp[4] = 0.0; - fp[5] = cu->ext1; - } - else if ((cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) { - /* We make a full round bevel in that case. */ - - nr = 4 + 2 * cu->bevresol; - - dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p1"); - BLI_addtail(disp, dl); - dl->type = DL_POLY; - dl->parts = 1; - dl->flag = DL_BACK_CURVE; - dl->nr = nr; - - /* a circle */ - fp = dl->verts; - dangle = (2.0f * (float)M_PI / (nr)); - angle = -(nr - 1) * dangle; - - for (a = 0; a < nr; a++) { - fp[0] = 0.0; - fp[1] = (cosf(angle) * (cu->ext2)); - fp[2] = (sinf(angle) * (cu->ext2)) - cu->ext1; - angle += dangle; - fp += 3; - } - } - else { - /* The general case for nonzero extrusion or an incomplete loop. */ - dl = MEM_callocN(sizeof(DispList), "makebevelcurve"); - if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { - /* The full loop. */ - nr = 4 * cu->bevresol + 6; - dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; - } - else if ((cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) { - /* Half the loop. */ - nr = 2 * (cu->bevresol + 1) + ((cu->ext1 == 0.0f) ? 1 : 2); - dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; - } - else { - /* One quarter of the loop (just front or back). */ - nr = (cu->ext1 == 0.0f) ? cu->bevresol + 2 : cu->bevresol + 3; - dl->flag = (cu->flag & CU_FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE; - } - - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve"); - BLI_addtail(disp, dl); - /* Use a different type depending on whether the loop is complete or not. */ - dl->type = ((cu->flag & (CU_FRONT | CU_BACK)) == 0) ? DL_POLY : DL_SEGM; - dl->parts = 1; - dl->nr = nr; - - fp = dl->verts; - dangle = (float)M_PI_2 / (cu->bevresol + 1); - angle = 0.0; - - /* Build the back section. */ - if (cu->flag & CU_BACK || !(cu->flag & CU_FRONT)) { - angle = (float)M_PI_2 * 3.0f; - for (a = 0; a < cu->bevresol + 2; a++) { - fp[0] = 0.0; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; - angle += dangle; - fp += 3; - } - if ((cu->ext1 != 0.0f) && !(cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) { - /* Add the extrusion if we're only building the back. */ - fp[0] = 0.0; - fp[1] = cu->ext2; - fp[2] = cu->ext1; - } - } - - /* Build the front section. */ - if (cu->flag & CU_FRONT || !(cu->flag & CU_BACK)) { - if ((cu->ext1 != 0.0f) && !(cu->flag & CU_BACK) && (cu->flag & CU_FRONT)) { - /* Add the extrusion if we're only building the back. */ - fp[0] = 0.0; - fp[1] = cu->ext2; - fp[2] = -cu->ext1; - fp += 3; - } - /* Don't duplicate the last back vertex. */ - angle = (cu->ext1 == 0.0f && (cu->flag & CU_BACK)) ? dangle : 0; - int front_len = (cu->ext1 == 0.0f && ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT))) ? - cu->bevresol + 1 : - cu->bevresol + 2; - for (a = 0; a < front_len; a++) { - fp[0] = 0.0; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; - angle += dangle; - fp += 3; - } - } - - /* Build the other half only if we're building the full loop. */ - if (!(cu->flag & (CU_FRONT | CU_BACK))) { - for (a = 0; a < cu->bevresol + 1; a++) { - fp[0] = 0.0; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; - angle += dangle; - fp += 3; - } - - angle = (float)M_PI; - for (a = 0; a < cu->bevresol + 1; a++) { - fp[0] = 0.0; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; - angle += dangle; - fp += 3; - } - } - } -} - static int cu_isectLL(const float v1[3], const float v2[3], const float v3[3], @@ -5441,7 +5242,7 @@ void BKE_curve_material_index_remove(Curve *cu, int index) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr && info->mat_nr >= index) { info->mat_nr--; } @@ -5465,7 +5266,7 @@ bool BKE_curve_material_index_used(Curve *cu, int index) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr == index) { return true; } @@ -5491,7 +5292,7 @@ void BKE_curve_material_index_clear(Curve *cu) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { info->mat_nr = 0; } } @@ -5513,7 +5314,7 @@ bool BKE_curve_material_index_validate(Curve *cu) CharInfo *info = cu->strinfo; const int max_idx = max_ii(0, cu->totcol); /* OB_FONT use 1 as first mat index, not 0!!! */ int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr > max_idx) { info->mat_nr = 0; is_valid = false; @@ -5561,7 +5362,7 @@ void BKE_curve_material_remap(Curve *cu, const unsigned int *remap, unsigned int } else { strinfo = cu->strinfo; - charinfo_len = cu->len_wchar; + charinfo_len = cu->len_char32; } for (i = 0; i <= charinfo_len; i++) { diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c new file mode 100644 index 00000000000..7f23f0215cc --- /dev/null +++ b/source/blender/blenkernel/intern/curve_bevel.c @@ -0,0 +1,272 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + * + * Handle curve object data bevel options, + * both extruding + */ + +#include <string.h> + +#include "BLI_listbase.h" +#include "BLI_math_base.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BKE_curve.h" +#include "BKE_displist.h" + +typedef enum CurveBevelFillType { + BACK = 0, + FRONT, + HALF, + FULL, +} CurveBevelFillType; + +static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve) +{ + if (!(curve->flag & (CU_FRONT | CU_BACK))) { + return FULL; + } + if ((curve->flag & CU_FRONT) && (curve->flag & CU_BACK)) { + return HALF; + } + + return (curve->flag & CU_FRONT) ? FRONT : BACK; +} + +static void curve_bevel_make_extrude_and_fill(Curve *cu, + ListBase *disp, + const bool use_extrude, + const CurveBevelFillType fill_type) +{ + DispList *dl = MEM_callocN(sizeof(DispList), __func__); + + int nr; + if (fill_type == FULL) { + /* The full loop. */ + nr = 4 * cu->bevresol + 6; + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + } + else if (fill_type == HALF) { + /* Half the loop. */ + nr = 2 * (cu->bevresol + 1) + (use_extrude ? 2 : 1); + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + } + else { + /* One quarter of the loop (just front or back). */ + nr = use_extrude ? cu->bevresol + 3 : cu->bevresol + 2; + dl->flag = (fill_type == FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE; + } + + dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), __func__); + BLI_addtail(disp, dl); + /* Use a different type depending on whether the loop is complete or not. */ + dl->type = (fill_type == FULL) ? DL_POLY : DL_SEGM; + dl->parts = 1; + dl->nr = nr; + + float *fp = dl->verts; + const float dangle = (float)M_PI_2 / (cu->bevresol + 1); + float angle = 0.0f; + + /* Build the back section. */ + if (ELEM(fill_type, BACK, HALF, FULL)) { + angle = (float)M_PI_2 * 3.0f; + for (int i = 0; i < cu->bevresol + 2; i++) { + fp[0] = 0.0f; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; + angle += dangle; + fp += 3; + } + if (use_extrude && fill_type == BACK) { + /* Add the extrusion if we're only building the back. */ + fp[0] = 0.0f; + fp[1] = cu->ext2; + fp[2] = cu->ext1; + } + } + + /* Build the front section. */ + if (ELEM(fill_type, FRONT, HALF, FULL)) { + if (use_extrude && fill_type == FRONT) { + /* Add the extrusion if we're only building the front. */ + fp[0] = 0.0f; + fp[1] = cu->ext2; + fp[2] = -cu->ext1; + fp += 3; + } + /* Don't duplicate the last back vertex. */ + angle = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? dangle : 0; + int front_len = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? cu->bevresol + 1 : + cu->bevresol + 2; + for (int i = 0; i < front_len; i++) { + fp[0] = 0.0f; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + angle += dangle; + fp += 3; + } + } + + /* Build the other half only if we're building the full loop. */ + if (fill_type == FULL) { + for (int i = 0; i < cu->bevresol + 1; i++) { + fp[0] = 0.0f; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + angle += dangle; + fp += 3; + } + + angle = (float)M_PI; + for (int i = 0; i < cu->bevresol + 1; i++) { + fp[0] = 0.0f; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; + angle += dangle; + fp += 3; + } + } +} + +static void curve_bevel_make_full_circle(Curve *cu, ListBase *disp) +{ + const int nr = 4 + 2 * cu->bevresol; + + DispList *dl = MEM_callocN(sizeof(DispList), __func__); + dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), __func__); + BLI_addtail(disp, dl); + dl->type = DL_POLY; + dl->parts = 1; + dl->flag = DL_BACK_CURVE; + dl->nr = nr; + + float *fp = dl->verts; + const float dangle = (2.0f * (float)M_PI / (nr)); + float angle = -(nr - 1) * dangle; + + for (int i = 0; i < nr; i++) { + fp[0] = 0.0; + fp[1] = (cosf(angle) * (cu->ext2)); + fp[2] = (sinf(angle) * (cu->ext2)) - cu->ext1; + angle += dangle; + fp += 3; + } +} + +static void curve_bevel_make_only_extrude(Curve *cu, ListBase *disp) +{ + DispList *dl = MEM_callocN(sizeof(DispList), __func__); + dl->verts = MEM_malloc_arrayN(2, sizeof(float[3]), __func__); + BLI_addtail(disp, dl); + dl->type = DL_SEGM; + dl->parts = 1; + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + dl->nr = 2; + + float *fp = dl->verts; + fp[0] = fp[1] = 0.0; + fp[2] = -cu->ext1; + fp[3] = fp[4] = 0.0; + fp[5] = cu->ext1; +} + +static void curve_bevel_make_from_object(Curve *cu, ListBase *disp) +{ + if (cu->bevobj->type != OB_CURVE) { + return; + } + + Curve *bevcu = cu->bevobj->data; + if (bevcu->ext1 == 0.0f && bevcu->ext2 == 0.0f) { + ListBase bevdisp = {NULL, NULL}; + float facx = cu->bevobj->scale[0]; + float facy = cu->bevobj->scale[1]; + + DispList *dl; + if (cu->bevobj->runtime.curve_cache) { + dl = cu->bevobj->runtime.curve_cache->disp.first; + } + else { + BLI_assert(cu->bevobj->runtime.curve_cache != NULL); + dl = NULL; + } + + while (dl) { + if (ELEM(dl->type, DL_POLY, DL_SEGM)) { + DispList *dlnew = MEM_mallocN(sizeof(DispList), __func__); + *dlnew = *dl; + dlnew->verts = MEM_malloc_arrayN(dl->parts * dl->nr, 3 * sizeof(float), __func__); + memcpy(dlnew->verts, dl->verts, 3 * sizeof(float) * dl->parts * dl->nr); + + if (dlnew->type == DL_SEGM) { + dlnew->flag |= (DL_FRONT_CURVE | DL_BACK_CURVE); + } + + BLI_addtail(disp, dlnew); + float *fp = dlnew->verts; + int nr = dlnew->parts * dlnew->nr; + while (nr--) { + fp[2] = fp[1] * facy; + fp[1] = -fp[0] * facx; + fp[0] = 0.0; + fp += 3; + } + } + dl = dl->next; + } + + BKE_displist_free(&bevdisp); + } +} + +void BKE_curve_bevel_make(Object *ob, ListBase *disp) +{ + Curve *curve = ob->data; + + const bool use_extrude = curve->ext1 != 0.0f; + const bool use_bevel = curve->ext2 != 0.0f; + + BLI_listbase_clear(disp); + + if (curve->bevobj) { + curve_bevel_make_from_object(curve, disp); + } + else if (!(use_extrude || use_bevel)) { + /* Pass. */ + } + else if (use_extrude && !use_bevel) { + curve_bevel_make_only_extrude(curve, disp); + } + else { + CurveBevelFillType fill_type = curve_bevel_get_fill_type(curve); + + if (!use_extrude && fill_type == FULL) { + curve_bevel_make_full_circle(curve, disp); + } + else { + /* The general case for nonzero extrusion or an incomplete loop. */ + curve_bevel_make_extrude_and_fill(curve, disp, use_extrude, fill_type); + } + } +} diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c index 6919d4fa10f..0a41529aac1 100644 --- a/source/blender/blenkernel/intern/curveprofile.c +++ b/source/blender/blenkernel/intern/curveprofile.c @@ -886,7 +886,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, */ static void curveprofile_make_table(CurveProfile *profile) { - int n_samples = PROF_N_TABLE(profile->path_len); + int n_samples = PROF_TABLE_LEN(profile->path_len); CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1), "high-res table"); @@ -1040,7 +1040,7 @@ void BKE_curveprofile_update(CurveProfile *profile, const int update_flags) * Also sets the number of segments used for the display preview of the locations * of the sampled points. */ -void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len) +void BKE_curveprofile_init(CurveProfile *profile, short segments_len) { if (segments_len != profile->segments_len) { profile->flag |= PROF_DIRTY_PRESET; @@ -1055,11 +1055,11 @@ void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len) * Gives the distance to the next point in the widgets sampled table, in other words the length * of the \a 'i' edge of the table. * - * \note Requires curveprofile_initialize or #BKE_curveprofile_update call before to fill table. + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. */ static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i) { - BLI_assert(i < PROF_N_TABLE(profile->path_len)); + BLI_assert(i < PROF_TABLE_LEN(profile->path_len)); return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); } @@ -1067,12 +1067,12 @@ static float curveprofile_distance_to_next_table_point(const CurveProfile *profi /** * Calculates the total length of the profile from the curves sampled in the table. * - * \note Requires curveprofile_initialize or #BKE_curveprofile_update call before to fill table. + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. */ float BKE_curveprofile_total_length(const CurveProfile *profile) { float total_length = 0; - for (int i = 0; i < PROF_N_TABLE(profile->path_len) - 1; i++) { + for (int i = 0; i < PROF_TABLE_LEN(profile->path_len) - 1; i++) { total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); } return total_length; @@ -1082,7 +1082,7 @@ float BKE_curveprofile_total_length(const CurveProfile *profile) * Samples evenly spaced positions along the curve profile's table (generated from path). Fills * an entire table at once for a speedup if all of the results are going to be used anyway. * - * \note Requires curveprofile_initialize or #BKE_curveprofile_update call before to fill table. + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. * \note Working, but would conflict with "Sample Straight Edges" option, so this is unused for * now. */ @@ -1145,7 +1145,7 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, * Travels down (length_portion * path) length and returns the position at that point. * * \param length_portion: The portion (0 to 1) of the path's full length to sample at. - * \note Requires curveprofile_initialize or #BKE_curveprofile_update call before to fill table. + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. */ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float length_portion, @@ -1160,7 +1160,7 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float length_travelled = 0.0f; while (length_travelled < requested_length) { /* Check if we reached the last point before the final one. */ - if (i == PROF_N_TABLE(profile->path_len) - 2) { + if (i == PROF_TABLE_LEN(profile->path_len) - 2) { break; } float new_length = curveprofile_distance_to_next_table_point(profile, i); diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 707db46a856..7ddb0a6862d 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -2643,11 +2643,13 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, } if (alloctype == CD_DUPLICATE && layerdata) { - if (typeInfo->copy) { - typeInfo->copy(layerdata, newlayerdata, totelem); - } - else { - memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size); + if (totelem > 0) { + if (typeInfo->copy) { + typeInfo->copy(layerdata, newlayerdata, totelem); + } + else { + memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size); + } } } else if (alloctype == CD_DEFAULT) { diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c index 6fcaf84d4ca..897fc7e692b 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.c +++ b/source/blender/blenkernel/intern/editmesh_tangent.c @@ -274,8 +274,8 @@ static void emDM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), vo /** * \see #BKE_mesh_calc_loop_tangent, same logic but used arrays instead of #BMesh data. * - * \note This function is not so normal, its using `bm->ldata` as input, - * but output's to `dm->loopData`. + * \note This function is not so normal, its using #BMesh.ldata as input, + * but output's to #Mesh.ldata. * This is done because #CD_TANGENT is cache data used only for drawing. */ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c index 10d804f437e..87cb77930f5 100644 --- a/source/blender/blenkernel/intern/fcurve_driver.c +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -21,12 +21,6 @@ * \ingroup bke */ -// #include <float.h> -// #include <math.h> -// #include <stddef.h> -// #include <stdio.h> -// #include <string.h> - #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" @@ -66,17 +60,19 @@ static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER; static CLG_LogRef LOG = {"bke.fcurve"}; -/* Driver Variables --------------------------- */ +/* -------------------------------------------------------------------- */ +/** \name Driver Variables + * \{ */ /* TypeInfo for Driver Variables (dvti) */ typedef struct DriverVarTypeInfo { - /* evaluation callback */ + /* Evaluation callback. */ float (*get_value)(ChannelDriver *driver, DriverVar *dvar); - /* allocation of target slots */ - int num_targets; /* number of target slots required */ - const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ - short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ + /* Allocation of target slots. */ + int num_targets; /* Number of target slots required. */ + const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots. */ + short target_flags[MAX_DRIVER_TARGETS]; /* Flags defining the requirements for each slot. */ } DriverVarTypeInfo; /* Macro to begin definitions */ @@ -85,7 +81,11 @@ typedef struct DriverVarTypeInfo { /* Macro to end definitions */ #define END_DVAR_TYPEDEF } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Target Utilities + * \{ */ static ID *dtar_id_ensure_proxy_from(ID *id) { @@ -107,14 +107,14 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) int index = -1; float value = 0.0f; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dtar)) { return 0.0f; } id = dtar_id_ensure_proxy_from(dtar->id); - /* error check for missing pointer... */ + /* Error check for missing pointer. */ if (id == NULL) { if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); @@ -125,12 +125,12 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) return 0.0f; } - /* get RNA-pointer for the ID-block given in target */ + /* Get RNA-pointer for the ID-block given in target. */ RNA_id_pointer_create(id, &id_ptr); - /* get property to read from, and get value as appropriate */ + /* Get property to read from, and get value as appropriate. */ if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* path couldn't be resolved */ + /* Path couldn't be resolved. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: cannot resolve target for %s -> %s", @@ -144,9 +144,9 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } if (RNA_property_array_check(prop)) { - /* array */ + /* Array. */ if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) { - /* out of bounds */ + /* Out of bounds. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", @@ -175,7 +175,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } } else { - /* not an array */ + /* Not an array. */ switch (RNA_property_type(prop)) { case PROP_BOOLEAN: value = (float)RNA_property_boolean_get(&ptr, prop); @@ -194,7 +194,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } } - /* if we're still here, we should be ok... */ + /* If we're still here, we should be ok. */ dtar->flag &= ~DTAR_FLAG_INVALID; return value; } @@ -214,14 +214,14 @@ bool driver_get_variable_property(ChannelDriver *driver, ID *id; int index = -1; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dtar)) { return false; } id = dtar_id_ensure_proxy_from(dtar->id); - /* error check for missing pointer... */ + /* Error check for missing pointer. */ if (id == NULL) { if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); @@ -232,19 +232,19 @@ bool driver_get_variable_property(ChannelDriver *driver, return false; } - /* get RNA-pointer for the ID-block given in target */ + /* Get RNA-pointer for the ID-block given in target. */ RNA_id_pointer_create(id, &id_ptr); - /* get property to read from, and get value as appropriate */ + /* Get property to read from, and get value as appropriate. */ if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') { ptr = PointerRNA_NULL; - prop = NULL; /* ok */ + prop = NULL; /* OK. */ } else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* ok */ + /* OK. */ } else { - /* path couldn't be resolved */ + /* Path couldn't be resolved. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: cannot resolve target for %s -> %s", @@ -265,7 +265,7 @@ bool driver_get_variable_property(ChannelDriver *driver, *r_prop = prop; *r_index = index; - /* if we're still here, we should be ok... */ + /* If we're still here, we should be ok. */ dtar->flag &= ~DTAR_FLAG_INVALID; return true; } @@ -277,14 +277,14 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - /* check if this target has valid data */ + /* Check if this target has valid data. */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ + /* Invalid target, so will not have enough targets. */ driver->flag |= DRIVER_FLAG_INVALID; dtar->flag |= DTAR_FLAG_INVALID; } else { - /* target seems to be OK now... */ + /* Target seems to be OK now. */ dtar->flag &= ~DTAR_FLAG_INVALID; valid_targets++; } @@ -294,21 +294,25 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) return valid_targets; } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Variable Utilities + * \{ */ -/* evaluate 'single prop' driver variable */ +/* Evaluate 'single prop' driver variable. */ static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar) { - /* just evaluate the first target slot */ + /* Just evaluate the first target slot. */ return dtar_get_prop_val(driver, &dvar->targets[0]); } -/* evaluate 'rotation difference' driver variable */ +/* Evaluate 'rotation difference' driver variable. */ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) { short valid_targets = driver_check_valid_targets(driver, dvar); - /* make sure we have enough valid targets to use - all or nothing for now... */ + /* Make sure we have enough valid targets to use - all or nothing for now. */ if (driver_check_valid_targets(driver, dvar) != 2) { if (G.debug & G_DEBUG) { CLOG_WARN(&LOG, @@ -324,31 +328,31 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) /* NOTE: for now, these are all just worldspace */ for (int i = 0; i < 2; i++) { - /* get pointer to loc values to store in */ + /* Get pointer to loc values to store in. */ DriverTarget *dtar = &dvar->targets[i]; Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); bPoseChannel *pchan; - /* after the checks above, the targets should be valid here... */ + /* After the checks above, the targets should be valid here. */ BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - /* try to get posechannel */ + /* Try to get pose-channel. */ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - /* check if object or bone */ + /* Check if object or bone. */ if (pchan) { - /* bone */ + /* Bone. */ mat[i] = pchan->pose_mat; } else { - /* object */ + /* Object. */ mat[i] = ob->obmat; } } float q1[4], q2[4], quat[4], angle; - /* use the final posed locations */ + /* Use the final posed locations. */ mat4_to_quat(q1, mat[0]); mat4_to_quat(q2, mat[1]); @@ -360,15 +364,18 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle); } -/* evaluate 'location difference' driver variable */ -/* TODO: this needs to take into account space conversions... */ +/** + * Evaluate 'location difference' driver variable. + * + * TODO: this needs to take into account space conversions. + */ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) { float loc1[3] = {0.0f, 0.0f, 0.0f}; float loc2[3] = {0.0f, 0.0f, 0.0f}; short valid_targets = driver_check_valid_targets(driver, dvar); - /* make sure we have enough valid targets to use - all or nothing for now... */ + /* Make sure we have enough valid targets to use - all or nothing for now. */ if (valid_targets < dvar->num_targets) { if (G.debug & G_DEBUG) { CLOG_WARN(&LOG, @@ -381,72 +388,72 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) } /* SECOND PASS: get two location values */ - /* NOTE: for now, these are all just worldspace */ + /* NOTE: for now, these are all just world-space */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - /* get pointer to loc values to store in */ + /* Get pointer to loc values to store in. */ Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); bPoseChannel *pchan; float tmp_loc[3]; - /* after the checks above, the targets should be valid here... */ + /* After the checks above, the targets should be valid here. */ BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - /* try to get posechannel */ + /* Try to get pose-channel. */ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - /* check if object or bone */ + /* Check if object or bone. */ if (pchan) { - /* bone */ + /* Bone. */ if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { float mat[4][4]; - /* extract transform just like how the constraints do it! */ + /* Extract transform just like how the constraints do it! */ copy_m4_m4(mat, pchan->pose_mat); BKE_constraint_mat_convertspace( ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); - /* ... and from that, we get our transform */ + /* ... and from that, we get our transform. */ copy_v3_v3(tmp_loc, mat[3]); } else { - /* transform space (use transform values directly) */ + /* Transform space (use transform values directly). */ copy_v3_v3(tmp_loc, pchan->loc); } } else { - /* convert to worldspace */ + /* Convert to worldspace. */ copy_v3_v3(tmp_loc, pchan->pose_head); mul_m4_v3(ob->obmat, tmp_loc); } } else { - /* object */ + /* Object. */ if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* XXX: this should practically be the same as transform space... */ + /* XXX: this should practically be the same as transform space. */ float mat[4][4]; - /* extract transform just like how the constraints do it! */ + /* Extract transform just like how the constraints do it! */ copy_m4_m4(mat, ob->obmat); BKE_constraint_mat_convertspace( ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); - /* ... and from that, we get our transform */ + /* ... and from that, we get our transform. */ copy_v3_v3(tmp_loc, mat[3]); } else { - /* transform space (use transform values directly) */ + /* Transform space (use transform values directly). */ copy_v3_v3(tmp_loc, ob->loc); } } else { - /* worldspace */ + /* World-space. */ copy_v3_v3(tmp_loc, ob->obmat[3]); } } - /* copy the location to the right place */ + /* Copy the location to the right place. */ if (tarIndex) { copy_v3_v3(loc2, tmp_loc); } @@ -456,13 +463,14 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) } DRIVER_TARGETS_LOOPER_END; - /* if we're still here, there should now be two targets to use, - * so just take the length of the vector between these points - */ + /* If we're still here, there should now be two targets to use, + * so just take the length of the vector between these points. */ return len_v3v3(loc1, loc2); } -/* evaluate 'transform channel' driver variable */ +/** + * Evaluate 'transform channel' driver variable. + */ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) { DriverTarget *dtar = &dvar->targets[0]; @@ -473,15 +481,15 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) bool use_eulers = false; short rot_order = ROT_MODE_EUL; - /* check if this target has valid data */ + /* Check if this target has valid data. */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ + /* Invalid target, so will not have enough targets. */ driver->flag |= DRIVER_FLAG_INVALID; dtar->flag |= DTAR_FLAG_INVALID; return 0.0f; } else { - /* target should be valid now */ + /* Target should be valid now. */ dtar->flag &= ~DTAR_FLAG_INVALID; } @@ -495,7 +503,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) * but #DTAR_FLAG_LOCAL_CONSTS is for all the common "corrective-shapes-for-limbs" situations. */ if (pchan) { - /* bone */ + /* Bone. */ if (pchan->rotmode > 0) { copy_v3_v3(oldEul, pchan->eul); rot_order = pchan->rotmode; @@ -504,16 +512,15 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ + /* Just like how the constraints do it! */ copy_m4_m4(mat, pchan->pose_mat); BKE_constraint_mat_convertspace( ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); } else { - /* specially calculate local matrix, since chan_mat is not valid + /* Specially calculate local matrix, since chan_mat is not valid * since it stores delta transform of pose_mat so that deforms work - * so it cannot be used here for "transform" space - */ + * so it cannot be used here for "transform" space. */ BKE_pchan_to_mat4(pchan, mat); } } @@ -523,7 +530,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) } } else { - /* object */ + /* Object. */ if (ob->rotmode > 0) { copy_v3_v3(oldEul, ob->rot); rot_order = ob->rotmode; @@ -532,25 +539,25 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ + /* Just like how the constraints do it! */ copy_m4_m4(mat, ob->obmat); BKE_constraint_mat_convertspace( ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); } else { - /* transforms to matrix */ + /* Transforms to matrix. */ BKE_object_to_mat4(ob, mat); } } else { - /* worldspace matrix - just the good-old one */ + /* World-space matrix - just the good-old one. */ copy_m4_m4(mat, ob->obmat); } } - /* check which transform */ + /* Check which transform. */ if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) { - /* not valid channel */ + /* Not valid channel. */ return 0.0f; } else if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) { @@ -567,7 +574,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]); } else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) { - /* extract rotation as eulers (if needed) + /* Extract rotation as eulers (if needed) * - definitely if rotation order isn't eulers already * - if eulers, then we have 2 options: * a) decompose transform matrix as required, then try to make eulers from @@ -596,7 +603,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) return quat[channel]; } else { - /* extract location and choose right axis */ + /* Extract location and choose right axis. */ return mat[3][dtar->transChan]; } } @@ -666,41 +673,45 @@ void BKE_driver_target_matrix_to_rot_channels( } } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Variable Type Info + * \{ */ /* Table of Driver Variable Type Info Data */ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = { - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */ - 1, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* Eval callback. */ + 1, /* Number of targets used. */ {"Property"}, /* UI names for targets */ - {0} /* flags */ + {0} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */ - 2, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* Eval callback. */ + 2, /* Number of targets used. */ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, - DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */ - 2, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* Eval callback. */ + 2, /* Number of targets used. */ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, - DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */ - 1, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* Eval callback. */ + 1, /* Number of targets used. */ {"Object/Bone"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, }; /* Get driver variable typeinfo */ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) { - /* check if valid type */ + /* Check if valid type. */ if ((type >= 0) && (type < MAX_DVAR_TYPES)) { return &dvar_types[type]; } @@ -709,40 +720,44 @@ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) } } -/* Driver API --------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver API + * \{ */ /* Perform actual freeing driver variable and remove it from the given list */ void driver_free_variable(ListBase *variables, DriverVar *dvar) { - /* sanity checks */ + /* Sanity checks. */ if (dvar == NULL) { return; } - /* free target vars + /* Free target vars: * - need to go over all of them, not just up to the ones that are used * currently, since there may be some lingering RNA paths from * previous users needing freeing */ DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* free RNA path if applicable */ + /* Free RNA path if applicable. */ if (dtar->rna_path) { MEM_freeN(dtar->rna_path); } } DRIVER_TARGETS_LOOPER_END; - /* remove the variable from the driver */ + /* Remove the variable from the driver. */ BLI_freelinkN(variables, dvar); } /* Free the driver variable and do extra updates */ void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar) { - /* remove and free the driver variable */ + /* Remove and free the driver variable. */ driver_free_variable(&driver->variables, dvar); - /* since driver variables are cached, the expression needs re-compiling too */ + /* Since driver variables are cached, the expression needs re-compiling too. */ BKE_driver_invalidate_expression(driver, false, true); } @@ -753,9 +768,9 @@ void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars) BLI_duplicatelist(dst_vars, src_vars); LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) { - /* need to go over all targets so that we don't leave any dangling paths */ + /* Need to go over all targets so that we don't leave any dangling paths. */ DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* make a copy of target's rna path if available */ + /* Make a copy of target's rna path if available. */ if (dtar->rna_path) { dtar->rna_path = MEM_dupallocN(dtar->rna_path); } @@ -769,25 +784,24 @@ void driver_change_variable_type(DriverVar *dvar, int type) { const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type); - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, dvar, dvti)) { return; } - /* set the new settings */ + /* Set the new settings. */ dvar->type = type; dvar->num_targets = dvti->num_targets; - /* make changes to the targets based on the defines for these types - * NOTE: only need to make sure the ones we're using here are valid... - */ + /* Make changes to the targets based on the defines for these types. + * NOTE: only need to make sure the ones we're using here are valid. */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { short flags = dvti->target_flags[tarIndex]; - /* store the flags */ + /* Store the flags. */ dtar->flag = flags; - /* object ID types only, or idtype not yet initialized */ + /* Object ID types only, or idtype not yet initialized. */ if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) { dtar->idtype = ID_OB; } @@ -804,12 +818,12 @@ void driver_variable_name_validate(DriverVar *dvar) '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r', }; - /* sanity checks */ + /* Sanity checks. */ if (dvar == NULL) { return; } - /* clear all invalid-name flags */ + /* Clear all invalid-name flags. */ dvar->flag &= ~DVAR_ALL_INVALID_FLAGS; /* 0) Zero-length identifiers are not allowed */ @@ -870,16 +884,16 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver) { DriverVar *dvar; - /* sanity checks */ + /* Sanity checks. */ if (driver == NULL) { return NULL; } - /* make a new variable */ + /* Make a new variable. */ dvar = MEM_callocN(sizeof(DriverVar), "DriverVar"); BLI_addtail(&driver->variables, dvar); - /* give the variable a 'unique' name */ + /* Give the variable a 'unique' name. */ strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var")); BLI_uniquename(&driver->variables, dvar, @@ -888,13 +902,13 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver) offsetof(DriverVar, name), sizeof(dvar->name)); - /* set the default type to 'single prop' */ + /* Set the default type to 'single prop'. */ driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP); - /* since driver variables are cached, the expression needs re-compiling too */ + /* Since driver variables are cached, the expression needs re-compiling too. */ BKE_driver_invalidate_expression(driver, false, true); - /* return the target */ + /* Return the target. */ return dvar; } @@ -904,20 +918,20 @@ void fcurve_free_driver(FCurve *fcu) ChannelDriver *driver; DriverVar *dvar, *dvarn; - /* sanity checks */ + /* Sanity checks. */ if (ELEM(NULL, fcu, fcu->driver)) { return; } driver = fcu->driver; - /* free driver targets */ + /* Free driver targets. */ for (dvar = driver->variables.first; dvar; dvar = dvarn) { dvarn = dvar->next; driver_free_variable_ex(driver, dvar); } #ifdef WITH_PYTHON - /* free compiled driver expression */ + /* Free compiled driver expression. */ if (driver->expr_comp) { BPY_DECREF(driver->expr_comp); } @@ -936,27 +950,31 @@ ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver) { ChannelDriver *ndriver; - /* sanity checks */ + /* Sanity checks. */ if (driver == NULL) { return NULL; } - /* copy all data */ + /* Copy all data. */ ndriver = MEM_dupallocN(driver); ndriver->expr_comp = NULL; ndriver->expr_simple = NULL; - /* copy variables */ + /* Copy variables. */ - /* to get rid of refs to non-copied data (that's still used on original) */ + /* To get rid of refs to non-copied data (that's still used on original). */ BLI_listbase_clear(&ndriver->variables); driver_variables_copy(&ndriver->variables, &driver->variables); - /* return the new driver */ + /* Return the new driver. */ return ndriver; } -/* Driver Expression Evaluation --------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Expression Evaluation + * \{ */ /* Index constants for the expression parameter array. */ enum { @@ -1026,7 +1044,7 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver, return true; default: - /* arriving here means a bug, not user error */ + /* Arriving here means a bug, not user error. */ CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression); return false; } @@ -1135,22 +1153,25 @@ void BKE_driver_invalidate_expression(ChannelDriver *driver, #endif } -/* Driver Evaluation -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Evaluation + * \{ */ /* Evaluate a Driver Variable to get a value that contributes to the final */ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar) { const DriverVarTypeInfo *dvti; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dvar)) { return 0.0f; } - /* call the relevant callbacks to get the variable value + /* Call the relevant callbacks to get the variable value * using the variable type info, storing the obtained value - * in dvar->curval so that drivers can be debugged - */ + * in `dvar->curval` so that drivers can be debugged. */ dvti = get_dvar_typeinfo(dvar->type); if (dvti && dvti->get_value) { @@ -1167,25 +1188,25 @@ static void evaluate_driver_sum(ChannelDriver *driver) { DriverVar *dvar; - /* check how many variables there are first (i.e. just one?) */ + /* Check how many variables there are first (i.e. just one?). */ if (BLI_listbase_is_single(&driver->variables)) { - /* just one target, so just use that */ + /* Just one target, so just use that. */ dvar = driver->variables.first; driver->curval = driver_get_variable_value(driver, dvar); return; } - /* more than one target, so average the values of the targets */ + /* More than one target, so average the values of the targets. */ float value = 0.0f; int tot = 0; - /* loop through targets, adding (hopefully we don't get any overflow!) */ + /* Loop through targets, adding (hopefully we don't get any overflow!). */ for (dvar = driver->variables.first; dvar; dvar = dvar->next) { value += driver_get_variable_value(driver, dvar); tot++; } - /* perform operations on the total if appropriate */ + /* Perform operations on the total if appropriate. */ if (driver->type == DRIVER_TYPE_AVERAGE) { driver->curval = tot ? (value / (float)tot) : 0.0f; } @@ -1199,34 +1220,34 @@ static void evaluate_driver_min_max(ChannelDriver *driver) DriverVar *dvar; float value = 0.0f; - /* loop through the variables, getting the values and comparing them to existing ones */ + /* Loop through the variables, getting the values and comparing them to existing ones. */ for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - /* get value */ + /* Get value. */ float tmp_val = driver_get_variable_value(driver, dvar); - /* store this value if appropriate */ + /* Store this value if appropriate. */ if (dvar->prev) { - /* check if greater/smaller than the baseline */ + /* Check if greater/smaller than the baseline. */ if (driver->type == DRIVER_TYPE_MAX) { - /* max? */ + /* Max? */ if (tmp_val > value) { value = tmp_val; } } else { - /* min? */ + /* Min? */ if (tmp_val < value) { value = tmp_val; } } } else { - /* first item - make this the baseline for comparisons */ + /* First item - make this the baseline for comparisons. */ value = tmp_val; } } - /* store value in driver */ + /* Store value in driver. */ driver->curval = value; } @@ -1235,62 +1256,63 @@ static void evaluate_driver_python(PathResolvedRNA *anim_rna, ChannelDriver *driver_orig, const AnimationEvalContext *anim_eval_context) { - /* check for empty or invalid expression */ + /* Check for empty or invalid expression. */ if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) { driver->curval = 0.0f; } else if (!driver_try_evaluate_simple_expr( driver, driver_orig, &driver->curval, anim_eval_context->eval_time)) { #ifdef WITH_PYTHON - /* this evaluates the expression using Python, and returns its result: - * - on errors it reports, then returns 0.0f - */ + /* This evaluates the expression using Python, and returns its result: + * - on errors it reports, then returns 0.0f. */ BLI_mutex_lock(&python_driver_lock); driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, anim_eval_context); BLI_mutex_unlock(&python_driver_lock); -#else /* WITH_PYTHON*/ +#else /* WITH_PYTHON */ UNUSED_VARS(anim_rna, anim_eval_context); -#endif /* WITH_PYTHON*/ +#endif /* WITH_PYTHON */ } } -/* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime" - * - "evaltime" is the frame at which F-Curve is being evaluated - * - has to return a float value - * - driver_orig is where we cache Python expressions, in case of COW +/** + * Evaluate an Channel-Driver to get a 'time' value to use + * instead of `anim_eval_context->eval_time`. + * + * - `anim_eval_context->eval_time` is the frame at which F-Curve is being evaluated. + * - Has to return a float value. + * - \a driver_orig is where we cache Python expressions, in case of COW */ float evaluate_driver(PathResolvedRNA *anim_rna, ChannelDriver *driver, ChannelDriver *driver_orig, const AnimationEvalContext *anim_eval_context) { - /* check if driver can be evaluated */ + /* Check if driver can be evaluated. */ if (driver_orig->flag & DRIVER_FLAG_INVALID) { return 0.0f; } switch (driver->type) { - case DRIVER_TYPE_AVERAGE: /* average values of driver targets */ - case DRIVER_TYPE_SUM: /* sum values of driver targets */ + case DRIVER_TYPE_AVERAGE: /* Average values of driver targets. */ + case DRIVER_TYPE_SUM: /* Sum values of driver targets. */ evaluate_driver_sum(driver); break; - case DRIVER_TYPE_MIN: /* smallest value */ - case DRIVER_TYPE_MAX: /* largest value */ + case DRIVER_TYPE_MIN: /* Smallest value. */ + case DRIVER_TYPE_MAX: /* Largest value. */ evaluate_driver_min_max(driver); break; - case DRIVER_TYPE_PYTHON: /* expression */ + case DRIVER_TYPE_PYTHON: /* Expression. */ evaluate_driver_python(anim_rna, driver, driver_orig, anim_eval_context); break; default: - /* special 'hack' - just use stored value + /* Special 'hack' - just use stored value * This is currently used as the mechanism which allows animated settings to be able - * to be changed via the UI. - */ + * to be changed via the UI. */ break; } - /* return value for driver */ + /* Return value for driver. */ return driver->curval; } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 079b436a3ea..2245af31f0d 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1119,6 +1119,7 @@ static void ensure_obstaclefields(FluidDomainSettings *fds) if (fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE) { manta_ensure_guiding(fds->fluid, fds->fmd); } + manta_update_pointers(fds->fluid, fds->fmd, false); } static void update_obstacleflags(FluidDomainSettings *fds, @@ -2596,7 +2597,7 @@ static void ensure_flowsfields(FluidDomainSettings *fds) manta_smoke_ensure_fire(fds->fluid, fds->fmd); } if (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) { - /* initialize all smoke with "active_color" */ + /* Initialize all smoke with "active_color". */ manta_smoke_ensure_colors(fds->fluid, fds->fmd); } if (fds->type == FLUID_DOMAIN_TYPE_LIQUID && @@ -2605,6 +2606,7 @@ static void ensure_flowsfields(FluidDomainSettings *fds) fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER)) { manta_liquid_ensure_sndparts(fds->fluid, fds->fmd); } + manta_update_pointers(fds->fluid, fds->fmd, false); } static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int numflowobj) @@ -2617,7 +2619,7 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n FLUID_DOMAIN_ACTIVE_HEAT | FLUID_DOMAIN_ACTIVE_FIRE); active_fields &= ~prev_flags; - /* Monitor active fields based on flow settings */ + /* Monitor active fields based on flow settings. */ for (flow_index = 0; flow_index < numflowobj; flow_index++) { Object *flow_ob = flowobjs[flow_index]; FluidModifierData *fmd2 = (FluidModifierData *)BKE_modifiers_findby_type(flow_ob, @@ -2628,6 +2630,7 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n continue; } + /* Activate specific grids if at least one flow object requires this grid. */ if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) { FluidFlowSettings *ffs = fmd2->flow; if (!ffs) { @@ -2648,17 +2651,17 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n continue; } - /* activate heat field if flow produces any heat */ - if (ffs->temperature) { + /* Activate heat field if a flow object produces any heat. */ + if (ffs->temperature != 0.0) { active_fields |= FLUID_DOMAIN_ACTIVE_HEAT; } - /* activate fuel field if flow adds any fuel */ - if (ffs->fuel_amount && - (ffs->type == FLUID_FLOW_TYPE_FIRE || ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) { + /* Activate fuel field if a flow object is of fire type. */ + if (ffs->fuel_amount != 0.0 || ffs->type == FLUID_FLOW_TYPE_FIRE || + ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE) { active_fields |= FLUID_DOMAIN_ACTIVE_FIRE; } - /* activate color field if flows add smoke with varying colors */ - if (ffs->density && + /* Activate color field if flows add smoke with varying colors. */ + if (ffs->density != 0.0 && (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) { if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) { copy_v3_v3(fds->active_color, ffs->color); @@ -2671,11 +2674,11 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n } } } - /* Monitor active fields based on domain settings */ + /* Monitor active fields based on domain settings. */ if (fds->type == FLUID_DOMAIN_TYPE_GAS && active_fields & FLUID_DOMAIN_ACTIVE_FIRE) { - /* heat is always needed for fire */ + /* Heat is always needed for fire. */ active_fields |= FLUID_DOMAIN_ACTIVE_HEAT; - /* also activate colors if domain smoke color differs from active color */ + /* Also activate colors if domain smoke color differs from active color. */ if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) { copy_v3_v3(fds->active_color, fds->flame_smoke_color); active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET; @@ -2924,8 +2927,21 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, float *velx_initial = manta_get_in_velocity_x(fds->fluid); float *vely_initial = manta_get_in_velocity_y(fds->fluid); float *velz_initial = manta_get_in_velocity_z(fds->fluid); - uint z; + float *forcex = manta_get_force_x(fds->fluid); + float *forcey = manta_get_force_y(fds->fluid); + float *forcez = manta_get_force_z(fds->fluid); + + BLI_assert(forcex && forcey && forcez); + + /* Either all or no components have to exist. */ + BLI_assert((color_r && color_g && color_b) || (!color_r && !color_g && !color_b)); + BLI_assert((color_r_in && color_g_in && color_b_in) || + (!color_r_in && !color_g_in && !color_b_in)); + BLI_assert((velx_initial && vely_initial && velz_initial) || + (!velx_initial && !vely_initial && !velz_initial)); + + uint z; /* Grid reset before writing again. */ for (z = 0; z < fds->res[0] * fds->res[1] * fds->res[2]; z++) { /* Only reset static phi on first frame, dynamic phi gets reset every time. */ @@ -2949,7 +2965,7 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, if (heat_in) { heat_in[z] = heat[z]; } - if (color_r_in) { + if (color_r_in && color_g_in && color_b_in) { color_r_in[z] = color_r[z]; color_g_in[z] = color_b[z]; color_b_in[z] = color_g[z]; @@ -2961,11 +2977,15 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, if (emission_in) { emission_in[z] = 0.0f; } - if (velx_initial) { + if (velx_initial && vely_initial && velz_initial) { velx_initial[z] = 0.0f; vely_initial[z] = 0.0f; velz_initial[z] = 0.0f; } + /* Reset forces here as update_effectors() is skipped when no external forces are present. */ + forcex[z] = 0.0f; + forcey[z] = 0.0f; + forcez[z] = 0.0f; } /* Apply emission data for every flow object. */ @@ -3149,13 +3169,13 @@ static void update_effectors_task_cb(void *__restrict userdata, continue; } - /* get velocities from manta grid space and convert to blender units */ + /* Get velocities from manta grid space and convert to blender units. */ vel[0] = data->velocity_x[index]; vel[1] = data->velocity_y[index]; vel[2] = data->velocity_z[index]; mul_v3_fl(vel, fds->dx); - /* convert vel to global space */ + /* Convert vel to global space. */ mag = len_v3(vel); mul_mat3_m4_v3(fds->obmat, vel); normalize_v3(vel); @@ -3166,18 +3186,18 @@ static void update_effectors_task_cb(void *__restrict userdata, voxel_center[2] = fds->p0[2] + fds->cell_size[2] * ((float)(z + fds->res_min[2]) + 0.5f); mul_m4_v3(fds->obmat, voxel_center); - /* do effectors */ + /* Do effectors. */ pd_point_from_loc(data->scene, voxel_center, vel, index, &epoint); BKE_effectors_apply( data->effectors, NULL, fds->effector_weights, &epoint, retvel, NULL, NULL); - /* convert retvel to local space */ + /* Convert retvel to local space. */ mag = len_v3(retvel); mul_mat3_m4_v3(fds->imat, retvel); normalize_v3(retvel); mul_v3_fl(retvel, mag); - /* constrain forces to interval -1 to 1 */ + /* Constrain forces to interval -1 to 1. */ data->force_x[index] = min_ff(max_ff(-1.0f, retvel[0] * 0.2f), 1.0f); data->force_y[index] = min_ff(max_ff(-1.0f, retvel[1] * 0.2f), 1.0f); data->force_z[index] = min_ff(max_ff(-1.0f, retvel[2] * 0.2f), 1.0f); @@ -3617,14 +3637,16 @@ static int manta_step( fds->time_per_frame = time_per_frame; fds->time_total = time_total; } + /* Total time must not exceed framecount times framelength. Correct tiny errors here. */ CLAMP(fds->time_total, fds->time_total, time_total_old + fds->frame_length); + /* Compute shadow grid for gas simulations. Make sure to skip if bake job was canceled early. */ if (fds->type == FLUID_DOMAIN_TYPE_GAS && result) { manta_smoke_calc_transparency(fds, DEG_get_evaluated_view_layer(depsgraph)); } - BLI_mutex_unlock(&object_update_lock); + BLI_mutex_unlock(&object_update_lock); return result; } @@ -3716,29 +3738,35 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, int mode = fds->cache_type; /* Do not process modifier if current frame is out of cache range. */ + bool escape = false; switch (mode) { case FLUID_DOMAIN_CACHE_ALL: case FLUID_DOMAIN_CACHE_MODULAR: if (fds->cache_frame_offset > 0) { if (scene_framenr < fds->cache_frame_start || scene_framenr > fds->cache_frame_end + fds->cache_frame_offset) { - return; + escape = true; } } else { if (scene_framenr < fds->cache_frame_start + fds->cache_frame_offset || scene_framenr > fds->cache_frame_end) { - return; + escape = true; } } break; case FLUID_DOMAIN_CACHE_REPLAY: default: if (scene_framenr < fds->cache_frame_start || scene_framenr > fds->cache_frame_end) { - return; + escape = true; } break; } + /* If modifier will not be processed, update/flush pointers from (old) fluid object once more. */ + if (escape && fds->fluid) { + manta_update_pointers(fds->fluid, fmd, true); + return; + } /* Reset fluid if no fluid present. Also resets active fields. */ if (!fds->fluid) { @@ -3830,7 +3858,15 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, has_mesh = manta_has_mesh(fds->fluid, fmd, scene_framenr); has_particles = manta_has_particles(fds->fluid, fmd, scene_framenr); has_guide = manta_has_guiding(fds->fluid, fmd, scene_framenr, guide_parent); - has_config = false; + has_config = manta_read_config(fds->fluid, fmd, scene_framenr); + + /* When reading data from cache (has_config == true) ensure that active fields are allocated. + * update_flowsflags() and update_obstacleflags() will not find flow sources hidden from renders. + * See also: T72192. */ + if (has_config) { + ensure_flowsfields(fds); + ensure_obstaclefields(fds); + } bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide; baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA; @@ -3947,7 +3983,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, /* Read mesh cache. */ if (with_liquid && with_mesh) { - has_config = manta_read_config(fds->fluid, fmd, mesh_frame); + if (mesh_frame != scene_framenr) { + has_config = manta_read_config(fds->fluid, fmd, mesh_frame); + } /* Update mesh data from file is faster than via Python (manta_read_mesh()). */ has_mesh = manta_read_mesh(fds->fluid, fmd, mesh_frame); @@ -3955,7 +3993,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, /* Read particles cache. */ if (with_liquid && with_particles) { - has_config = manta_read_config(fds->fluid, fmd, particles_frame); + if (particles_frame != scene_framenr) { + has_config = manta_read_config(fds->fluid, fmd, particles_frame); + } read_partial = !baking_data && !baking_particles && next_particles; read_all = !read_partial && with_resumable_cache; @@ -3970,7 +4010,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, /* Read noise and data cache */ if (with_smoke && with_noise) { - has_config = manta_read_config(fds->fluid, fmd, noise_frame); + if (noise_frame != scene_framenr) { + has_config = manta_read_config(fds->fluid, fmd, noise_frame); + } /* Only reallocate when just reading cache or when resuming during bake. */ if (has_data && has_config && manta_needs_realloc(fds->fluid, fmd)) { @@ -3988,7 +4030,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, } /* Read data cache only */ else { - has_config = manta_read_config(fds->fluid, fmd, data_frame); + if (data_frame != scene_framenr) { + has_config = manta_read_config(fds->fluid, fmd, data_frame); + } if (with_smoke) { /* Read config and realloc fluid object if needed. */ @@ -4073,6 +4117,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, } } + /* Ensure that fluid pointers are always up to date at the end of modifier processing. */ + manta_update_pointers(fds->fluid, fmd, false); + fds->flags &= ~FLUID_DOMAIN_FILE_LOAD; fmd->time = scene_framenr; } diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index dfa5ff6975f..958acf0589b 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -797,7 +797,7 @@ static bool vfont_to_curve(Object *ob, } else { char32_t *mem_tmp; - slen = cu->len_wchar; + slen = cu->len_char32; /* Create unicode string */ mem_tmp = MEM_malloc_arrayN((slen + 1), sizeof(*mem_tmp), "convertedmem"); diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 579ebc9b9b3..14eb6bb4f4c 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -2599,10 +2599,9 @@ void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps) float color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; bGPDspoint *pt = &gps->points[0]; - color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->x)); - color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->y)); - color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->z)); - + color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints / 5, pt->x + pt->z)); + color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints + pt->x, pt->y * pt->z + pt->x)); + color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints - pt->x, pt->z * pt->x + pt->y)); for (int i = 0; i < gps->totpoints; i++) { pt = &gps->points[i]; copy_v4_v4(pt->vert_color, color); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 9365ee040c2..e331e5ae1dd 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -57,6 +57,7 @@ #include "DNA_packedFile_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_simulation_types.h" #include "DNA_world_types.h" #include "BLI_blenlib.h" @@ -89,7 +90,6 @@ #include "RE_pipeline.h" -#include "GPU_draw.h" #include "GPU_texture.h" #include "BLI_sys_types.h" // for intptr_t support @@ -392,7 +392,7 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock) ima->rr = NULL; } - GPU_free_image(ima); + BKE_image_free_gputextures(ima); LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { tile->ok = IMA_OK; @@ -3240,6 +3240,12 @@ static void image_walk_id_all_users( if (scene->nodetree && scene->use_nodes && !skip_nested_nodes) { image_walk_ntree_all_users(scene->nodetree, &scene->id, customdata, callback); } + break; + } + case ID_SIM: { + Simulation *simulation = (Simulation *)id; + image_walk_ntree_all_users(simulation->nodetree, &simulation->id, customdata, callback); + break; } default: break; @@ -3344,8 +3350,7 @@ static void image_free_tile(Image *ima, ImageTile *tile) for (int i = 0; i < TEXTARGET_COUNT; i++) { /* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other * two. */ - if (tile != ima->tiles.first && - !(ELEM(i, TEXTARGET_TEXTURE_2D_ARRAY, TEXTARGET_TEXTURE_TILE_MAPPING))) { + if (tile != ima->tiles.first && !(ELEM(i, TEXTARGET_2D_ARRAY, TEXTARGET_TILE_MAPPING))) { continue; } @@ -3622,13 +3627,13 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la for (int eye = 0; eye < 2; eye++) { /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye] = NULL; + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; } - if (ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] = NULL; + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; } } @@ -3937,7 +3942,7 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) #endif /* WITH_OPENEXR */ /* common stuff to do with images after loading */ -static void image_initialize_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf)) +static void image_init_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf)) { /* Preview is NULL when it has never been used as an icon before. * Never handle previews/icons outside of main thread. */ @@ -4040,11 +4045,11 @@ static ImBuf *load_sequence_single( } } else { - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); *r_assign = true; } #else - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); *r_assign = true; #endif } @@ -4149,7 +4154,7 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int e BKE_imbuf_stamp_info(ima->rr, ibuf); - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, entry); } // else printf("pass not found\n"); @@ -4213,7 +4218,7 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i ibuf = IMB_makeSingleUser(IMB_anim_absolute(ia->anim, fra, IMB_TC_RECORD_RUN, IMB_PROXY_NONE)); if (ibuf) { - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); } else { tile->ok = 0; @@ -4358,7 +4363,7 @@ static ImBuf *load_image_single(Image *ima, else #endif { - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); *r_assign = true; /* make packed file for autopack */ @@ -4472,7 +4477,7 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) if (rpass) { ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0); - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); ibuf->rect_float = rpass->rect; ibuf->flags |= IB_rectfloat; diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c new file mode 100644 index 00000000000..22fb6dfd02a --- /dev/null +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -0,0 +1,774 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_boxpack_2d.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_threads.h" + +#include "DNA_image_types.h" +#include "DNA_userdef_types.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_main.h" + +#include "GPU_extensions.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "PIL_time.h" + +/* Prototypes. */ +static void gpu_free_unused_buffers(void); +static void image_free_gpu(Image *ima, const bool immediate); + +/* -------------------------------------------------------------------- */ +/** \name UDIM gpu texture + * \{ */ + +static bool is_over_resolution_limit(int w, int h) +{ + return (w > GPU_texture_size_with_limit(w) || h > GPU_texture_size_with_limit(h)); +} + +static int smaller_power_of_2_limit(int num) +{ + return power_of_2_min_i(GPU_texture_size_with_limit(num)); +} + +static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) +{ + GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; + + if (tilearray == NULL) { + return 0; + } + + float array_w = GPU_texture_width(tilearray); + float array_h = GPU_texture_height(tilearray); + + ImageTile *last_tile = (ImageTile *)ima->tiles.last; + /* Tiles are sorted by number. */ + int max_tile = last_tile->tile_number - 1001; + + /* create image */ + int width = max_tile + 1; + float *data = (float *)MEM_callocN(width * 8 * sizeof(float), __func__); + for (int i = 0; i < width; i++) { + data[4 * i] = -1.0f; + } + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int i = tile->tile_number - 1001; + data[4 * i] = tile->runtime.tilearray_layer; + + float *tile_info = &data[4 * width + 4 * i]; + tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; + tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; + tile_info[2] = tile->runtime.tilearray_size[0] / array_w; + tile_info[3] = tile->runtime.tilearray_size[1] / array_h; + } + + GPUTexture *tex = GPU_texture_create_1d_array(width, 2, GPU_RGBA32F, data, NULL); + GPU_texture_mipmap_mode(tex, false, false); + + MEM_freeN(data); + + return tex; +} + +typedef struct PackTile { + FixedSizeBoxPack boxpack; + ImageTile *tile; + float pack_score; +} PackTile; + +static int compare_packtile(const void *a, const void *b) +{ + const PackTile *tile_a = (const PackTile *)a; + const PackTile *tile_b = (const PackTile *)b; + + return tile_a->pack_score < tile_b->pack_score; +} + +static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) +{ + int arraywidth = 0, arrayheight = 0; + ListBase boxes = {NULL}; + + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + + if (ibuf) { + PackTile *packtile = (PackTile *)MEM_callocN(sizeof(PackTile), __func__); + packtile->tile = tile; + packtile->boxpack.w = ibuf->x; + packtile->boxpack.h = ibuf->y; + + if (is_over_resolution_limit(packtile->boxpack.w, packtile->boxpack.h)) { + packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); + packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); + } + arraywidth = max_ii(arraywidth, packtile->boxpack.w); + arrayheight = max_ii(arrayheight, packtile->boxpack.h); + + /* We sort the tiles by decreasing size, with an additional penalty term + * for high aspect ratios. This improves packing efficiency. */ + float w = packtile->boxpack.w, h = packtile->boxpack.h; + packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h; + + BKE_image_release_ibuf(ima, ibuf, NULL); + BLI_addtail(&boxes, packtile); + } + } + + BLI_assert(arraywidth > 0 && arrayheight > 0); + + BLI_listbase_sort(&boxes, compare_packtile); + int arraylayers = 0; + /* Keep adding layers until all tiles are packed. */ + while (boxes.first != NULL) { + ListBase packed = {NULL}; + BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed); + BLI_assert(packed.first != NULL); + + LISTBASE_FOREACH (PackTile *, packtile, &packed) { + ImageTile *tile = packtile->tile; + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + + tileoffset[0] = packtile->boxpack.x; + tileoffset[1] = packtile->boxpack.y; + tilesize[0] = packtile->boxpack.w; + tilesize[1] = packtile->boxpack.h; + tile->runtime.tilearray_layer = arraylayers; + } + + BLI_freelistN(&packed); + arraylayers++; + } + + const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); + /* Create Texture without content. */ + GPUTexture *tex = IMB_touch_gpu_texture( + main_ibuf, arraywidth, arrayheight, arraylayers, use_high_bitdepth); + + GPU_texture_bind(tex, 0); + + /* Upload each tile one by one. */ + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int tilelayer = tile->runtime.tilearray_layer; + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + + if (tilesize[0] == 0 || tilesize[1] == 0) { + continue; + } + + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + + if (ibuf) { + const bool store_premultiplied = ibuf->rect_float ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : + (ima->alpha_mode == IMA_ALPHA_PREMUL); + IMB_update_gpu_texture_sub(tex, + ibuf, + UNPACK2(tileoffset), + tilelayer, + UNPACK2(tilesize), + use_high_bitdepth, + store_premultiplied); + } + + BKE_image_release_ibuf(ima, ibuf, NULL); + } + + if (GPU_mipmap_enabled()) { + GPU_texture_generate_mipmap(tex); + GPU_texture_mipmap_mode(tex, true, true); + if (ima) { + ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; + } + } + else { + GPU_texture_mipmap_mode(tex, false, true); + } + + GPU_texture_unbind(tex); + + return tex; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Regular gpu texture + * \{ */ + +static GPUTexture **get_image_gpu_texture_ptr(Image *ima, + eGPUTextureTarget textarget, + const int multiview_eye) +{ + const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); + BLI_assert(in_range); + + if (in_range) { + return &(ima->gputexture[textarget][multiview_eye]); + } + return NULL; +} + +static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget) +{ + switch (textarget) { + case TEXTARGET_2D_ARRAY: + return GPU_texture_create_error(2, true); + case TEXTARGET_TILE_MAPPING: + return GPU_texture_create_error(1, true); + case TEXTARGET_2D: + default: + return GPU_texture_create_error(2, false); + } +} + +static GPUTexture *image_get_gpu_texture(Image *ima, + ImageUser *iuser, + ImBuf *ibuf, + eGPUTextureTarget textarget) +{ + if (ima == NULL) { + return NULL; + } + + /* Free any unused GPU textures, since we know we are in a thread with OpenGL + * context and might as well ensure we have as much space free as possible. */ + gpu_free_unused_buffers(); + + /* currently, gpu refresh tagging is used by ima sequences */ + if (ima->gpuflag & IMA_GPU_REFRESH) { + image_free_gpu(ima, true); + ima->gpuflag &= ~IMA_GPU_REFRESH; + } + + /* Tag as in active use for garbage collector. */ + BKE_image_tag_time(ima); + + /* Test if we already have a texture. */ + GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, iuser ? iuser->multiview_eye : 0); + if (*tex) { + return *tex; + } + + /* Check if we have a valid image. If not, we return a dummy + * texture with zero bind-code so we don't keep trying. */ + ImageTile *tile = BKE_image_get_tile(ima, 0); + if (tile == NULL || tile->ok == 0) { + *tex = image_gpu_texture_error_create(textarget); + return *tex; + } + + /* check if we have a valid image buffer */ + ImBuf *ibuf_intern = ibuf; + if (ibuf_intern == NULL) { + ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL); + if (ibuf_intern == NULL) { + *tex = image_gpu_texture_error_create(textarget); + return *tex; + } + } + + if (textarget == TEXTARGET_2D_ARRAY) { + *tex = gpu_texture_create_tile_array(ima, ibuf_intern); + } + else if (textarget == TEXTARGET_TILE_MAPPING) { + *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); + } + else { + const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); + const bool store_premultiplied = ibuf_intern->rect_float ? + (ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false) : + (ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true); + + *tex = IMB_create_gpu_texture(ibuf_intern, use_high_bitdepth, store_premultiplied); + + if (GPU_mipmap_enabled()) { + GPU_texture_bind(*tex, 0); + GPU_texture_generate_mipmap(*tex); + GPU_texture_unbind(*tex); + if (ima) { + ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; + } + GPU_texture_mipmap_mode(*tex, true, true); + } + else { + GPU_texture_mipmap_mode(*tex, false, true); + } + } + + /* if `ibuf` was given, we don't own the `ibuf_intern` */ + if (ibuf == NULL) { + BKE_image_release_ibuf(ima, ibuf_intern, NULL); + } + + GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); + + return *tex; +} + +GPUTexture *BKE_image_get_gpu_texture(Image *image, ImageUser *iuser, ImBuf *ibuf) +{ + return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_2D); +} + +GPUTexture *BKE_image_get_gpu_tiles(Image *image, ImageUser *iuser, ImBuf *ibuf) +{ + return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_2D_ARRAY); +} + +GPUTexture *BKE_image_get_gpu_tilemap(Image *image, ImageUser *iuser, ImBuf *ibuf) +{ + return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_TILE_MAPPING); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delayed GPU texture free + * + * Image datablocks can be deleted by any thread, but there may not be any active OpenGL context. + * In that case we push them into a queue and free the buffers later. + * \{ */ + +static LinkNode *gpu_texture_free_queue = NULL; +static ThreadMutex gpu_texture_queue_mutex = BLI_MUTEX_INITIALIZER; + +static void gpu_free_unused_buffers(void) +{ + if (gpu_texture_free_queue == NULL) { + return; + } + + BLI_mutex_lock(&gpu_texture_queue_mutex); + + while (gpu_texture_free_queue != NULL) { + GPUTexture *tex = BLI_linklist_pop(&gpu_texture_free_queue); + GPU_texture_free(tex); + } + + BLI_mutex_unlock(&gpu_texture_queue_mutex); +} + +void BKE_image_free_unused_gpu_textures() +{ + if (BLI_thread_is_main()) { + gpu_free_unused_buffers(); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Deletion + * \{ */ + +static void image_free_gpu(Image *ima, const bool immediate) +{ + for (int eye = 0; eye < 2; eye++) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + if (ima->gputexture[i][eye] != NULL) { + if (immediate) { + GPU_texture_free(ima->gputexture[i][eye]); + } + else { + BLI_mutex_lock(&gpu_texture_queue_mutex); + BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]); + BLI_mutex_unlock(&gpu_texture_queue_mutex); + } + + ima->gputexture[i][eye] = NULL; + } + } + } + + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; +} + +void BKE_image_free_gputextures(Image *ima) +{ + image_free_gpu(ima, BLI_thread_is_main()); +} + +void BKE_image_free_all_gputextures(Main *bmain) +{ + if (bmain) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + BKE_image_free_gputextures(ima); + } + } +} + +/* same as above but only free animated images */ +void BKE_image_free_anim_gputextures(Main *bmain) +{ + if (bmain) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + if (BKE_image_is_animated(ima)) { + BKE_image_free_gputextures(ima); + } + } + } +} + +void BKE_image_free_old_gputextures(Main *bmain) +{ + static int lasttime = 0; + int ctime = (int)PIL_check_seconds_timer(); + + /* + * Run garbage collector once for every collecting period of time + * if textimeout is 0, that's the option to NOT run the collector + */ + if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) { + return; + } + + /* of course not! */ + if (G.is_rendering) { + return; + } + + lasttime = ctime; + + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) { + /* If it's in GL memory, deallocate and set time tag to current time + * This gives textures a "second chance" to be used before dying. */ + if (BKE_image_has_opengl_texture(ima)) { + BKE_image_free_gputextures(ima); + ima->lastused = ctime; + } + /* Otherwise, just kill the buffers */ + else { + BKE_image_free_buffers(ima); + } + } + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Paint Update + * \{ */ + +static ImBuf *update_do_scale(uchar *rect, + float *rect_float, + int *x, + int *y, + int *w, + int *h, + int limit_w, + int limit_h, + int full_w, + int full_h) +{ + /* Partial update with scaling. */ + float xratio = limit_w / (float)full_w; + float yratio = limit_h / (float)full_h; + + int part_w = *w, part_h = *h; + + /* Find sub coordinates in scaled image. Take ceiling because we will be + * losing 1 pixel due to rounding errors in x,y. */ + *x *= xratio; + *y *= yratio; + *w = (int)ceil(xratio * (*w)); + *h = (int)ceil(yratio * (*h)); + + /* ...but take back if we are over the limit! */ + if (*x + *w > limit_w) { + (*w)--; + } + if (*y + *h > limit_h) { + (*h)--; + } + + /* Scale pixels. */ + ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4); + IMB_scaleImBuf(ibuf, *w, *h); + + return ibuf; +} + +static void gpu_texture_update_scaled(GPUTexture *tex, + uchar *rect, + float *rect_float, + int full_w, + int full_h, + int x, + int y, + int layer, + const int *tile_offset, + const int *tile_size, + int w, + int h) +{ + ImBuf *ibuf; + if (layer > -1) { + ibuf = update_do_scale( + rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h); + + /* Shift to account for tile packing. */ + x += tile_offset[0]; + y += tile_offset[1]; + } + else { + /* Partial update with scaling. */ + int limit_w = smaller_power_of_2_limit(full_w); + int limit_h = smaller_power_of_2_limit(full_h); + + ibuf = update_do_scale(rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h); + } + + void *data = (ibuf->rect_float) ? (void *)(ibuf->rect_float) : (void *)(ibuf->rect); + eGPUDataFormat data_format = (ibuf->rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + + GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1); + + IMB_freeImBuf(ibuf); +} + +static void gpu_texture_update_unscaled(GPUTexture *tex, + uchar *rect, + float *rect_float, + int x, + int y, + int layer, + const int tile_offset[2], + int w, + int h, + int tex_stride, + int tex_offset) +{ + if (layer > -1) { + /* Shift to account for tile packing. */ + x += tile_offset[0]; + y += tile_offset[1]; + } + + void *data = (rect_float) ? (void *)(rect_float + tex_offset) : (void *)(rect + tex_offset); + eGPUDataFormat data_format = (rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + + /* Partial update without scaling. Stride and offset are used to copy only a + * subset of a possible larger buffer than what we are updating. */ + GPU_unpack_row_length_set(tex_stride); + + GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1); + /* Restore default. */ + GPU_unpack_row_length_set(0); +} + +static void gpu_texture_update_from_ibuf( + GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) +{ + bool scaled; + if (tile != NULL) { + int *tilesize = tile->runtime.tilearray_size; + scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); + } + else { + scaled = is_over_resolution_limit(ibuf->x, ibuf->y); + } + + if (scaled) { + /* Extra padding to account for bleed from neighboring pixels. */ + const int padding = 4; + const int xmax = min_ii(x + w + padding, ibuf->x); + const int ymax = min_ii(y + h + padding, ibuf->y); + x = max_ii(x - padding, 0); + y = max_ii(y - padding, 0); + w = xmax - x; + h = ymax - y; + } + + /* Get texture data pointers. */ + float *rect_float = ibuf->rect_float; + uchar *rect = (uchar *)ibuf->rect; + int tex_stride = ibuf->x; + int tex_offset = ibuf->channels * (y * ibuf->x + x); + + if (rect_float == NULL) { + /* Byte pixels. */ + if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear( + ibuf->rect_colorspace); + + rect = (uchar *)MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__); + if (rect == NULL) { + return; + } + + tex_stride = w; + tex_offset = 0; + + /* Convert to scene linear with sRGB compression, and premultiplied for + * correct texture interpolation. */ + const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL); + IMB_colormanagement_imbuf_to_byte_texture( + rect, x, y, w, h, ibuf, compress_as_srgb, store_premultiplied); + } + } + else { + /* Float pixels. */ + const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT); + + if (ibuf->channels != 4 || scaled || !store_premultiplied) { + rect_float = (float *)MEM_mallocN(sizeof(float) * 4 * w * h, __func__); + if (rect_float == NULL) { + return; + } + + tex_stride = w; + tex_offset = 0; + + IMB_colormanagement_imbuf_to_float_texture( + rect_float, x, y, w, h, ibuf, store_premultiplied); + } + } + + GPU_texture_bind(tex, 0); + + if (scaled) { + /* Slower update where we first have to scale the input pixels. */ + if (tile != NULL) { + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + int tilelayer = tile->runtime.tilearray_layer; + gpu_texture_update_scaled( + tex, rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); + } + else { + gpu_texture_update_scaled( + tex, rect, rect_float, ibuf->x, ibuf->y, x, y, -1, NULL, NULL, w, h); + } + } + else { + /* Fast update at same resolution. */ + if (tile != NULL) { + int *tileoffset = tile->runtime.tilearray_offset; + int tilelayer = tile->runtime.tilearray_layer; + gpu_texture_update_unscaled( + tex, rect, rect_float, x, y, tilelayer, tileoffset, w, h, tex_stride, tex_offset); + } + else { + gpu_texture_update_unscaled( + tex, rect, rect_float, x, y, -1, NULL, w, h, tex_stride, tex_offset); + } + } + + /* Free buffers if needed. */ + if (rect && rect != (uchar *)ibuf->rect) { + MEM_freeN(rect); + } + if (rect_float && rect_float != ibuf->rect_float) { + MEM_freeN(rect_float); + } + + if (GPU_mipmap_enabled()) { + GPU_texture_generate_mipmap(tex); + } + else { + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + } + + GPU_texture_unbind(tex); +} + +/* Partial update of texture for texture painting. This is often much + * quicker than fully updating the texture for high resolution images. */ +void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h) +{ + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + + if ((ibuf == NULL) || (w == 0) || (h == 0)) { + /* Full reload of texture. */ + BKE_image_free_gputextures(ima); + } + + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; + /* Check if we need to update the main gputexture. */ + if (tex != NULL && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); + } + + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; + if (tex != NULL) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); + } + + BKE_image_release_ibuf(ima, ibuf, NULL); +} + +/* these two functions are called on entering and exiting texture paint mode, + * temporary disabling/enabling mipmapping on all images for quick texture + * updates with glTexSubImage2D. images that didn't change don't have to be + * re-uploaded to OpenGL */ +void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) +{ + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + if (BKE_image_has_opengl_texture(ima)) { + if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { + for (int eye = 0; eye < 2; eye++) { + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { + GPUTexture *tex = ima->gputexture[a][eye]; + if (tex != NULL) { + GPU_texture_mipmap_mode(tex, mipmap, true); + } + } + } + } + } + else { + BKE_image_free_gputextures(ima); + } + } + else { + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + } + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 64649d84320..83071fd5dd3 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -221,7 +221,7 @@ ViewLayer *BKE_view_layer_add(Scene *scene, view_layer_new = view_layer_add(name); BLI_addtail(&scene->view_layers, view_layer_new); - /* Initialise layercollections */ + /* Initialize layer-collections. */ BKE_layer_collection_sync(scene, view_layer_new); layer_collection_exclude_all(view_layer_new->layer_collections.first); diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 2989c910c45..8b0517a77fb 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1515,7 +1515,7 @@ void BKE_lib_override_library_main_update(Main *bmain) */ /** Initialize an override storage. */ -OverrideLibraryStorage *BKE_lib_override_library_operations_store_initialize(void) +OverrideLibraryStorage *BKE_lib_override_library_operations_store_init(void) { return BKE_main_new(); } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 00a42b12e07..0f81d45c10f 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -412,6 +412,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return ELEM(id_type_used, ID_MA); case ID_VO: return ELEM(id_type_used, ID_MA); + case ID_SIM: + return ELEM(id_type_used, ID_OB, ID_IM); case ID_IM: case ID_VF: case ID_TXT: @@ -422,7 +424,6 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) case ID_PAL: case ID_PC: case ID_CF: - case ID_SIM: /* Those types never use/reference other IDs... */ return false; case ID_IP: diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index aa1005c663f..f42df6765c4 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -58,7 +58,7 @@ static void light_init_data(ID *id) MEMCPY_STRUCT_AFTER(la, DNA_struct_default_get(Light), id); la->curfalloff = BKE_curvemapping_add(1, 0.0f, 1.0f, 1.0f, 0.0f); - BKE_curvemapping_initialize(la->curfalloff); + BKE_curvemapping_init(la->curfalloff); } /** diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index b0b542f6000..e878a894b27 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -959,14 +959,13 @@ void BKE_object_material_array_assign(Main *bmain, const bool to_object_only) { int actcol_orig = ob->actcol; - short i; while ((ob->totcol > totcol) && BKE_object_material_slot_remove(bmain, ob)) { /* pass */ } /* now we have the right number of slots */ - for (i = 0; i < totcol; i++) { + for (int i = 0; i < totcol; i++) { if (to_object_only && ob->matbits[i] == 0) { /* If we only assign to object, and that slot uses obdata material, do nothing. */ continue; diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 4a65c6ff5e7..a5bd2ecf68a 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1866,3 +1866,88 @@ void BKE_movieclip_eval_selection_update(struct Depsgraph *depsgraph, MovieClip DEG_debug_print_eval(depsgraph, __func__, clip->id.name, clip); movieclip_selection_sync(clip, (MovieClip *)clip->id.orig_id); } + +/* -------------------------------------------------------------------- */ +/** \name GPU textures + * \{ */ + +static GPUTexture **movieclip_get_gputexture_ptr(MovieClip *clip, + MovieClipUser *cuser, + eGPUTextureTarget textarget) +{ + /* Check if we have an existing entry for that clip user. */ + MovieClip_RuntimeGPUTexture *tex; + for (tex = clip->runtime.gputextures.first; tex; tex = tex->next) { + if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) { + break; + } + } + + /* If not, allocate a new one. */ + if (tex == NULL) { + tex = (MovieClip_RuntimeGPUTexture *)MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), + __func__); + + for (int i = 0; i < TEXTARGET_COUNT; i++) { + tex->gputexture[i] = NULL; + } + + memcpy(&tex->user, cuser, sizeof(MovieClipUser)); + BLI_addtail(&clip->runtime.gputextures, tex); + } + + return &tex->gputexture[textarget]; +} + +GPUTexture *BKE_movieclip_get_gpu_texture(MovieClip *clip, MovieClipUser *cuser) +{ + if (clip == NULL) { + return NULL; + } + + GPUTexture **tex = movieclip_get_gputexture_ptr(clip, cuser, TEXTARGET_2D); + if (*tex) { + return *tex; + } + + /* check if we have a valid image buffer */ + ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser); + if (ibuf == NULL) { + *tex = GPU_texture_create_error(2, false); + return *tex; + } + + /* This only means RGBA16F instead of RGBA32F. */ + const bool high_bitdepth = false; + const bool store_premultiplied = ibuf->rect_float ? false : true; + *tex = IMB_create_gpu_texture(ibuf, high_bitdepth, store_premultiplied); + + /* Do not generate mips for movieclips... too slow. */ + GPU_texture_mipmap_mode(*tex, false, true); + + IMB_freeImBuf(ibuf); + + return *tex; +} + +void BKE_movieclip_free_gputexture(struct MovieClip *clip) +{ + /* number of gpu textures to keep around as cache + * We don't want to keep too many GPU textures for + * movie clips around, as they can be large.*/ + const int MOVIECLIP_NUM_GPUTEXTURES = 1; + + while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) { + MovieClip_RuntimeGPUTexture *tex = (MovieClip_RuntimeGPUTexture *)BLI_pophead( + &clip->runtime.gputextures); + for (int i = 0; i < TEXTARGET_COUNT; i++) { + /* free glsl image binding */ + if (tex->gputexture[i]) { + GPU_texture_free(tex->gputexture[i]); + tex->gputexture[i] = NULL; + } + } + MEM_freeN(tex); + } +} +/** \} */ diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 71d49dd1c19..6c10f6de855 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -241,7 +241,7 @@ static void multires_mdisps_subdivide_hidden(MDisps *md, int new_level) md->hidden = subd; } -static MDisps *multires_mdisps_initialize_hidden(Mesh *me, int level) +static MDisps *multires_mdisps_init_hidden(Mesh *me, int level) { MDisps *mdisps = CustomData_add_layer(&me->ldata, CD_MDISPS, CD_CALLOC, NULL, me->totloop); int gridsize = BKE_ccg_gridsize(level); @@ -868,7 +868,7 @@ static void multires_subdivide_legacy( mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS); if (!mdisps) { - mdisps = multires_mdisps_initialize_hidden(me, totlvl); + mdisps = multires_mdisps_init_hidden(me, totlvl); } if (mdisps->disps && !updateblock && lvl != 0) { diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 20d65e52b09..91693abd1cf 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -61,6 +61,7 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_simulation.h" #include "BLI_ghash.h" #include "BLI_threads.h" @@ -845,12 +846,12 @@ static void socket_id_user_increment(bNodeSocket *sock) switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { bNodeSocketValueObject *default_value = sock->default_value; - id_us_plus(&default_value->value->id); + id_us_plus((ID *)default_value->value); break; } case SOCK_IMAGE: { bNodeSocketValueImage *default_value = sock->default_value; - id_us_plus(&default_value->value->id); + id_us_plus((ID *)default_value->value); break; } case SOCK_FLOAT: @@ -2493,6 +2494,7 @@ ID *BKE_node_tree_find_owner_ID(Main *bmain, struct bNodeTree *ntree) &bmain->textures, &bmain->scenes, &bmain->linestyles, + &bmain->simulations, NULL}; for (int i = 0; lists[i] != NULL; i++) { @@ -3637,6 +3639,16 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup) FOREACH_NODETREE_END; } +static void ntreeUpdateSimulationDependencies(Main *main, bNodeTree *simulation_ntree) +{ + FOREACH_NODETREE_BEGIN (main, ntree, owner_id) { + if (GS(owner_id->name) == ID_SIM && ntree == simulation_ntree) { + BKE_simulation_update_dependencies((Simulation *)owner_id, main); + } + } + FOREACH_NODETREE_END; +} + void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) { bNode *node; @@ -3679,7 +3691,6 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntreeInterfaceTypeUpdate(ntree); } - /* XXX hack, should be done by depsgraph!! */ if (bmain) { ntreeUpdateAllUsers(bmain, &ntree->id); } @@ -3695,6 +3706,11 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntree_validate_links(ntree); } + if (bmain != NULL && ntree->typeinfo == ntreeType_Simulation && + (ntree->id.flag & LIB_EMBEDDED_DATA)) { + ntreeUpdateSimulationDependencies(bmain, ntree); + } + /* clear update flags */ for (node = ntree->nodes.first; node; node = node->next) { node->update = 0; @@ -4336,6 +4352,8 @@ static void registerSimulationNodes(void) register_node_type_sim_emit_particles(); register_node_type_sim_time(); register_node_type_sim_particle_attribute(); + register_node_type_sim_age_reached_event(); + register_node_type_sim_kill_particle(); } static void registerFunctionNodes(void) @@ -4346,6 +4364,7 @@ static void registerFunctionNodes(void) register_node_type_fn_group_instance_id(); register_node_type_fn_combine_strings(); register_node_type_fn_object_transforms(); + register_node_type_fn_random_float(); } void init_nodesystem(void) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index ecb2256d080..fe559d2a44e 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2409,7 +2409,7 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4]) /* ctime is now a proper var setting of Curve which gets set by Animato like any other var * that's animated, but this will only work if it actually is animated. * - * We divide the curvetime calculated in the previous step by the length of the path, + * We divide the curve-time calculated in the previous step by the length of the path, * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. */ if (cu->pathlen) { @@ -3970,15 +3970,20 @@ int BKE_object_is_modified(Scene *scene, Object *ob) return flag; } -/* Check of objects moves in time. */ -/* NOTE: This function is currently optimized for usage in combination - * with mti->canDeform, so modifiers can quickly check if their target - * objects moves (causing deformation motion blur) or not. +/** + * Check of objects moves in time. + * + * \note This function is currently optimized for usage in combination + * with modifier deformation checks (#eModifierTypeType_OnlyDeform), + * so modifiers can quickly check if their target objects moves + * (causing deformation motion blur) or not. * * This makes it possible to give some degree of false-positives here, * but it's currently an acceptable tradeoff between complexity and check * speed. In combination with checks of modifier stack and real life usage - * percentage of false-positives shouldn't be that height. + * percentage of false-positives shouldn't be that high. + * + * \note This function does not consider physics systems. */ bool BKE_object_moves_in_time(const Object *object, bool recurse_parent) { diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index f498e147110..e0aea3a2910 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -37,6 +37,7 @@ #include "DNA_collection_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_vfont_types.h" @@ -367,17 +368,13 @@ static void vertex_dupli(const VertexDupliData *vdd, DupliObject *dob; float obmat[4][4], space_mat[4][4]; - /* obmat is transform to vertex */ - get_duplivert_transform(co, no, vdd->use_rotation, inst_ob->trackflag, inst_ob->upflag, obmat); + /* space_mat is transform to vertex */ + get_duplivert_transform( + co, no, vdd->use_rotation, inst_ob->trackflag, inst_ob->upflag, space_mat); /* make offset relative to inst_ob using relative child transform */ - mul_mat3_m4_v3((float(*)[4])vdd->child_imat, obmat[3]); + mul_mat3_m4_v3((float(*)[4])vdd->child_imat, space_mat[3]); /* apply obmat _after_ the local vertex transform */ - mul_m4_m4m4(obmat, inst_ob->obmat, obmat); - - /* space matrix is constructed by removing obmat transform, - * this yields the worldspace transform for recursive duplis - */ - mul_m4_m4m4(space_mat, obmat, inst_ob->imat); + mul_m4_m4m4(obmat, inst_ob->obmat, space_mat); dob = make_dupli(vdd->ctx, vdd->inst_ob, obmat, index); @@ -523,7 +520,7 @@ static void make_duplis_font(const DupliContext *ctx) /* Safety check even if it might fail badly when called for original object. */ const bool is_eval_curve = DEG_is_evaluated_id(&cu->id); - /* advance matching BLI_strncpy_wchar_from_utf8 */ + /* Advance matching BLI_str_utf8_as_utf32. */ for (a = 0; a < text_len; a++, ct++) { /* XXX That G.main is *really* ugly, but not sure what to do here... @@ -573,6 +570,63 @@ static const DupliGenerator gen_dupli_verts_font = { make_duplis_font /* make_duplis */ }; +/* OB_DUPLIVERTS - PointCloud */ +static void make_child_duplis_pointcloud(const DupliContext *ctx, + void *UNUSED(userdata), + Object *child) +{ + const Object *parent = ctx->object; + const PointCloud *pointcloud = parent->data; + const float(*co)[3] = pointcloud->co; + const float *radius = pointcloud->radius; + const float(*rotation)[4] = NULL; /* TODO: add optional rotation attribute. */ + const float(*orco)[3] = NULL; /* TODO: add optional texture coordinate attribute. */ + + /* Relative transform from parent to child space. */ + float child_imat[4][4]; + mul_m4_m4m4(child_imat, child->imat, parent->obmat); + + for (int i = 0; i < pointcloud->totpoint; i++) { + /* Transform matrix from point position, radius and rotation. */ + float quat[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + float size[3] = {1.0f, 1.0f, 1.0f}; + if (radius) { + copy_v3_fl(size, radius[i]); + } + if (rotation) { + copy_v4_v4(quat, rotation[i]); + } + + float space_mat[4][4]; + loc_quat_size_to_mat4(space_mat, co[i], quat, size); + + /* Make offset relative to child object using relative child transform, + * and apply object matrix after local vertex transform. */ + mul_mat3_m4_v3(child_imat, space_mat[3]); + + /* Create dupli object. */ + float obmat[4][4]; + mul_m4_m4m4(obmat, child->obmat, space_mat); + DupliObject *dob = make_dupli(ctx, child, obmat, i); + if (orco) { + copy_v3_v3(dob->orco, orco[i]); + } + + /* Recursion. */ + make_recursive_duplis(ctx, child, space_mat, i); + } +} + +static void make_duplis_pointcloud(const DupliContext *ctx) +{ + make_child_duplis(ctx, NULL, make_child_duplis_pointcloud); +} + +static const DupliGenerator gen_dupli_verts_pointcloud = { + OB_DUPLIVERTS, /* type */ + make_duplis_pointcloud /* make_duplis */ +}; + /* OB_DUPLIFACES */ typedef struct FaceDupliData { Mesh *me_eval; @@ -1105,6 +1159,9 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) else if (ctx->object->type == OB_FONT) { return &gen_dupli_verts_font; } + else if (ctx->object->type == OB_POINTCLOUD) { + return &gen_dupli_verts_pointcloud; + } } else if (transflag & OB_DUPLIFACES) { if (ctx->object->type == OB_MESH) { diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index f94ef946851..198ff5a0540 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -753,18 +753,26 @@ struct Ocean *BKE_ocean_add(void) return oc; } -bool BKE_ocean_ensure(struct OceanModifierData *omd) +bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution) { if (omd->ocean) { - return false; + /* Check that the ocean has the same resolution than we want now. */ + if (omd->ocean->_M == resolution * resolution) { + return false; + } + else { + BKE_ocean_free(omd->ocean); + } } omd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(omd->ocean, omd); + BKE_ocean_init_from_modifier(omd->ocean, omd, resolution); return true; } -void BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData const *omd) +void BKE_ocean_init_from_modifier(struct Ocean *ocean, + struct OceanModifierData const *omd, + const int resolution) { short do_heightfield, do_chop, do_normals, do_jacobian; @@ -774,9 +782,10 @@ void BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData do_jacobian = (omd->flag & MOD_OCEAN_GENERATE_FOAM); BKE_ocean_free_data(ocean); + BKE_ocean_init(ocean, - omd->resolution * omd->resolution, - omd->resolution * omd->resolution, + resolution * resolution, + resolution * resolution, omd->spatial_size, omd->spatial_size, omd->wind_velocity, @@ -1607,7 +1616,8 @@ void BKE_ocean_bake(struct Ocean *UNUSED(o), } void BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean), - struct OceanModifierData const *UNUSED(omd)) + struct OceanModifierData const *UNUSED(omd), + int UNUSED(resolution)) { } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 0ba5ec43318..e7ff53f27b6 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1493,6 +1493,8 @@ static void sculpt_update_object(Depsgraph *depsgraph, MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); const bool use_face_sets = (ob->mode & OB_MODE_SCULPT) != 0; + ss->depsgraph = depsgraph; + ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob); ss->show_mask = (sd->flags & SCULPT_HIDE_MASK) == 0; ss->show_face_sets = (sd->flags & SCULPT_HIDE_FACE_SETS) == 0; @@ -1691,7 +1693,6 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object) CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert); BKE_mesh_update_customdata_pointers(orig_me, true); DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY); - return; } void BKE_sculpt_update_object_for_edit( diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index a003daf1042..1df5cda0ce5 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -3871,7 +3871,7 @@ void BKE_particlesettings_clump_curve_init(ParticleSettings *part) cumap->cm[0].curve[1].x = 1.0f; cumap->cm[0].curve[1].y = 1.0f; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); part->clumpcurve = cumap; } @@ -3885,7 +3885,7 @@ void BKE_particlesettings_rough_curve_init(ParticleSettings *part) cumap->cm[0].curve[1].x = 1.0f; cumap->cm[0].curve[1].y = 1.0f; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); part->roughcurve = cumap; } @@ -3899,7 +3899,7 @@ void BKE_particlesettings_twist_curve_init(ParticleSettings *part) cumap->cm[0].curve[1].x = 1.0f; cumap->cm[0].curve[1].y = 1.0f; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); part->twistcurve = cumap; } diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index bec9cbbad79..6bfbb4b9d00 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -570,7 +570,7 @@ void psys_thread_context_free(ParticleThreadContext *ctx) } } -static void initialize_particle_texture(ParticleSimulationData *sim, ParticleData *pa, int p) +static void init_particle_texture(ParticleSimulationData *sim, ParticleData *pa, int p) { ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; @@ -595,7 +595,7 @@ static void initialize_particle_texture(ParticleSimulationData *sim, ParticleDat } /* set particle parameters that don't change during particle's life */ -void initialize_particle(ParticleSimulationData *sim, ParticleData *pa) +void init_particle(ParticleSimulationData *sim, ParticleData *pa) { ParticleSettings *part = sim->psys->part; float birth_time = (float)(pa - sim->psys->particles) / (float)sim->psys->totpart; @@ -629,7 +629,7 @@ static void initialize_all_particles(ParticleSimulationData *sim) LOOP_PARTICLES { if (!(emit_from_volume_grid && (pa->flag & PARS_UNEXIST) != 0)) { - initialize_particle(sim, pa); + init_particle(sim, pa); } } } @@ -1092,7 +1092,7 @@ void reset_particle(ParticleSimulationData *sim, ParticleData *pa, float dtime, * We could only do it now because we'll need to know coordinate * before sampling the texture. */ - initialize_particle_texture(sim, pa, p); + init_particle_texture(sim, pa, p); if (part->phystype == PART_PHYS_BOIDS && pa->boid) { BoidParticle *bpa = pa->boid; @@ -4584,7 +4584,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_ psys->dt_frac = get_base_time_step(part); } else if ((int)cfra == startframe) { - /* Variable time step; initialise to subframes */ + /* Variable time step; initialize to sub-frames. */ psys->dt_frac = get_base_time_step(part); } else if (psys->dt_frac < MIN_TIMESTEP) { diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 4b09f7542c7..c2c5b42dbb0 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1511,7 +1511,7 @@ static int ptcache_rigidbody_write(int index, void *rb_v, void **data, int UNUSE if (ob && ob->rigidbody_object) { RigidBodyOb *rbo = ob->rigidbody_object; - if (rbo->type == RBO_TYPE_ACTIVE) { + if (rbo->type == RBO_TYPE_ACTIVE && rbo->shared->physics_object != NULL) { #ifdef WITH_BULLET RB_body_get_position(rbo->shared->physics_object, rbo->pos); RB_body_get_orientation(rbo->shared->physics_object, rbo->orn); diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 4752782eaeb..7c335a8e98c 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -466,10 +466,10 @@ static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob) return shape; } -/* Create new physics sim collision shape for object and store it, - * or remove the existing one first and replace... +/* Helper function to create physics collision shape for object. + * Returns a new collision shape. */ -static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) +static rbCollisionShape *rigidbody_validate_sim_shape_helper(RigidBodyWorld *rbw, Object *ob) { RigidBodyOb *rbo = ob->rigidbody_object; rbCollisionShape *new_shape = NULL; @@ -484,12 +484,7 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) /* sanity check */ if (rbo == NULL) { - return; - } - - /* don't create a new shape if we already have one and don't want to rebuild it */ - if (rbo->shared->physics_shape && !rebuild) { - return; + return NULL; } /* if automatically determining dimensions, use the Object's boundbox @@ -539,7 +534,7 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) break; case RB_SHAPE_CONVEXH: - /* try to emged collision margin */ + /* try to embed collision margin */ has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f); if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && has_volume) { @@ -555,18 +550,69 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) case RB_SHAPE_TRIMESH: new_shape = rigidbody_get_shape_trimesh_from_mesh(ob); break; + case RB_SHAPE_COMPOUND: + new_shape = RB_shape_new_compound(); + rbCollisionShape *childShape = NULL; + float loc[3], rot[4]; + float mat[4][4]; + /* Add children to the compound shape */ + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, childObject) { + if (childObject->parent == ob) { + childShape = rigidbody_validate_sim_shape_helper(rbw, childObject); + if (childShape) { + BKE_object_matrix_local_get(childObject, mat); + mat4_to_loc_quat(loc, rot, mat); + RB_compound_add_child_shape(new_shape, childShape, loc, rot); + } + } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + + break; } - /* use box shape if we can't fall back to old shape */ - if (new_shape == NULL && rbo->shared->physics_shape == NULL) { + /* use box shape if it failed to create new shape */ + if (new_shape == NULL) { new_shape = RB_shape_new_box(size[0], size[1], size[2]); } + if (new_shape) { + RB_shape_set_margin(new_shape, RBO_GET_MARGIN(rbo)); + } + + return new_shape; +} + +/* Create new physics sim collision shape for object and store it, + * or remove the existing one first and replace... + */ +static void rigidbody_validate_sim_shape(RigidBodyWorld *rbw, Object *ob, bool rebuild) +{ + RigidBodyOb *rbo = ob->rigidbody_object; + rbCollisionShape *new_shape = NULL; + + /* sanity check */ + if (rbo == NULL) { + return; + } + + /* don't create a new shape if we already have one and don't want to rebuild it */ + if (rbo->shared->physics_shape && !rebuild) { + return; + } + + /* Also don't create a shape if this object is parent of a compound shape */ + if (ob->parent != NULL && ob->parent->rigidbody_object != NULL && + ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) { + return; + } + + new_shape = rigidbody_validate_sim_shape_helper(rbw, ob); + /* assign new collision shape if creation was successful */ if (new_shape) { if (rbo->shared->physics_shape) { RB_shape_delete(rbo->shared->physics_shape); } rbo->shared->physics_shape = new_shape; - RB_shape_set_margin(rbo->shared->physics_shape, RBO_GET_MARGIN(rbo)); } } @@ -750,7 +796,7 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool /* FIXME we shouldn't always have to rebuild collision shapes when rebuilding objects, * but it's needed for constraints to update correctly. */ if (rbo->shared->physics_shape == NULL || rebuild) { - rigidbody_validate_sim_shape(ob, true); + rigidbody_validate_sim_shape(rbw, ob, true); } if (rbo->shared->physics_object) { @@ -760,6 +806,12 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool /* remove rigid body if it already exists before creating a new one */ if (rbo->shared->physics_object) { RB_body_delete(rbo->shared->physics_object); + rbo->shared->physics_object = NULL; + } + /* Don't create rigid body object if the parent is a compound shape */ + if (ob->parent != NULL && ob->parent->rigidbody_object != NULL && + ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) { + return; } mat4_to_loc_quat(loc, rot, ob->obmat); @@ -793,7 +845,7 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED); } - if (rbw && rbw->shared->physics_world) { + if (rbw && rbw->shared->physics_world && rbo->shared->physics_object) { RB_dworld_add_body(rbw->shared->physics_world, rbo->shared->physics_object, rbo->col_groups); } } @@ -1179,9 +1231,12 @@ RigidBodyOb *BKE_rigidbody_create_object(Scene *scene, Object *ob, short type) * - object must exist * - cannot add rigid body if it already exists */ - if (ob == NULL || (ob->rigidbody_object != NULL)) { + if (ob == NULL) { return NULL; } + if (ob->rigidbody_object != NULL) { + return ob->rigidbody_object; + } /* create new settings data, and link it up */ rbo = MEM_callocN(sizeof(RigidBodyOb), "RigidBodyOb"); @@ -1530,7 +1585,11 @@ static void rigidbody_update_ob_array(RigidBodyWorld *rbw) int n = 0; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { (void)object; - n++; + /* Ignore if this object is the direct child of an object with a compound shape */ + if (object->parent == NULL || object->parent->rigidbody_object == NULL || + object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) { + n++; + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; @@ -1541,8 +1600,12 @@ static void rigidbody_update_ob_array(RigidBodyWorld *rbw) int i = 0; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { - rbw->objects[i] = object; - i++; + /* Ignore if this object is the direct child of an object with a compound shape */ + if (object->parent == NULL || object->parent->rigidbody_object == NULL || + object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) { + rbw->objects[i] = object; + i++; + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } @@ -1754,11 +1817,13 @@ static void rigidbody_update_simulation(Depsgraph *depsgraph, /* refresh shape... */ if (rbo->flag & RBO_FLAG_NEEDS_RESHAPE) { /* mesh/shape data changed, so force shape refresh */ - rigidbody_validate_sim_shape(ob, true); + rigidbody_validate_sim_shape(rbw, ob, true); /* now tell RB sim about it */ /* XXX: we assume that this can only get applied for active/passive shapes * that will be included as rigidbodies. */ - RB_body_set_collision_shape(rbo->shared->physics_object, rbo->shared->physics_shape); + if (rbo->shared->physics_object != NULL && rbo->shared->physics_shape != NULL) { + RB_body_set_collision_shape(rbo->shared->physics_object, rbo->shared->physics_shape); + } } } rbo->flag &= ~(RBO_FLAG_NEEDS_VALIDATE | RBO_FLAG_NEEDS_RESHAPE); @@ -1817,7 +1882,8 @@ static void rigidbody_update_simulation_post_step(Depsgraph *depsgraph, RigidBod Base *base = BKE_view_layer_base_find(view_layer, ob); RigidBodyOb *rbo = ob->rigidbody_object; /* Reset kinematic state for transformed objects. */ - if (rbo && base && (base->flag & BASE_SELECTED) && (G.moving & G_TRANSFORM_OBJ)) { + if (rbo && base && (base->flag & BASE_SELECTED) && (G.moving & G_TRANSFORM_OBJ) && + rbo->shared->physics_object) { RB_body_set_kinematic_state(rbo->shared->physics_object, rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED); RB_body_set_mass(rbo->shared->physics_object, RBO_GET_MASS(rbo)); @@ -1840,8 +1906,13 @@ void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) { RigidBodyOb *rbo = ob->rigidbody_object; + /* True if the shape of this object's parent is of type compound */ + bool obCompoundParent = (ob->parent != NULL && ob->parent->rigidbody_object != NULL && + ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND); + /* keep original transform for kinematic and passive objects */ - if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE) { + if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE || + obCompoundParent) { return; } @@ -1963,7 +2034,11 @@ void BKE_rigidbody_rebuild_world(Depsgraph *depsgraph, Scene *scene, float ctime int n = 0; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { (void)object; - n++; + /* Ignore if this object is the direct child of an object with a compound shape */ + if (object->parent == NULL || object->parent->rigidbody_object == NULL || + object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) { + n++; + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 5ae2f4b9005..fdec29dd43e 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -127,7 +127,7 @@ static void scene_init_data(ID *id) mblur_shutter_curve = &scene->r.mblur_shutter_curve; BKE_curvemapping_set_defaults(mblur_shutter_curve, 1, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(mblur_shutter_curve); + BKE_curvemapping_init(mblur_shutter_curve); BKE_curvemap_reset(mblur_shutter_curve->cm, &mblur_shutter_curve->clipr, CURVE_PRESET_MAX, @@ -140,13 +140,13 @@ static void scene_init_data(ID *id) /* grease pencil multiframe falloff curve */ scene->toolsettings->gp_sculpt.cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_falloff_curve = scene->toolsettings->gp_sculpt.cur_falloff; - BKE_curvemapping_initialize(gp_falloff_curve); + BKE_curvemapping_init(gp_falloff_curve); BKE_curvemap_reset( gp_falloff_curve->cm, &gp_falloff_curve->clipr, CURVE_PRESET_GAUSS, CURVEMAP_SLOPE_POSITIVE); scene->toolsettings->gp_sculpt.cur_primitive = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_primitive_curve = scene->toolsettings->gp_sculpt.cur_primitive; - BKE_curvemapping_initialize(gp_primitive_curve); + BKE_curvemapping_init(gp_primitive_curve); BKE_curvemap_reset(gp_primitive_curve->cm, &gp_primitive_curve->clipr, CURVE_PRESET_BELL, diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c index a630170d6d5..0bf7fffb833 100644 --- a/source/blender/blenkernel/intern/seqmodifier.c +++ b/source/blender/blenkernel/intern/seqmodifier.c @@ -396,7 +396,7 @@ static void curves_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *m float black[3] = {0.0f, 0.0f, 0.0f}; float white[3] = {1.0f, 1.0f, 1.0f}; - BKE_curvemapping_initialize(&cmd->curve_mapping); + BKE_curvemapping_init(&cmd->curve_mapping); BKE_curvemapping_premultiply(&cmd->curve_mapping, 0); BKE_curvemapping_set_black_white(&cmd->curve_mapping, black, white); @@ -525,7 +525,7 @@ static void hue_correct_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImB { HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd; - BKE_curvemapping_initialize(&hcmd->curve_mapping); + BKE_curvemapping_init(&hcmd->curve_mapping); modifier_apply_threaded(ibuf, mask, hue_correct_apply_threaded, &hcmd->curve_mapping); } diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 5481cfe8193..b0a8f709399 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -6082,6 +6082,64 @@ bool BKE_sequencer_render_loop_check(Sequence *seq_main, Sequence *seq) return false; } +static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq) +{ + LISTBASE_FOREACH (Sequence *, user_seq, seqbase) { + /* Look in metas for usage of seq. */ + if (user_seq->type == SEQ_TYPE_META) { + sequencer_flag_users_for_removal(scene, &user_seq->seqbase, seq); + } + + /* Clear seq from modifiers. */ + SequenceModifierData *smd; + for (smd = user_seq->modifiers.first; smd; smd = smd->next) { + if (smd->mask_sequence == seq) { + smd->mask_sequence = NULL; + } + } + + /* Remove effects, that use seq. */ + if ((user_seq->seq1 && user_seq->seq1 == seq) || (user_seq->seq2 && user_seq->seq2 == seq) || + (user_seq->seq3 && user_seq->seq3 == seq)) { + user_seq->flag |= SEQ_FLAG_DELETE; + /* Strips can be used as mask even if not in same seqbase. */ + sequencer_flag_users_for_removal(scene, &scene->ed->seqbase, user_seq); + } + } +} + +/* Flag seq and its users (effects) for removal. */ +void BKE_sequencer_flag_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq) +{ + if (seq == NULL || (seq->flag & SEQ_FLAG_DELETE) != 0) { + return; + } + + /* Flag and remove meta children. */ + if (seq->type == SEQ_TYPE_META) { + LISTBASE_FOREACH (Sequence *, meta_child, &seq->seqbase) { + BKE_sequencer_flag_for_removal(scene, &seq->seqbase, meta_child); + } + } + + seq->flag |= SEQ_FLAG_DELETE; + sequencer_flag_users_for_removal(scene, seqbase, seq); +} + +/* Remove all flagged sequences, return true if sequence is removed. */ +void BKE_sequencer_remove_flagged_sequences(Scene *scene, ListBase *seqbase) +{ + LISTBASE_FOREACH_MUTABLE (Sequence *, seq, seqbase) { + if (seq->flag & SEQ_FLAG_DELETE) { + if (seq->type == SEQ_TYPE_META) { + BKE_sequencer_remove_flagged_sequences(scene, &seq->seqbase); + } + BLI_remlink(seqbase, seq); + BKE_sequence_free(scene, seq, true); + } + } +} + void BKE_sequencer_check_uuids_unique_and_report(const Scene *scene) { if (scene->ed == NULL) { diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 95340e4e29c..c0fc8fcb464 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -112,8 +112,8 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons BKE_simulation_state_copy_data(state_src, state_dst); } - BLI_duplicatelist(&simulation_dst->persistent_data_handles, - &simulation_src->persistent_data_handles); + BLI_listbase_clear(&simulation_dst->dependencies); + BLI_duplicatelist(&simulation_dst->dependencies, &simulation_src->dependencies); } static void simulation_free_data(ID *id) @@ -130,7 +130,7 @@ static void simulation_free_data(ID *id) BKE_simulation_state_remove_all(simulation); - BLI_freelistN(&simulation->persistent_data_handles); + BLI_freelistN(&simulation->dependencies); } static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) @@ -140,9 +140,8 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree); } - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BKE_LIB_FOREACHID_PROCESS_ID(data, handle_item->id, IDWALK_CB_USER); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BKE_LIB_FOREACHID_PROCESS_ID(data, dependency->id, IDWALK_CB_USER); } } @@ -284,6 +283,14 @@ void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation * blender::sim::update_simulation_in_depsgraph(depsgraph, scene, simulation); } +void BKE_simulation_update_dependencies(Simulation *simulation, Main *bmain) +{ + bool dependencies_changed = blender::sim::update_simulation_dependencies(simulation); + if (dependencies_changed) { + DEG_relations_tag_update(bmain); + } +} + using StateTypeMap = blender::Map<std::string, std::unique_ptr<SimulationStateType>>; template<typename T> diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index bc1b79f62c5..c992990e0a0 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -1618,7 +1618,7 @@ static int prev_adjacent_edge_point_index(const SubdivCCG *subdiv_ccg, const int return point_index - 1; } -/* When the point index corresponds to a grid corner, returs the point index which corresponds to +/* When the point index corresponds to a grid corner, returns the point index which corresponds to * the corner of the adjacent grid, as the adjacent edge has two separate points for each grid * corner at the middle of the edge. */ static int adjacent_grid_corner_point_index_on_edge(const SubdivCCG *subdiv_ccg, @@ -1650,7 +1650,7 @@ static void neighbor_coords_edge_get(const SubdivCCG *subdiv_ccg, if (include_duplicates) { num_duplicates += num_adjacent_faces - 1; if (is_corner) { - /* When the coord is a grid corner, add an extra duplicate per adajacent grid in all adjacent + /* When the coord is a grid corner, add an extra duplicate per adjacent grid in all adjacent * faces to the edge. */ num_duplicates += num_adjacent_faces; } diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c index e09e92588c6..b9c6bb83157 100644 --- a/source/blender/blenkernel/intern/tracking_stabilize.c +++ b/source/blender/blenkernel/intern/tracking_stabilize.c @@ -215,7 +215,7 @@ static void use_values_from_fcurves(StabContext *ctx, bool toggle) /* Prepare per call private working area. * Used for access to possibly animated values: retrieve available F-curves. */ -static StabContext *initialize_stabilization_working_context(MovieClip *clip) +static StabContext *init_stabilization_working_context(MovieClip *clip) { StabContext *ctx = MEM_callocN(sizeof(StabContext), "2D stabilization animation runtime data"); ctx->clip = clip; @@ -841,14 +841,14 @@ static int establish_track_initialization_order(StabContext *ctx, TrackInitOrder * * NOTE: when done, this track is marked as initialized */ -static void initialize_track_for_stabilization(StabContext *ctx, - MovieTrackingTrack *track, - int reference_frame, - float aspect, - const float average_translation[2], - const float pivot[2], - const float average_angle, - const float average_scale_step) +static void init_track_for_stabilization(StabContext *ctx, + MovieTrackingTrack *track, + int reference_frame, + float aspect, + const float average_translation[2], + const float pivot[2], + const float average_angle, + const float average_scale_step) { float pos[2], angle, len; TrackStabilizationBase *local_data = access_stabilization_baseline_data(ctx, track); @@ -876,7 +876,7 @@ static void initialize_track_for_stabilization(StabContext *ctx, local_data->is_init_for_stabilization = true; } -static void initialize_all_tracks(StabContext *ctx, float aspect) +static void init_all_tracks(StabContext *ctx, float aspect) { size_t track_len = 0; MovieClip *clip = ctx->clip; @@ -936,14 +936,14 @@ static void initialize_all_tracks(StabContext *ctx, float aspect) &average_angle, &average_scale_step); } - initialize_track_for_stabilization(ctx, - track, - reference_frame, - aspect, - average_translation, - pivot, - average_angle, - average_scale_step); + init_track_for_stabilization(ctx, + track, + reference_frame, + aspect, + average_translation, + pivot, + average_angle, + average_scale_step); } cleanup: @@ -1257,9 +1257,9 @@ static float calculate_autoscale_factor(StabContext *ctx, int size, float aspect */ static StabContext *init_stabilizer(MovieClip *clip, int size, float aspect) { - StabContext *ctx = initialize_stabilization_working_context(clip); + StabContext *ctx = init_stabilization_working_context(clip); BLI_assert(ctx != NULL); - initialize_all_tracks(ctx, aspect); + init_all_tracks(ctx, aspect); if (ctx->stab->flag & TRACKING_AUTOSCALE) { ctx->stab->scale = 1.0; ctx->stab->scale = calculate_autoscale_factor(ctx, size, aspect); diff --git a/source/blender/blenlib/BLI_dial_2d.h b/source/blender/blenlib/BLI_dial_2d.h index f43237f6b75..fdbc3d4430b 100644 --- a/source/blender/blenlib/BLI_dial_2d.h +++ b/source/blender/blenlib/BLI_dial_2d.h @@ -37,7 +37,7 @@ * float angle; * Dial *dial; * - * dial = BLI_dial_initialize(start_position, threshold); + * dial = BLI_dial_init(start_position, threshold); * * angle = BLI_dial_angle(dial, current_position); * @@ -51,7 +51,7 @@ extern "C" { typedef struct Dial Dial; -Dial *BLI_dial_initialize(const float start_position[2], float threshold); +Dial *BLI_dial_init(const float start_position[2], float threshold); float BLI_dial_angle(Dial *dial, const float current_position[2]); diff --git a/source/blender/blenlib/BLI_dot_export.hh b/source/blender/blenlib/BLI_dot_export.hh index 2d6dbb1f600..a026a85dd62 100644 --- a/source/blender/blenlib/BLI_dot_export.hh +++ b/source/blender/blenlib/BLI_dot_export.hh @@ -43,9 +43,8 @@ class NodePort; class DirectedEdge; class UndirectedEdge; class Cluster; -class AttributeList; -class AttributeList { +class Attributes { private: Map<std::string, std::string> attributes_; @@ -56,11 +55,15 @@ class AttributeList { { attributes_.add_overwrite(key, value); } + + void set(StringRef key, float value) + { + attributes_.add_overwrite(key, std::to_string(value)); + } }; class Graph { private: - AttributeList attributes_; Vector<std::unique_ptr<Node>> nodes_; Vector<std::unique_ptr<Cluster>> clusters_; @@ -71,19 +74,17 @@ class Graph { friend Node; public: + Attributes attributes; + + public: Node &new_node(StringRef label); Cluster &new_cluster(StringRef label = ""); void export__declare_nodes_and_clusters(std::stringstream &ss) const; - void set_attribute(StringRef key, StringRef value) - { - attributes_.set(key, value); - } - void set_rankdir(Attr_rankdir rankdir) { - this->set_attribute("rankdir", rankdir_to_string(rankdir)); + attributes.set("rankdir", rankdir_to_string(rankdir)); } void set_random_cluster_bgcolors(); @@ -91,7 +92,6 @@ class Graph { class Cluster { private: - AttributeList attributes_; Graph &graph_; Cluster *parent_ = nullptr; Set<Cluster *> children_; @@ -100,6 +100,9 @@ class Cluster { friend Graph; friend Node; + public: + Attributes attributes; + Cluster(Graph &graph) : graph_(graph) { } @@ -107,9 +110,9 @@ class Cluster { public: void export__declare_nodes_and_clusters(std::stringstream &ss) const; - void set_attribute(StringRef key, StringRef value) + std::string name() const { - attributes_.set(key, value); + return "cluster_" + std::to_string((uintptr_t)this); } void set_parent_cluster(Cluster *cluster); @@ -118,53 +121,52 @@ class Cluster { this->set_parent_cluster(&cluster); } + Cluster *parent_cluster() + { + return parent_; + } + void set_random_cluster_bgcolors(); + + bool contains(Node &node) const; }; class Node { private: - AttributeList attributes_; Graph &graph_; Cluster *cluster_ = nullptr; friend Graph; - Node(Graph &graph) : graph_(graph) - { - } - public: - const AttributeList &attributes() const - { - return attributes_; - } + Attributes attributes; - AttributeList &attributes() + Node(Graph &graph) : graph_(graph) { - return attributes_; } + public: void set_parent_cluster(Cluster *cluster); void set_parent_cluster(Cluster &cluster) { this->set_parent_cluster(&cluster); } - void set_attribute(StringRef key, StringRef value) + Cluster *parent_cluster() { - attributes_.set(key, value); + return cluster_; } void set_shape(Attr_shape shape) { - this->set_attribute("shape", shape_to_string(shape)); + attributes.set("shape", shape_to_string(shape)); } /* See https://www.graphviz.org/doc/info/attrs.html#k:color. */ void set_background_color(StringRef name) { - this->set_attribute("fillcolor", name); - this->set_attribute("style", "filled"); + attributes.set("fillcolor", name); + attributes.set("style", "filled"); } void export__as_id(std::stringstream &ss) const; @@ -208,33 +210,35 @@ class NodePort { class Edge : blender::NonCopyable, blender::NonMovable { protected: - AttributeList attributes_; NodePort a_; NodePort b_; public: - Edge(NodePort a, NodePort b) : a_(std::move(a)), b_(std::move(b)) - { - } + Attributes attributes; - void set_attribute(StringRef key, StringRef value) + public: + Edge(NodePort a, NodePort b) : a_(std::move(a)), b_(std::move(b)) { - attributes_.set(key, value); } void set_arrowhead(Attr_arrowType type) { - this->set_attribute("arrowhead", arrowType_to_string(type)); + attributes.set("arrowhead", arrowType_to_string(type)); } void set_arrowtail(Attr_arrowType type) { - this->set_attribute("arrowtail", arrowType_to_string(type)); + attributes.set("arrowtail", arrowType_to_string(type)); } void set_dir(Attr_dirType type) { - this->set_attribute("dir", dirType_to_string(type)); + attributes.set("dir", dirType_to_string(type)); + } + + void set_label(StringRef label) + { + attributes.set("label", label); } }; diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 85575f65365..a976e909738 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -62,11 +62,12 @@ struct float3 { return {a.x + b.x, a.y + b.y, a.z + b.z}; } - void operator+=(const float3 &b) + float3 &operator+=(const float3 &b) { this->x += b.x; this->y += b.y; this->z += b.z; + return *this; } friend float3 operator-(const float3 &a, const float3 &b) @@ -79,25 +80,28 @@ struct float3 { return {-a.x, -a.y, -a.z}; } - void operator-=(const float3 &b) + float3 &operator-=(const float3 &b) { this->x -= b.x; this->y -= b.y; this->z -= b.z; + return *this; } - void operator*=(float scalar) + float3 &operator*=(float scalar) { this->x *= scalar; this->y *= scalar; this->z *= scalar; + return *this; } - void operator*=(const float3 &other) + float3 &operator*=(const float3 &other) { this->x *= other.x; this->y *= other.y; this->z *= other.z; + return *this; } friend float3 operator*(const float3 &a, const float3 &b) @@ -142,6 +146,17 @@ struct float3 { return normalize_v3(*this); } + /** + * Normalizes the vector in place. + */ + void normalize() + { + normalize_v3(*this); + } + + /** + * Returns a normalized vector. The original vector is not changed. + */ float3 normalized() const { float3 result; diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index a8b939ed32e..85d38149bb9 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -70,8 +70,8 @@ struct float4x4 { float4x4 inverted() const { - float result[4][4]; - invert_m4_m4(result, values); + float4x4 result; + invert_m4_m4(result.values, values); return result; } @@ -85,6 +85,18 @@ struct float4x4 { return this->inverted(); } + float4x4 transposed() const + { + float4x4 result; + transpose_m4_m4(result.values, values); + return result; + } + + float4x4 inverted_transposed_affine() const + { + return this->inverted_affine().transposed(); + } + struct float3x3_ref { const float4x4 &data; diff --git a/source/blender/blenlib/BLI_multi_value_map.hh b/source/blender/blenlib/BLI_multi_value_map.hh new file mode 100644 index 00000000000..c20c4ef9677 --- /dev/null +++ b/source/blender/blenlib/BLI_multi_value_map.hh @@ -0,0 +1,134 @@ +/* + * 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_MULTI_VALUE_MAP_HH__ +#define __BLI_MULTI_VALUE_MAP_HH__ + +/** \file + * \ingroup bli + * + * A `blender::MultiValueMap<Key, Value>` is an unordered associative container that stores + * key-value pairs. It is different from `blender::Map` in that it can store multiple values for + * the same key. The list of values that corresponds to a specific key can contain duplicates + * and their order is maintained. + * + * This data structure is different from a `std::multi_map`, because multi_map can store the same + * key more than once and MultiValueMap can't. + * + * Currently, this class exists mainly for convenience. There are no performance benefits over + * using Map<Key, Vector<Value>>. In the future, a better implementation for this data structure + * can be developed. + */ + +#include "BLI_map.hh" +#include "BLI_vector.hh" + +namespace blender { + +template<typename Key, typename Value> class MultiValueMap { + private: + using MapType = Map<Key, Vector<Value>>; + MapType map_; + + public: + /** + * Add a new value for the given key. If the map contains the key already, the value will be + * appended to the list of corresponding values. + */ + void add(const Key &key, const Value &value) + { + this->add_as(key, value); + } + void add(const Key &key, Value &&value) + { + this->add_as(key, std::move(value)); + } + void add(Key &&key, const Value &value) + { + this->add_as(std::move(key), value); + } + void add(Key &&key, Value &&value) + { + this->add_as(std::move(key), std::move(value)); + } + template<typename ForwardKey, typename ForwardValue> + void add_as(ForwardKey &&key, ForwardValue &&value) + { + Vector<Value> &vector = map_.lookup_or_add_default_as(std::forward<ForwardKey>(key)); + vector.append(std::forward<ForwardValue>(value)); + } + + /** + * Add all given values to the key. + */ + void add_multiple(const Key &key, Span<Value> values) + { + this->add_multiple_as(key, values); + } + void add_multiple(Key &&key, Span<Value> values) + { + this->add_multiple_as(std::move(key), values); + } + template<typename ForwardKey> void add_multiple_as(ForwardKey &&key, Span<Value> values) + { + Vector<Value> &vector = map_.lookup_or_add_default_as(std::forward<ForwardKey>(key)); + vector.extend(values); + } + + /** + * Get a span to all the values that are stored for the given key. + */ + Span<Value> lookup(const Key &key) const + { + return this->lookup_as(key); + } + template<typename ForwardKey> Span<Value> lookup_as(const ForwardKey &key) const + { + const Vector<Value> *vector = map_.lookup_ptr_as(key); + if (vector != nullptr) { + return vector->as_span(); + } + return {}; + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::ItemIterator items() const + { + return map_.items(); + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::KeyIterator keys() const + { + return map_.keys(); + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::ValueIterator values() const + { + return map_.values(); + } +}; + +} // namespace blender + +#endif /* __BLI_MULTI_VALUE_MAP_HH__ */ diff --git a/source/blender/blenlib/BLI_rand.hh b/source/blender/blenlib/BLI_rand.hh index 72c750d9fa2..b3c9c376141 100644 --- a/source/blender/blenlib/BLI_rand.hh +++ b/source/blender/blenlib/BLI_rand.hh @@ -62,6 +62,15 @@ class RandomNumberGenerator { } /** + * \return Random value (0..N), but never N. + */ + int32_t get_int32(int32_t max_exclusive) + { + BLI_assert(max_exclusive > 0); + return this->get_int32() % max_exclusive; + } + + /** * \return Random value (0..1), but never 1.0. */ double get_double() @@ -77,6 +86,35 @@ class RandomNumberGenerator { return (float)this->get_int32() / 0x80000000; } + template<typename T> void shuffle(MutableSpan<T> values) + { + /* Cannot shuffle arrays of this size yet. */ + BLI_assert(values.size() <= INT32_MAX); + + for (int i = values.size() - 1; i >= 2; i--) { + int j = this->get_int32(i); + if (i != j) { + std::swap(values[i], values[j]); + } + } + } + + /** + * Compute uniformly distributed barycentric coordinates. + */ + float3 get_barycentric_coordinates() + { + float rand1 = this->get_float(); + float rand2 = this->get_float(); + + if (rand1 + rand2 > 1.0f) { + rand1 = 1.0f - rand1; + rand2 = 1.0f - rand2; + } + + return float3(rand1, rand2, 1.0f - rand1 - rand2); + } + float2 get_unit_float2(); float3 get_unit_float3(); float2 get_triangle_sample(float2 v1, float2 v2, float2 v3); diff --git a/source/blender/blenlib/BLI_resource_collector.hh b/source/blender/blenlib/BLI_resource_collector.hh index 9c8fefc1202..20180f3b2c9 100644 --- a/source/blender/blenlib/BLI_resource_collector.hh +++ b/source/blender/blenlib/BLI_resource_collector.hh @@ -78,6 +78,12 @@ class ResourceCollector : NonCopyable, NonMovable { */ template<typename T> void add(destruct_ptr<T> resource, const char *name) { + /* There is no need to keep track of such types. */ + if (std::is_trivially_destructible_v<T>) { + resource.release(); + return; + } + BLI_assert(resource.get() != nullptr); this->add( resource.release(), diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index a2de76e080e..5aee0028846 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -86,7 +86,7 @@ namespace blender { */ template<typename T> class Span { private: - const T *start_ = nullptr; + const T *data_ = nullptr; int64_t size_ = 0; public: @@ -95,13 +95,13 @@ template<typename T> class Span { */ Span() = default; - Span(const T *start, int64_t size) : start_(start), size_(size) + Span(const T *start, int64_t size) : data_(start), size_(size) { BLI_assert(size >= 0); } template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(const U *start, int64_t size) : start_((const T *)start), size_(size) + Span(const U *start, int64_t size) : data_((const T *)start), size_(size) { BLI_assert(size >= 0); } @@ -135,7 +135,7 @@ template<typename T> class Span { * Span<Derived *> -> Span<Base *> */ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(Span<U> array) : start_((T *)array.data()), size_(array.size()) + Span(Span<U> array) : data_((T *)array.data()), size_(array.size()) { } @@ -148,7 +148,7 @@ template<typename T> class Span { BLI_assert(start >= 0); BLI_assert(size >= 0); BLI_assert(start + size <= this->size() || size == 0); - return Span(start_ + start, size); + return Span(data_ + start, size); } Span slice(IndexRange range) const @@ -206,17 +206,17 @@ template<typename T> class Span { */ const T *data() const { - return start_; + return data_; } const T *begin() const { - return start_; + return data_; } const T *end() const { - return start_ + size_; + return data_ + size_; } /** @@ -227,7 +227,7 @@ template<typename T> class Span { { BLI_assert(index >= 0); BLI_assert(index < size_); - return start_[index]; + return data_[index]; } /** @@ -299,7 +299,7 @@ template<typename T> class Span { const T &first() const { BLI_assert(size_ > 0); - return start_[0]; + return data_[0]; } /** @@ -309,7 +309,7 @@ template<typename T> class Span { const T &last() const { BLI_assert(size_ > 0); - return start_[size_ - 1]; + return data_[size_ - 1]; } /** @@ -319,7 +319,7 @@ template<typename T> class Span { T get(int64_t index, const T &fallback) const { if (index < size_ && index >= 0) { - return start_[index]; + return data_[index]; } return fallback; } @@ -335,9 +335,9 @@ template<typename T> class Span { BLI_assert(size_ < 1000); for (int64_t i = 0; i < size_; i++) { - const T &value = start_[i]; + const T &value = data_[i]; for (int64_t j = i + 1; j < size_; j++) { - if (value == start_[j]) { + if (value == data_[j]) { return true; } } @@ -357,7 +357,7 @@ template<typename T> class Span { BLI_assert(size_ < 1000); for (int64_t i = 0; i < size_; i++) { - const T &value = start_[i]; + const T &value = data_[i]; if (other.contains(value)) { return true; } @@ -382,7 +382,7 @@ template<typename T> class Span { int64_t first_index_try(const T &search_value) const { for (int64_t i = 0; i < size_; i++) { - if (start_[i] == search_value) { + if (data_[i] == search_value) { return i; } } @@ -405,7 +405,7 @@ template<typename T> class Span { { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); - return Span<NewT>(reinterpret_cast<const NewT *>(start_), new_size); + return Span<NewT>(reinterpret_cast<const NewT *>(data_), new_size); } /** @@ -438,13 +438,13 @@ template<typename T> class Span { */ template<typename T> class MutableSpan { private: - T *start_; + T *data_; int64_t size_; public: MutableSpan() = default; - MutableSpan(T *start, const int64_t size) : start_(start), size_(size) + MutableSpan(T *start, const int64_t size) : data_(start), size_(size) { } @@ -458,7 +458,7 @@ template<typename T> class MutableSpan { operator Span<T>() const { - return Span<T>(start_, size_); + return Span<T>(data_, size_); } /** @@ -474,7 +474,7 @@ template<typename T> class MutableSpan { */ void fill(const T &value) { - initialized_fill_n(start_, size_, value); + initialized_fill_n(data_, size_, value); } /** @@ -485,7 +485,7 @@ template<typename T> class MutableSpan { { for (int64_t i : indices) { BLI_assert(i < size_); - start_[i] = value; + data_[i] = value; } } @@ -495,23 +495,23 @@ template<typename T> class MutableSpan { */ T *data() const { - return start_; + return data_; } T *begin() const { - return start_; + return data_; } T *end() const { - return start_ + size_; + return data_ + size_; } T &operator[](const int64_t index) const { BLI_assert(index < this->size()); - return start_[index]; + return data_[index]; } /** @@ -521,7 +521,7 @@ template<typename T> class MutableSpan { MutableSpan slice(const int64_t start, const int64_t length) const { BLI_assert(start + length <= this->size()); - return MutableSpan(start_ + start, length); + return MutableSpan(data_ + start, length); } /** @@ -570,7 +570,7 @@ template<typename T> class MutableSpan { */ Span<T> as_span() const { - return Span<T>(start_, size_); + return Span<T>(data_, size_); } /** @@ -589,7 +589,7 @@ template<typename T> class MutableSpan { T &last() const { BLI_assert(size_ > 0); - return start_[size_ - 1]; + return data_[size_ - 1]; } /** @@ -608,13 +608,24 @@ template<typename T> class MutableSpan { } /** + * Copy all values from another span into this span. This invokes undefined behavior when the + * destination contains uninitialized data and T is not trivially copy constructible. + * The size of both spans is expected to be the same. + */ + void copy_from(Span<T> values) + { + BLI_assert(size_ == values.size()); + initialized_copy_n(values.data(), size_, data_); + } + + /** * Returns a new span to the same underlying memory buffer. No conversions are done. */ template<typename NewT> MutableSpan<NewT> cast() const { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); - return MutableSpan<NewT>(reinterpret_cast<NewT *>(start_), new_size); + return MutableSpan<NewT>(reinterpret_cast<NewT *>(data_), new_size); } }; diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index bc35e969e6a..2699f2498ac 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -755,6 +755,43 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); /** \} */ /* -------------------------------------------------------------------- */ +/** \name C++ Macros + * \{ */ + +#ifdef __cplusplus + +/* Useful to port C code using enums to C++ where enums are strongly typed. + * To use after the enum declaration. */ +# define ENUM_OPERATORS(_enum_type) \ + inline constexpr _enum_type operator|(_enum_type a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) | b); \ + } \ + inline constexpr _enum_type operator&(_enum_type a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) & b); \ + } \ + inline constexpr _enum_type operator~(_enum_type a) \ + { \ + return a = static_cast<_enum_type>(~static_cast<int>(a)); \ + } \ + inline _enum_type &operator|=(_enum_type &a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) | b); \ + } \ + inline _enum_type &operator&=(_enum_type &a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) & b); \ + } + +#else +/* Output nothing. */ +# define ENUM_OPERATORS(_type) +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Misc Macros * \{ */ diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index df577660f4e..52e966267b5 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -560,7 +560,7 @@ class Vector { /** * Return a reference to the last element in the vector. - * This will assert when the vector is empty. + * This invokes undefined behavior when the vector is empty. */ const T &last() const { diff --git a/source/blender/blenlib/BLI_vector_adaptor.hh b/source/blender/blenlib/BLI_vector_adaptor.hh new file mode 100644 index 00000000000..cadffc0b445 --- /dev/null +++ b/source/blender/blenlib/BLI_vector_adaptor.hh @@ -0,0 +1,105 @@ +/* + * 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_VECTOR_ADAPTOR_HH__ +#define __BLI_VECTOR_ADAPTOR_HH__ + +/** \file + * \ingroup bli + * + * A `blender::VectorAdaptor` is a container with a fixed maximum size and does not own the + * underlying memory. When an adaptor is constructed, you have to provide it with an uninitialized + * array that will be filled when elements are added to the vector. The vector adaptor is not able + * to grow. Therefore, it is undefined behavior to add more elements than fit into the provided + * buffer. + */ + +#include "BLI_span.hh" + +namespace blender { + +template<typename T> class VectorAdaptor { + private: + T *begin_; + T *end_; + T *capacity_end_; + + public: + VectorAdaptor() : begin_(nullptr), end_(nullptr), capacity_end_(nullptr) + { + } + + VectorAdaptor(T *data, int64_t capacity, int64_t size = 0) + : begin_(data), end_(data + size), capacity_end_(data + capacity) + { + } + + VectorAdaptor(MutableSpan<T> span) : VectorAdaptor(span.data(), span.size(), 0) + { + } + + void append(const T &value) + { + BLI_assert(end_ < capacity_end_); + new (end_) T(value); + end_++; + } + + void append(T &&value) + { + BLI_assert(end_ < capacity_end_); + new (end_) T(std::move(value)); + end_++; + } + + void append_n_times(const T &value, int64_t n) + { + BLI_assert(end_ + n <= capacity_end_); + uninitialized_fill_n(end_, n, value); + end_ += n; + } + + void extend(Span<T> values) + { + BLI_assert(end_ + values.size() <= capacity_end_); + uninitialized_copy_n(values.data(), values.size(), end_); + end_ += values.size(); + } + + int64_t capacity() const + { + return capacity_end_ - begin_; + } + + int64_t size() const + { + return end_ - begin_; + } + + bool is_empty() const + { + return begin_ == end_; + } + + bool is_full() const + { + return end_ == capacity_end_; + } +}; + +} // namespace blender + +#endif /* __BLI_VECTOR_ADAPTOR_HH__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 9703c78e19c..a5af517ecca 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -270,6 +270,7 @@ set(SRC BLI_utility_mixins.hh BLI_uvproject.h BLI_vector.hh + BLI_vector_adaptor.hh BLI_vector_set.hh BLI_vector_set_slots.hh BLI_vfontdata.h @@ -343,3 +344,29 @@ set_source_files_properties( ) blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_GTESTS) + set(TEST_SRC + tests/BLI_array_test.cc + tests/BLI_disjoint_set_test.cc + tests/BLI_edgehash_test.cc + tests/BLI_index_mask_test.cc + tests/BLI_index_range_test.cc + tests/BLI_linear_allocator_test.cc + tests/BLI_map_test.cc + tests/BLI_math_base_safe_test.cc + tests/BLI_memory_utils_test.cc + tests/BLI_multi_value_map_test.cc + tests/BLI_set_test.cc + tests/BLI_span_test.cc + tests/BLI_stack_cxx_test.cc + tests/BLI_string_ref_test.cc + tests/BLI_vector_set_test.cc + tests/BLI_vector_test.cc + ) + set(TEST_LIB + bf_blenlib + ) + include(GTestTesting) + blender_add_test_lib(bf_bli_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") +endif() diff --git a/source/blender/blenlib/intern/BLI_dial_2d.c b/source/blender/blenlib/intern/BLI_dial_2d.c index c6d28e20f35..7363233d573 100644 --- a/source/blender/blenlib/intern/BLI_dial_2d.c +++ b/source/blender/blenlib/intern/BLI_dial_2d.c @@ -45,7 +45,7 @@ struct Dial { bool initialized; }; -Dial *BLI_dial_initialize(const float start_position[2], float threshold) +Dial *BLI_dial_init(const float start_position[2], float threshold) { Dial *dial = MEM_callocN(sizeof(Dial), "dial"); diff --git a/source/blender/blenlib/intern/dot_export.cc b/source/blender/blenlib/intern/dot_export.cc index 48b6dc826d0..8e4aafa4bb8 100644 --- a/source/blender/blenlib/intern/dot_export.cc +++ b/source/blender/blenlib/intern/dot_export.cc @@ -28,7 +28,7 @@ Node &Graph::new_node(StringRef label) Node *node = new Node(*this); nodes_.append(std::unique_ptr<Node>(node)); top_level_nodes_.add_new(node); - node->set_attribute("label", label); + node->attributes.set("label", label); return *node; } @@ -37,7 +37,7 @@ Cluster &Graph::new_cluster(StringRef label) Cluster *cluster = new Cluster(*this); clusters_.append(std::unique_ptr<Cluster>(cluster)); top_level_clusters_.add_new(cluster); - cluster->set_attribute("label", label); + cluster->attributes.set("label", label); return *cluster; } @@ -110,13 +110,25 @@ void Cluster::set_random_cluster_bgcolors() float hue = rand() / (float)RAND_MAX; float staturation = 0.3f; float value = 0.8f; - this->set_attribute("bgcolor", color_attr_from_hsv(hue, staturation, value)); + this->attributes.set("bgcolor", color_attr_from_hsv(hue, staturation, value)); for (Cluster *cluster : children_) { cluster->set_random_cluster_bgcolors(); } } +bool Cluster::contains(Node &node) const +{ + Cluster *current = node.parent_cluster(); + while (current != nullptr) { + if (current == this) { + return true; + } + current = current->parent_; + } + return false; +} + /* Dot Generation **********************************************/ @@ -155,7 +167,7 @@ std::string UndirectedGraph::to_dot_string() const void Graph::export__declare_nodes_and_clusters(std::stringstream &ss) const { ss << "graph "; - attributes_.export__as_bracket_list(ss); + attributes.export__as_bracket_list(ss); ss << "\n\n"; for (Node *node : top_level_nodes_) { @@ -169,10 +181,10 @@ void Graph::export__declare_nodes_and_clusters(std::stringstream &ss) const void Cluster::export__declare_nodes_and_clusters(std::stringstream &ss) const { - ss << "subgraph cluster_" << (uintptr_t)this << " {\n"; + ss << "subgraph " << this->name() << " {\n"; ss << "graph "; - attributes_.export__as_bracket_list(ss); + attributes.export__as_bracket_list(ss); ss << "\n\n"; for (Node *node : nodes_) { @@ -192,7 +204,7 @@ void DirectedEdge::export__as_edge_statement(std::stringstream &ss) const ss << " -> "; b_.to_dot_string(ss); ss << " "; - attributes_.export__as_bracket_list(ss); + attributes.export__as_bracket_list(ss); } void UndirectedEdge::export__as_edge_statement(std::stringstream &ss) const @@ -201,10 +213,10 @@ void UndirectedEdge::export__as_edge_statement(std::stringstream &ss) const ss << " -- "; b_.to_dot_string(ss); ss << " "; - attributes_.export__as_bracket_list(ss); + attributes.export__as_bracket_list(ss); } -void AttributeList::export__as_bracket_list(std::stringstream &ss) const +void Attributes::export__as_bracket_list(std::stringstream &ss) const { ss << "["; attributes_.foreach_item([&](StringRef key, StringRef value) { @@ -228,7 +240,7 @@ void Node::export__as_declaration(std::stringstream &ss) const { this->export__as_id(ss); ss << " "; - attributes_.export__as_bracket_list(ss); + attributes.export__as_bracket_list(ss); ss << "\n"; } @@ -296,7 +308,7 @@ NodeWithSocketsRef::NodeWithSocketsRef(Node &node, ss << "</table>>"; - node_->set_attribute("label", ss.str()); + node_->attributes.set("label", ss.str()); node_->set_shape(Attr_shape::Rectangle); } diff --git a/tests/gtests/blenlib/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc index c01ba26ffd7..7348a6f93f3 100644 --- a/tests/gtests/blenlib/BLI_array_test.cc +++ b/source/blender/blenlib/tests/BLI_array_test.cc @@ -4,7 +4,7 @@ #include "BLI_strict_flags.h" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(array, DefaultConstructor) { @@ -72,7 +72,7 @@ TEST(array, MoveConstructor) Array<int> array = {5, 6, 7, 8}; Array<int> new_array(std::move(array)); - EXPECT_EQ(array.size(), 0); + EXPECT_EQ(array.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(new_array.size(), 4); EXPECT_EQ(new_array[0], 5); EXPECT_EQ(new_array[1], 6); @@ -101,7 +101,7 @@ TEST(array, MoveAssignment) EXPECT_EQ(new_array.size(), 1); new_array = std::move(array); EXPECT_EQ(new_array.size(), 3); - EXPECT_EQ(array.size(), 0); + EXPECT_EQ(array.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(new_array[0], 1); EXPECT_EQ(new_array[1], 2); EXPECT_EQ(new_array[2], 3); @@ -141,7 +141,7 @@ TEST(array, NoInitializationSizeConstructor) using MyArray = Array<ConstructibleType>; TypedBuffer<MyArray> buffer; - memset(buffer, 100, sizeof(MyArray)); + memset((void *)&buffer, 100, sizeof(MyArray)); /* Doing this to avoid some compiler optimization. */ for (int64_t i : IndexRange(sizeof(MyArray))) { @@ -173,4 +173,4 @@ TEST(array, Fill) EXPECT_EQ(array[4], 3); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_disjoint_set_test.cc b/source/blender/blenlib/tests/BLI_disjoint_set_test.cc index 22663b936d9..f30ee610b2a 100644 --- a/tests/gtests/blenlib/BLI_disjoint_set_test.cc +++ b/source/blender/blenlib/tests/BLI_disjoint_set_test.cc @@ -5,7 +5,7 @@ #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(disjoint_set, Test) { @@ -33,4 +33,4 @@ TEST(disjoint_set, Test) EXPECT_FALSE(disjoint_set.in_same_set(0, 4)); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_edgehash_test.cc b/source/blender/blenlib/tests/BLI_edgehash_test.cc index 23ad618825b..7106033df36 100644 --- a/tests/gtests/blenlib/BLI_edgehash_test.cc +++ b/source/blender/blenlib/tests/BLI_edgehash_test.cc @@ -5,10 +5,8 @@ #include <random> #include <vector> -extern "C" { #include "BLI_edgehash.h" #include "BLI_utildefines.h" -} #define VALUE_1 POINTER_FROM_INT(1) #define VALUE_2 POINTER_FROM_INT(2) @@ -334,10 +332,12 @@ TEST(edgehash, StressTest) /* check if the right ones have been removed */ for (int i = 0; i < shuffled.size(); i++) { bool haskey = BLI_edgehash_haskey(eh, shuffled[i].v1, shuffled[i].v2); - if (i < remove_until) + if (i < remove_until) { ASSERT_FALSE(haskey); - else + } + else { ASSERT_TRUE(haskey); + } } /* reinsert all edges */ diff --git a/tests/gtests/blenlib/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc index bce467cadd9..4d6060e51c9 100644 --- a/tests/gtests/blenlib/BLI_index_mask_test.cc +++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc @@ -3,7 +3,7 @@ #include "BLI_index_mask.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(index_mask, DefaultConstructor) { @@ -40,4 +40,4 @@ TEST(index_mask, RangeConstructor) EXPECT_EQ(indices[2], 5); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc index 1c4d4a1a1f3..d472ded0f18 100644 --- a/tests/gtests/blenlib/BLI_index_range_test.cc +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -5,7 +5,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(index_range, DefaultConstructor) { @@ -140,4 +140,4 @@ TEST(index_range, AsSpan) EXPECT_EQ(span[3], 7); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc index 9c2dcdc7457..44b70d1f55d 100644 --- a/tests/gtests/blenlib/BLI_linear_allocator_test.cc +++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc @@ -4,7 +4,7 @@ #include "BLI_strict_flags.h" #include "testing/testing.h" -namespace blender { +namespace blender::tests { static bool is_aligned(void *ptr, uint alignment) { @@ -115,4 +115,4 @@ TEST(linear_allocator, ConstructArrayCopy) EXPECT_EQ(span2[2], 3); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc index cebce218112..fe7b0f01279 100644 --- a/tests/gtests/blenlib/BLI_map_test.cc +++ b/source/blender/blenlib/tests/BLI_map_test.cc @@ -8,7 +8,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(map, DefaultConstructor) { @@ -335,7 +335,7 @@ TEST(map, MoveConstructorSmall) EXPECT_EQ(map2.size(), 2); EXPECT_EQ(map2.lookup(1), 2.0f); EXPECT_EQ(map2.lookup(4), 1.0f); - EXPECT_EQ(map1.size(), 0); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(map1.lookup_ptr(4), nullptr); } @@ -349,7 +349,7 @@ TEST(map, MoveConstructorLarge) EXPECT_EQ(map2.size(), 100); EXPECT_EQ(map2.lookup(1), 1); EXPECT_EQ(map2.lookup(4), 4); - EXPECT_EQ(map1.size(), 0); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(map1.lookup_ptr(4), nullptr); } @@ -363,7 +363,7 @@ TEST(map, MoveAssignment) EXPECT_EQ(map2.size(), 2); EXPECT_EQ(map2.lookup(1), 2.0f); EXPECT_EQ(map2.lookup(4), 1.0f); - EXPECT_EQ(map1.size(), 0); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(map1.lookup_ptr(4), nullptr); } @@ -587,4 +587,4 @@ TEST(map, Benchmark) #endif /* Benchmark */ -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_math_base_safe_test.cc b/source/blender/blenlib/tests/BLI_math_base_safe_test.cc index 2e3e083cf92..2e3e083cf92 100644 --- a/tests/gtests/blenlib/BLI_math_base_safe_test.cc +++ b/source/blender/blenlib/tests/BLI_math_base_safe_test.cc diff --git a/tests/gtests/blenlib/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index b99db5c5eca..f3cb02b63d7 100644 --- a/tests/gtests/blenlib/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -5,7 +5,7 @@ #include "BLI_strict_flags.h" #include "testing/testing.h" -namespace blender { +namespace blender::tests { struct MyValue { static inline int alive = 0; @@ -19,7 +19,7 @@ struct MyValue { alive++; } - MyValue(const MyValue &other) + MyValue(const MyValue &UNUSED(other)) { if (alive == 15) { throw std::exception(); @@ -156,4 +156,4 @@ static_assert(is_convertible_pointer_v<int **, int **const>); static_assert(is_convertible_pointer_v<int **, int *const *>); static_assert(is_convertible_pointer_v<int **, int const *const *>); -} // namespace blender +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_multi_value_map_test.cc b/source/blender/blenlib/tests/BLI_multi_value_map_test.cc new file mode 100644 index 00000000000..7501fbe0d87 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_multi_value_map_test.cc @@ -0,0 +1,109 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_multi_value_map.hh" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(multi_value_map, LookupNotExistant) +{ + MultiValueMap<int, int> map; + EXPECT_EQ(map.lookup(5).size(), 0); + map.add(2, 5); + EXPECT_EQ(map.lookup(5).size(), 0); +} + +TEST(multi_value_map, LookupExistant) +{ + MultiValueMap<int, int> map; + map.add(2, 4); + map.add(2, 5); + map.add(3, 6); + + EXPECT_EQ(map.lookup(2).size(), 2); + EXPECT_EQ(map.lookup(2)[0], 4); + EXPECT_EQ(map.lookup(2)[1], 5); + + EXPECT_EQ(map.lookup(3).size(), 1); + EXPECT_EQ(map.lookup(3)[0], 6); +} + +TEST(multi_value_map, AddMultiple) +{ + MultiValueMap<int, int> map; + map.add_multiple(2, {4, 5, 6}); + map.add_multiple(2, {1, 2}); + map.add_multiple(5, {7, 5, 3}); + + EXPECT_EQ(map.lookup(2).size(), 5); + EXPECT_EQ(map.lookup(2)[0], 4); + EXPECT_EQ(map.lookup(2)[1], 5); + EXPECT_EQ(map.lookup(2)[2], 6); + EXPECT_EQ(map.lookup(2)[3], 1); + EXPECT_EQ(map.lookup(2)[4], 2); + + EXPECT_EQ(map.lookup(5).size(), 3); + EXPECT_EQ(map.lookup(5)[0], 7); + EXPECT_EQ(map.lookup(5)[1], 5); + EXPECT_EQ(map.lookup(5)[2], 3); +} + +TEST(multi_value_map, Keys) +{ + MultiValueMap<int, int> map; + map.add(5, 7); + map.add(5, 7); + map.add_multiple(2, {6, 7, 8}); + + Vector<int> keys; + for (int key : map.keys()) { + keys.append(key); + } + + EXPECT_EQ(keys.size(), 2); + EXPECT_TRUE(keys.contains(5)); + EXPECT_TRUE(keys.contains(2)); +} + +TEST(multi_value_map, Values) +{ + MultiValueMap<int, int> map; + map.add(3, 5); + map.add_multiple(3, {1, 2}); + map.add(6, 1); + + Vector<Span<int>> values; + for (Span<int> value_span : map.values()) { + values.append(value_span); + } + + EXPECT_EQ(values.size(), 2); +} + +TEST(multi_value_map, Items) +{ + MultiValueMap<int, int> map; + map.add_multiple(4, {1, 2, 3}); + + for (auto &&item : map.items()) { + int key = item.key; + Span<int> values = item.value; + EXPECT_EQ(key, 4); + EXPECT_EQ(values.size(), 3); + EXPECT_EQ(values[0], 1); + EXPECT_EQ(values[1], 2); + EXPECT_EQ(values[2], 3); + } +} + +TEST(multi_value_map, UniquePtr) +{ + /* Mostly testing if it compiles here. */ + MultiValueMap<std::unique_ptr<int>, std::unique_ptr<int>> map; + map.add(std::make_unique<int>(4), std::make_unique<int>(6)); + map.add(std::make_unique<int>(4), std::make_unique<int>(7)); + EXPECT_EQ(map.lookup(std::make_unique<int>(10)).size(), 0); +} + +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc index fcd958dc2f1..7bd0b258df8 100644 --- a/tests/gtests/blenlib/BLI_set_test.cc +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -12,6 +12,7 @@ #include "testing/testing.h" namespace blender { +namespace tests { TEST(set, DefaultConstructor) { @@ -81,7 +82,7 @@ TEST(set, MoveConstructor) Set<int> set = {1, 2, 3}; EXPECT_EQ(set.size(), 3); Set<int> set2(std::move(set)); - EXPECT_EQ(set.size(), 0); + EXPECT_EQ(set.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(set2.size(), 3); } @@ -106,7 +107,7 @@ TEST(set, MoveAssignment) EXPECT_EQ(set.size(), 3); Set<int> set2; set2 = std::move(set); - EXPECT_EQ(set.size(), 0); + EXPECT_EQ(set.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(set2.size(), 3); } @@ -279,31 +280,32 @@ struct Type2 { uint32_t value; }; -bool operator==(const Type1 &a, const Type1 &b) +static bool operator==(const Type1 &a, const Type1 &b) { return a.value == b.value; } -bool operator==(const Type1 &a, const Type2 &b) -{ - return a.value == b.value; -} -bool operator==(const Type2 &a, const Type1 &b) +static bool operator==(const Type2 &a, const Type1 &b) { return a.value == b.value; } -template<> struct DefaultHash<Type1> { - uint32_t operator()(const Type1 &value) const +} // namespace tests + +/* This has to be defined in ::blender namespace. */ +template<> struct DefaultHash<tests::Type1> { + uint32_t operator()(const tests::Type1 &value) const { return value.value; } - uint32_t operator()(const Type2 &value) const + uint32_t operator()(const tests::Type2 &value) const { return value.value; } }; +namespace tests { + TEST(set, ContainsAs) { Set<Type1> set; @@ -559,4 +561,5 @@ TEST(set, Benchmark) #endif /* Benchmark */ +} // namespace tests } // namespace blender diff --git a/tests/gtests/blenlib/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 9c2e7cf26fb..587497624f4 100644 --- a/tests/gtests/blenlib/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -5,7 +5,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(span, FromSmallVector) { @@ -295,4 +295,17 @@ TEST(span, VoidPointerSpan) func1({&a, &b, &c}); } -} // namespace blender +TEST(span, CopyFrom) +{ + std::array<int, 4> src = {5, 6, 7, 8}; + std::array<int, 4> dst = {1, 2, 3, 4}; + + EXPECT_EQ(dst[2], 3); + MutableSpan(dst).copy_from(src); + EXPECT_EQ(dst[0], 5); + EXPECT_EQ(dst[1], 6); + EXPECT_EQ(dst[2], 7); + EXPECT_EQ(dst[3], 8); +} + +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc index 43b3dd8b3ae..3572e751b88 100644 --- a/tests/gtests/blenlib/BLI_stack_cxx_test.cc +++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc @@ -5,7 +5,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(stack, DefaultConstructor) { @@ -45,7 +45,7 @@ TEST(stack, MoveConstructor) { Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7}; Stack<int> stack2 = std::move(stack1); - EXPECT_EQ(stack1.size(), 0); + EXPECT_EQ(stack1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(stack2.size(), 7); for (int i = 7; i >= 1; i--) { EXPECT_EQ(stack2.pop(), i); @@ -75,7 +75,7 @@ TEST(stack, MoveAssignment) Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7}; Stack<int> stack2 = {5, 3, 7, 2, 2}; stack2 = std::move(stack1); - EXPECT_EQ(stack1.size(), 0); + EXPECT_EQ(stack1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(stack2.size(), 7); for (int i = 7; i >= 1; i--) { EXPECT_EQ(stack2.pop(), i); @@ -185,4 +185,4 @@ TEST(stack, OveralignedValues) } } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc index 83099741c29..d08c8a77455 100644 --- a/tests/gtests/blenlib/BLI_string_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc @@ -5,7 +5,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(string_ref_null, DefaultConstructor) { @@ -274,4 +274,4 @@ TEST(string_ref, Copy) EXPECT_EQ(ref, dst); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc index 116c4747c5a..8f3db8d8403 100644 --- a/tests/gtests/blenlib/BLI_vector_set_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc @@ -4,7 +4,7 @@ #include "BLI_vector_set.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(vector_set, DefaultConstructor) { @@ -57,7 +57,7 @@ TEST(vector_set, Move) { VectorSet<int> set1 = {1, 2, 3}; VectorSet<int> set2 = std::move(set1); - EXPECT_EQ(set1.size(), 0); + EXPECT_EQ(set1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(set2.size(), 3); } @@ -66,7 +66,7 @@ TEST(vector_set, MoveAssignment) VectorSet<int> set1 = {1, 2, 3}; VectorSet<int> set2 = {}; set2 = std::move(set1); - EXPECT_EQ(set1.size(), 0); + EXPECT_EQ(set1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(set2.size(), 3); } @@ -161,4 +161,4 @@ TEST(vector_set, Remove) EXPECT_FALSE(set.contains(5)); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index f581626d1ad..f72dfc5deb8 100644 --- a/tests/gtests/blenlib/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -5,7 +5,7 @@ #include "testing/testing.h" #include <forward_list> -namespace blender { +namespace blender::tests { TEST(vector, DefaultConstructor) { @@ -167,7 +167,7 @@ TEST(vector, MoveConstructor) Vector<int> vec1 = {1, 2, 3, 4}; Vector<int> vec2(std::move(vec1)); - EXPECT_EQ(vec1.size(), 0); + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(vec2.size(), 4); EXPECT_EQ(vec2[0], 1); EXPECT_EQ(vec2[1], 2); @@ -180,7 +180,7 @@ TEST(vector, MoveConstructor2) Vector<int, 2> vec1 = {1, 2, 3, 4}; Vector<int, 3> vec2(std::move(vec1)); - EXPECT_EQ(vec1.size(), 0); + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(vec2.size(), 4); EXPECT_EQ(vec2[0], 1); EXPECT_EQ(vec2[1], 2); @@ -193,7 +193,7 @@ TEST(vector, MoveConstructor3) Vector<int, 20> vec1 = {1, 2, 3, 4}; Vector<int, 1> vec2(std::move(vec1)); - EXPECT_EQ(vec1.size(), 0); + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(vec2.size(), 4); EXPECT_EQ(vec2[2], 3); } @@ -203,7 +203,7 @@ TEST(vector, MoveConstructor4) Vector<int, 5> vec1 = {1, 2, 3, 4}; Vector<int, 6> vec2(std::move(vec1)); - EXPECT_EQ(vec1.size(), 0); + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(vec2.size(), 4); EXPECT_EQ(vec2[3], 4); } @@ -473,11 +473,11 @@ class TypeConstructMock { { } - TypeConstructMock(const TypeConstructMock &other) : copy_constructed(true) + TypeConstructMock(const TypeConstructMock &UNUSED(other)) : copy_constructed(true) { } - TypeConstructMock(TypeConstructMock &&other) noexcept : move_constructed(true) + TypeConstructMock(TypeConstructMock &&UNUSED(other)) noexcept : move_constructed(true) { } @@ -636,4 +636,4 @@ TEST(vector, Fill) EXPECT_EQ(vec[4], 3); } -} // namespace blender +} // namespace blender::tests diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 134e23d36e8..435c96711bd 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4162,7 +4162,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu) direct_link_animdata(reader, cu->adt); /* Protect against integer overflow vulnerability. */ - CLAMP(cu->len_wchar, 0, INT_MAX - 4); + CLAMP(cu->len_char32, 0, INT_MAX - 4); BLO_read_pointer_array(reader, (void **)&cu->mat); @@ -5518,7 +5518,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); /* initialize the curve. Maybe this could be moved to modififer logic */ - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } else if (md->type == eGpencilModifierType_Thick) { @@ -5527,7 +5527,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_thickness); if (gpmd->curve_thickness) { BKE_curvemapping_blend_read(reader, gpmd->curve_thickness); - BKE_curvemapping_initialize(gpmd->curve_thickness); + BKE_curvemapping_init(gpmd->curve_thickness); } } else if (md->type == eGpencilModifierType_Tint) { @@ -5536,7 +5536,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_intensity); if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } else if (md->type == eGpencilModifierType_Smooth) { @@ -5544,7 +5544,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_intensity); if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } else if (md->type == eGpencilModifierType_Color) { @@ -5552,7 +5552,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_intensity); if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } else if (md->type == eGpencilModifierType_Opacity) { @@ -5560,7 +5560,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_intensity); if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } } @@ -8695,9 +8695,8 @@ static void direct_link_volume(BlendDataReader *reader, Volume *volume) static void lib_link_simulation(BlendLibReader *reader, Simulation *simulation) { - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BLO_read_id_address(reader, simulation->id.lib, &handle_item->id); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BLO_read_id_address(reader, simulation->id.lib, &dependency->id); } } @@ -8716,7 +8715,7 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati } } - BLO_read_list(reader, &simulation->persistent_data_handles); + BLO_read_list(reader, &simulation->dependencies); } /** \} */ @@ -11125,9 +11124,8 @@ static void expand_simulation(BlendExpander *expander, Simulation *simulation) if (simulation->adt) { expand_animdata(expander, simulation->adt); } - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BLO_expand(expander, handle_item->id); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BLO_expand(expander, dependency->id); } } diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 3ed59a0baa1..1432fbeb4e2 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -280,7 +280,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb) region->v2d.keepzoom |= (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_KEEPASPECT); region->v2d.keeptot = V2D_KEEPTOT_STRICT; region->v2d.minzoom = region->v2d.maxzoom = 1.0f; - // region->v2d.flag |= V2D_IS_INITIALISED; + // region->v2d.flag |= V2D_IS_INIT; break; } case SPACE_GRAPH: { @@ -297,7 +297,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb) region->v2d.max[0] = MAXFRAMEF; region->v2d.max[1] = FLT_MAX; - // region->v2d.flag |= V2D_IS_INITIALISED; + // region->v2d.flag |= V2D_IS_INIT; break; } case SPACE_NLA: { @@ -355,7 +355,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb) region->v2d.scroll |= (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES); region->v2d.scroll |= (V2D_SCROLL_LEFT | V2D_SCROLL_VERTICAL_HANDLES); region->v2d.align = V2D_ALIGN_NO_NEG_Y; - region->v2d.flag |= V2D_IS_INITIALISED; + region->v2d.flag |= V2D_IS_INIT; break; } case SPACE_NODE: { @@ -965,7 +965,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) bPoseChannel *pchan; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - /* just need to initialise rotation axis properly... */ + /* Just need to initialize rotation axis properly. */ pchan->rotAxis[1] = 1.0f; } } diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index b3bf8991c3e..f74ee9cd735 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1205,7 +1205,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) if (region->regiontype == RGN_TYPE_PREVIEW) { if (region->alignment != RGN_ALIGN_NONE) { region->flag |= RGN_FLAG_HIDDEN; - region->v2d.flag &= ~V2D_IS_INITIALISED; + region->v2d.flag &= ~V2D_IS_INIT; region->alignment = RGN_ALIGN_NONE; hide = true; @@ -2520,7 +2520,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (cu = bmain->curves.first; cu; cu = cu->id.next) { if (cu->str) { - cu->len_wchar = BLI_strlen_utf8(cu->str); + cu->len_char32 = BLI_strlen_utf8(cu->str); } } } diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 521fc4b9b82..2c4602f546b 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -1108,7 +1108,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) for (scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { CurveMapping *curve_mapping = &scene->r.mblur_shutter_curve; BKE_curvemapping_set_defaults(curve_mapping, 1, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(curve_mapping); + BKE_curvemapping_init(curve_mapping); BKE_curvemap_reset( curve_mapping->cm, &curve_mapping->clipr, CURVE_PRESET_MAX, CURVEMAP_SLOPE_POS_NEG); } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 111ac728cc3..fc3e81a2005 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -190,7 +190,7 @@ static void do_version_workspaces_create_from_screens(Main *bmain) static void do_version_area_change_space_to_space_action(ScrArea *area, const Scene *scene) { SpaceType *stype = BKE_spacetype_from_id(SPACE_ACTION); - SpaceAction *saction = (SpaceAction *)stype->new (area, scene); + SpaceAction *saction = (SpaceAction *)stype->create(area, scene); ARegion *region_channels; /* Properly free current regions */ @@ -1958,7 +1958,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; if ((gset) && (gset->cur_falloff == NULL)) { gset->cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(gset->cur_falloff); + BKE_curvemapping_init(gset->cur_falloff); BKE_curvemap_reset(gset->cur_falloff->cm, &gset->cur_falloff->clipr, CURVE_PRESET_GAUSS, @@ -3371,7 +3371,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; if ((gset) && (gset->cur_primitive == NULL)) { gset->cur_primitive = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(gset->cur_primitive); + BKE_curvemapping_init(gset->cur_primitive); BKE_curvemap_reset(gset->cur_primitive->cm, &gset->cur_primitive->clipr, CURVE_PRESET_BELL, @@ -4767,7 +4767,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; @@ -4778,7 +4778,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; @@ -4788,7 +4788,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; @@ -4798,7 +4798,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; @@ -4808,7 +4808,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index a96b82e2e91..12b5a297df5 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -22,9 +22,11 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" #include "DNA_constraint_types.h" #include "DNA_genfile.h" #include "DNA_gpencil_modifier_types.h" @@ -446,5 +448,39 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Initialise additional velocity parameter for CacheFiles. */ + if (!DNA_struct_elem_find( + fd->filesdna, "MeshSeqCacheModifierData", "float", "velocity_scale")) { + for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_MeshSequenceCache) { + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + mcmd->velocity_scale = 1.0f; + mcmd->vertex_velocities = NULL; + mcmd->num_vertices = 0; + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "CacheFile", "char", "velocity_unit")) { + for (CacheFile *cache_file = bmain->cachefiles.first; cache_file != NULL; + cache_file = cache_file->id.next) { + BLI_strncpy(cache_file->velocity_name, ".velocities", sizeof(cache_file->velocity_name)); + cache_file->velocity_unit = CACHEFILE_VELOCITY_UNIT_SECOND; + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "OceanModifierData", "int", "viewport_resolution")) { + for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Ocean) { + OceanModifierData *omd = (OceanModifierData *)md; + omd->viewport_resolution = omd->resolution; + } + } + } + } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 7f75c0100b8..ae6d3a58091 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -106,7 +106,7 @@ static void blo_update_defaults_screen(bScreen *screen, /* Some toolbars have been saved as initialized, * we don't want them to have odd zoom-level or scrolling set, see: T47047 */ if (ELEM(region->regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) { - region->v2d.flag &= ~V2D_IS_INITIALISED; + region->v2d.flag &= ~V2D_IS_INIT; } } @@ -326,7 +326,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (ts->gp_sculpt.cur_falloff == NULL) { ts->gp_sculpt.cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_falloff_curve = ts->gp_sculpt.cur_falloff; - BKE_curvemapping_initialize(gp_falloff_curve); + BKE_curvemapping_init(gp_falloff_curve); BKE_curvemap_reset(gp_falloff_curve->cm, &gp_falloff_curve->clipr, CURVE_PRESET_GAUSS, @@ -335,7 +335,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (ts->gp_sculpt.cur_primitive == NULL) { ts->gp_sculpt.cur_primitive = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_primitive_curve = ts->gp_sculpt.cur_primitive; - BKE_curvemapping_initialize(gp_primitive_curve); + BKE_curvemapping_init(gp_primitive_curve); BKE_curvemap_reset(gp_primitive_curve->cm, &gp_primitive_curve->clipr, CURVE_PRESET_BELL, diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 44c7c35e47d..d25e340d4fc 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -2096,7 +2096,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) if (la->curfalloff == NULL) { la->curfalloff = BKE_curvemapping_add(1, 0.0f, 1.0f, 1.0f, 0.0f); - BKE_curvemapping_initialize(la->curfalloff); + BKE_curvemapping_init(la->curfalloff); } } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 50e3b375166..e2dc27d7e88 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -366,7 +366,7 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) } if (!USER_VERSION_ATLEAST(250, 0)) { /* adjust grease-pencil distances */ - userdef->gp_manhattendist = 1; + userdef->gp_manhattandist = 1; userdef->gp_euclideandist = 2; /* adjust default interpolation for new IPO-curves */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 0d1c3f2c3e5..d842c204ba1 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2007,7 +2007,7 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address) if (cu->vfont) { BLO_write_raw(writer, cu->len + 1, cu->str); - BLO_write_struct_array(writer, CharInfo, cu->len_wchar + 1, cu->strinfo); + BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo); BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb); } else { @@ -3867,7 +3867,7 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const } } - BLO_write_struct_list(writer, PersistentDataHandleItem, &simulation->persistent_data_handles); + BLO_write_struct_list(writer, SimulationDependency, &simulation->dependencies); } } @@ -4052,8 +4052,9 @@ static bool write_file_handle(Main *mainvar, * avoid thumbnail detecting changes because of this. */ mywrite_flush(wd); - OverrideLibraryStorage *override_storage = - wd->use_memfile ? NULL : BKE_lib_override_library_operations_store_initialize(); + OverrideLibraryStorage *override_storage = wd->use_memfile ? + NULL : + BKE_lib_override_library_operations_store_init(); #define ID_BUFFER_STATIC_SIZE 8192 /* This outer loop allows to save first data-blocks from real mainvar, diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 6c666183755..f263392bdd2 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -45,6 +45,11 @@ #include "./intern/bmesh_private.h" +// #define BEVEL_DEBUG_TIME +#ifdef BEVEL_DEBUG_TIME +# include "PIL_time.h" +#endif + #define BEVEL_EPSILON_D 1e-6 #define BEVEL_EPSILON 1e-6f #define BEVEL_EPSILON_SQ 1e-12f @@ -917,7 +922,7 @@ static void math_layer_info_init(BevelParams *bp, BMesh *bm) * segment (and its continuation into vmesh) can usually arbitrarily be * the previous face or the next face. * Or, for the center polygon of a corner, all of the faces around - * the vertex are possible choices. + * the vertex are possibleface_component choices. * If we just choose randomly, the resulting UV maps or material * assignment can look ugly/inconsistent. * Allow for the case when arguments are null. @@ -1891,6 +1896,57 @@ static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int ns } /** + * Helper for #calculate_profile that builds the 3D locations for the segments + * and the higher power of 2 segments. + */ +static void calculate_profile_segments(const Profile *profile, + const float map[4][4], + const bool use_map, + const bool reversed, + const int ns, + const double *xvals, + const double *yvals, + float *r_prof_co) +{ + /* Iterate over the vertices along the boundary arc. */ + for (int k = 0; k <= ns; k++) { + float co[3]; + if (k == 0) { + copy_v3_v3(co, profile->start); + } + else if (k == ns) { + copy_v3_v3(co, profile->end); + } + else { + if (use_map) { + float p[3] = {reversed ? (float)yvals[ns - k] : (float)xvals[k], + reversed ? (float)xvals[ns - k] : (float)yvals[k], + 0.0f}; + /* Do the 2D->3D transformation of the profile coordinates. */ + mul_v3_m4v3(co, map, p); + } + else { + interp_v3_v3v3(co, profile->start, profile->end, (float)k / (float)ns); + } + } + /* Finish the 2D->3D transformation by projecting onto the final profile plane. */ + float *prof_co_k = r_prof_co + 3 * k; + if (!is_zero_v3(profile->proj_dir)) { + float co2[3]; + add_v3_v3v3(co2, co, profile->proj_dir); + /* pro->plane_co and pro->plane_no are filled in #set_profile_params. */ + if (!isect_line_plane_v3(prof_co_k, co, co2, profile->plane_co, profile->plane_no)) { + /* Shouldn't happen. */ + copy_v3_v3(prof_co_k, co); + } + } + else { + copy_v3_v3(prof_co_k, co); + } + } +} + +/** * Calculate the actual coordinate values for bndv's profile. * This is only needed if bp->seg > 1. * Allocate the space for them if that hasn't been done already. @@ -1900,12 +1956,6 @@ static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int ns */ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, bool miter) { - int i, k, ns; - const double *xvals, *yvals; - float co[3], co2[3], p[3], map[4][4], bottom_corner[3], top_corner[3]; - float *prof_co, *prof_co_k; - float r; - bool need_2, map_ok; Profile *pro = &bndv->profile; ProfileSpacing *pro_spacing = (miter) ? &bp->pro_spacing_miter : &bp->pro_spacing; @@ -1913,89 +1963,51 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, b return; } - need_2 = bp->seg != bp->pro_spacing.seg_2; - if (!pro->prof_co) { - pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena, - ((size_t)bp->seg + 1) * 3 * sizeof(float)); + bool need_2 = bp->seg != bp->pro_spacing.seg_2; + if (pro->prof_co == NULL) { + pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena, sizeof(float) * 3 * (bp->seg + 1)); if (need_2) { pro->prof_co_2 = (float *)BLI_memarena_alloc( - bp->mem_arena, ((size_t)bp->pro_spacing.seg_2 + 1) * 3 * sizeof(float)); + bp->mem_arena, sizeof(float) * 3 * (bp->pro_spacing.seg_2 + 1)); } else { pro->prof_co_2 = pro->prof_co; } } - r = pro->super_r; - if (bp->profile_type == BEVEL_PROFILE_SUPERELLIPSE && r == PRO_LINE_R) { - map_ok = false; + + bool use_map; + float map[4][4]; + if (bp->profile_type == BEVEL_PROFILE_SUPERELLIPSE && pro->super_r == PRO_LINE_R) { + use_map = false; } else { - map_ok = make_unit_square_map(pro->start, pro->middle, pro->end, map); + use_map = make_unit_square_map(pro->start, pro->middle, pro->end, map); } - if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && map_ok) { + if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && use_map) { /* Calculate the "height" of the profile by putting the (0,0) and (1,1) corners of the * un-transformed profile through the 2D->3D map and calculating the distance between them. */ - zero_v3(p); - mul_v3_m4v3(bottom_corner, map, p); - p[0] = 1.0f; - p[1] = 1.0f; - mul_v3_m4v3(top_corner, map, p); + float bottom_corner[3] = {0.0f, 0.0f, 0.0f}; + mul_v3_m4v3(bottom_corner, map, bottom_corner); + float top_corner[3] = {1.0f, 1.0f, 0.0f}; + mul_v3_m4v3(top_corner, map, top_corner); + pro->height = len_v3v3(bottom_corner, top_corner); } - /* The first iteration is the nseg case, the second is the seg_2 case (if it's needed) .*/ - for (i = 0; i < 2; i++) { - if (i == 0) { - ns = bp->seg; - xvals = pro_spacing->xvals; - yvals = pro_spacing->yvals; - prof_co = pro->prof_co; - } - else { - if (!need_2) { - break; /* Shares coords with pro->prof_co. */ - } - ns = bp->pro_spacing.seg_2; - xvals = pro_spacing->xvals_2; - yvals = pro_spacing->yvals_2; - prof_co = pro->prof_co_2; - } - - /* Iterate over the vertices along the boundary arc. */ - for (k = 0; k <= ns; k++) { - if (k == 0) { - copy_v3_v3(co, pro->start); - } - else if (k == ns) { - copy_v3_v3(co, pro->end); - } - else { - if (map_ok) { - p[0] = reversed ? (float)yvals[ns - k] : (float)xvals[k]; - p[1] = reversed ? (float)xvals[ns - k] : (float)yvals[k]; - p[2] = 0.0f; - /* Do the 2D->3D transformation of the profile coordinates. */ - mul_v3_m4v3(co, map, p); - } - else { - interp_v3_v3v3(co, pro->start, pro->end, (float)k / (float)ns); - } - } - /* Finish the 2D->3D transformation by projecting onto the final profile plane. */ - prof_co_k = prof_co + 3 * k; /* Each coord takes up 3 spaces. */ - if (!is_zero_v3(pro->proj_dir)) { - add_v3_v3v3(co2, co, pro->proj_dir); - /* pro->plane_co and pro->plane_no are filled in "set_profile_params". */ - if (!isect_line_plane_v3(prof_co_k, co, co2, pro->plane_co, pro->plane_no)) { - /* Shouldn't happen. */ - copy_v3_v3(prof_co_k, co); - } - } - else { - copy_v3_v3(prof_co_k, co); - } - } + /* Calculate the 3D locations for the profile points */ + calculate_profile_segments( + pro, map, use_map, reversed, bp->seg, pro_spacing->xvals, pro_spacing->yvals, pro->prof_co); + /* Also calcualte for the is the seg_2 case if it's needed .*/ + if (need_2) { + calculate_profile_segments(pro, + map, + use_map, + reversed, + bp->pro_spacing.seg_2, + pro_spacing->xvals_2, + pro_spacing->yvals_2, + pro->prof_co_2); } } @@ -7143,68 +7155,64 @@ static float find_profile_fullness(BevelParams *bp) */ static void set_profile_spacing(BevelParams *bp, ProfileSpacing *pro_spacing, bool custom) { - int seg, seg_2; + int seg = bp->seg; - seg = bp->seg; - seg_2 = power_of_2_max_i(bp->seg); - if (seg > 1) { - /* Sample the seg_2 segments used for subdividing the vertex meshes. */ - if (seg_2 == 2) { - seg_2 = 4; - } - bp->pro_spacing.seg_2 = seg_2; - if (seg_2 == seg) { - pro_spacing->xvals_2 = pro_spacing->xvals; - pro_spacing->yvals_2 = pro_spacing->yvals; - } - else { - pro_spacing->xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena, - (size_t)(seg_2 + 1) * sizeof(double)); - pro_spacing->yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena, - (size_t)(seg_2 + 1) * sizeof(double)); - if (custom) { - /* Make sure the curve profile widget's sample table is full of the seg_2 samples. */ - BKE_curveprofile_initialize((CurveProfile *)bp->custom_profile, (short)seg_2); - - /* Copy segment locations into the profile spacing struct. */ - for (int i = 0; i < seg_2 + 1; i++) { - pro_spacing->xvals_2[i] = (double)bp->custom_profile->segments[i].y; - pro_spacing->yvals_2[i] = (double)bp->custom_profile->segments[i].x; - } - } - else { - find_even_superellipse_chords( - seg_2, bp->pro_super_r, pro_spacing->xvals_2, pro_spacing->yvals_2); - } - } + if (seg <= 1) { + /* Only 1 segment, we don't need any profile information. */ + pro_spacing->xvals = NULL; + pro_spacing->yvals = NULL; + pro_spacing->xvals_2 = NULL; + pro_spacing->yvals_2 = NULL; + pro_spacing->seg_2 = 0; + return; + } - /* Sample the input number of segments. */ - pro_spacing->xvals = (double *)BLI_memarena_alloc(bp->mem_arena, - (size_t)(seg + 1) * sizeof(double)); - pro_spacing->yvals = (double *)BLI_memarena_alloc(bp->mem_arena, - (size_t)(seg + 1) * sizeof(double)); + int seg_2 = max_ii(power_of_2_max_i(bp->seg), 4); + + /* Sample the seg_2 segments used during vertex mesh subdivision. */ + bp->pro_spacing.seg_2 = seg_2; + if (seg_2 == seg) { + pro_spacing->xvals_2 = pro_spacing->xvals; + pro_spacing->yvals_2 = pro_spacing->yvals; + } + else { + pro_spacing->xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena, + sizeof(double) * (seg_2 + 1)); + pro_spacing->yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena, + sizeof(double) * (seg_2 + 1)); if (custom) { - /* Make sure the curve profile's sample table is full. */ - if (bp->custom_profile->segments_len != seg || !bp->custom_profile->segments) { - BKE_curveprofile_initialize((CurveProfile *)bp->custom_profile, (short)seg); - } + /* Make sure the curve profile widget's sample table is full of the seg_2 samples. */ + BKE_curveprofile_init((CurveProfile *)bp->custom_profile, (short)seg_2); /* Copy segment locations into the profile spacing struct. */ - for (int i = 0; i < seg + 1; i++) { - pro_spacing->xvals[i] = (double)bp->custom_profile->segments[i].y; - pro_spacing->yvals[i] = (double)bp->custom_profile->segments[i].x; + for (int i = 0; i < seg_2 + 1; i++) { + pro_spacing->xvals_2[i] = (double)bp->custom_profile->segments[i].y; + pro_spacing->yvals_2[i] = (double)bp->custom_profile->segments[i].x; } } else { - find_even_superellipse_chords(seg, bp->pro_super_r, pro_spacing->xvals, pro_spacing->yvals); + find_even_superellipse_chords( + seg_2, bp->pro_super_r, pro_spacing->xvals_2, pro_spacing->yvals_2); } } - else { /* Only 1 segment, we don't need any profile information. */ - pro_spacing->xvals = NULL; - pro_spacing->yvals = NULL; - pro_spacing->xvals_2 = NULL; - pro_spacing->yvals_2 = NULL; - pro_spacing->seg_2 = 0; + + /* Sample the input number of segments. */ + pro_spacing->xvals = (double *)BLI_memarena_alloc(bp->mem_arena, sizeof(double) * (seg + 1)); + pro_spacing->yvals = (double *)BLI_memarena_alloc(bp->mem_arena, sizeof(double) * (seg + 1)); + if (custom) { + /* Make sure the curve profile's sample table is full. */ + if (bp->custom_profile->segments_len != seg || !bp->custom_profile->segments) { + BKE_curveprofile_init((CurveProfile *)bp->custom_profile, (short)seg); + } + + /* Copy segment locations into the profile spacing struct. */ + for (int i = 0; i < seg + 1; i++) { + pro_spacing->xvals[i] = (double)bp->custom_profile->segments[i].y; + pro_spacing->yvals[i] = (double)bp->custom_profile->segments[i].x; + } + } + else { + find_even_superellipse_chords(seg, bp->pro_super_r, pro_spacing->xvals, pro_spacing->yvals); } } @@ -7493,6 +7501,10 @@ void BM_mesh_bevel(BMesh *bm, bp.custom_profile = custom_profile; bp.vmesh_method = vmesh_method; +#ifdef BEVEL_DEBUG_TIME + double start_time = PIL_check_seconds_timer(); +#endif + /* Disable the miters with the cutoff vertex mesh method, the combination isn't useful anyway. */ if (bp.vmesh_method == BEVEL_VMESH_CUTOFF) { bp.miter_outer = BEVEL_MITER_SHARP; @@ -7661,4 +7673,8 @@ void BM_mesh_bevel(BMesh *bm, BLI_ghash_free(bp.face_hash, NULL, NULL); BLI_memarena_free(bp.mem_arena); } +#ifdef BEVEL_DEBUG_TIME + double end_time = PIL_check_seconds_timer(); + printf("BMESH BEVEL TIME = %.3f\n", end_time - start_time); +#endif } diff --git a/source/blender/compositor/nodes/COM_TimeNode.cpp b/source/blender/compositor/nodes/COM_TimeNode.cpp index 9722ead0716..247e0d11df6 100644 --- a/source/blender/compositor/nodes/COM_TimeNode.cpp +++ b/source/blender/compositor/nodes/COM_TimeNode.cpp @@ -49,7 +49,7 @@ void TimeNode::convertToOperations(NodeConverter &converter, fac = (context.getFramenumber() - node->custom1) / (float)(node->custom2 - node->custom1); } - BKE_curvemapping_initialize((CurveMapping *)node->storage); + BKE_curvemapping_init((CurveMapping *)node->storage); fac = BKE_curvemapping_evaluateF((CurveMapping *)node->storage, 0, fac); operation->setValue(clamp_f(fac, 0.0f, 1.0f)); converter.addOperation(operation); diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cpp b/source/blender/compositor/operations/COM_CurveBaseOperation.cpp index b18e77cf0e3..855f728f7bf 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cpp +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cpp @@ -35,7 +35,7 @@ CurveBaseOperation::~CurveBaseOperation() void CurveBaseOperation::initExecution() { - BKE_curvemapping_initialize(this->m_curveMapping); + BKE_curvemapping_init(this->m_curveMapping); } void CurveBaseOperation::deinitExecution() { diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cpp b/source/blender/compositor/operations/COM_InpaintOperation.cpp index 0967984899d..b8329b1cb29 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cpp +++ b/source/blender/compositor/operations/COM_InpaintOperation.cpp @@ -34,7 +34,7 @@ InpaintSimpleOperation::InpaintSimpleOperation() : NodeOperation() this->setComplex(true); this->m_inputImageProgram = NULL; this->m_pixelorder = NULL; - this->m_manhatten_distance = NULL; + this->m_manhattan_distance = NULL; this->m_cached_buffer = NULL; this->m_cached_buffer_ready = false; } @@ -43,7 +43,7 @@ void InpaintSimpleOperation::initExecution() this->m_inputImageProgram = this->getInputSocketReader(0); this->m_pixelorder = NULL; - this->m_manhatten_distance = NULL; + this->m_manhattan_distance = NULL; this->m_cached_buffer = NULL; this->m_cached_buffer_ready = false; @@ -85,7 +85,7 @@ int InpaintSimpleOperation::mdist(int x, int y) ASSERT_XY_RANGE(x, y); - return this->m_manhatten_distance[y * width + x]; + return this->m_manhattan_distance[y * width + x]; } bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) @@ -108,11 +108,11 @@ bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) return true; } -void InpaintSimpleOperation::calc_manhatten_distance() +void InpaintSimpleOperation::calc_manhattan_distance() { int width = this->getWidth(); int height = this->getHeight(); - short *m = this->m_manhatten_distance = (short *)MEM_mallocN(sizeof(short) * width * height, + short *m = this->m_manhattan_distance = (short *)MEM_mallocN(sizeof(short) * width * height, __func__); int *offsets; @@ -223,7 +223,7 @@ void *InpaintSimpleOperation::initializeTileData(rcti *rect) MemoryBuffer *buf = (MemoryBuffer *)this->m_inputImageProgram->initializeTileData(rect); this->m_cached_buffer = (float *)MEM_dupallocN(buf->getBuffer()); - this->calc_manhatten_distance(); + this->calc_manhattan_distance(); int curr = 0; int x, y; @@ -258,9 +258,9 @@ void InpaintSimpleOperation::deinitExecution() this->m_pixelorder = NULL; } - if (this->m_manhatten_distance) { - MEM_freeN(this->m_manhatten_distance); - this->m_manhatten_distance = NULL; + if (this->m_manhattan_distance) { + MEM_freeN(this->m_manhattan_distance); + this->m_manhattan_distance = NULL; } this->m_cached_buffer_ready = false; } diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index 12523d5f064..86f3393e1ea 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -34,7 +34,7 @@ class InpaintSimpleOperation : public NodeOperation { int *m_pixelorder; int m_area_size; - short *m_manhatten_distance; + short *m_manhattan_distance; public: InpaintSimpleOperation(); @@ -65,7 +65,7 @@ class InpaintSimpleOperation : public NodeOperation { rcti *output); private: - void calc_manhatten_distance(); + void calc_manhattan_distance(); void clamp_xy(int &x, int &y); float *get_pixel(int x, int y); int mdist(int x, int y); diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cpp b/source/blender/compositor/operations/COM_SunBeamsOperation.cpp index 6f47e0e190b..4a7139537c1 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cpp +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cpp @@ -172,7 +172,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { return; } - /* initialise the iteration variables */ + /* Initialize the iteration variables. */ float *buffer = init_buffer_iterator( input, source, co, dist_min, dist_max, x, y, num, v, dv, falloff_factor); zero_v3(border); diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 51fce738700..417aaa2c4c0 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -54,6 +54,11 @@ set(SRC intern/builder/deg_builder_remove_noop.cc intern/builder/deg_builder_rna.cc intern/builder/deg_builder_transitive.cc + intern/builder/pipeline.cc + intern/builder/pipeline_compositor.cc + intern/builder/pipeline_from_ids.cc + intern/builder/pipeline_render.cc + intern/builder/pipeline_view_layer.cc intern/debug/deg_debug.cc intern/debug/deg_debug_relations_graphviz.cc intern/debug/deg_debug_stats_gnuplot.cc @@ -110,6 +115,11 @@ set(SRC intern/builder/deg_builder_remove_noop.h intern/builder/deg_builder_rna.h intern/builder/deg_builder_transitive.h + intern/builder/pipeline.h + intern/builder/pipeline_compositor.h + intern/builder/pipeline_from_ids.h + intern/builder/pipeline_render.h + intern/builder/pipeline_view_layer.h intern/debug/deg_debug.h intern/debug/deg_time_average.h intern/eval/deg_eval.h diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index dcdcf0c05ca..e262c880421 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1126,6 +1126,12 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene) if (object->type != OB_MESH) { continue; } + if (object->rigidbody_object == nullptr) { + continue; + } + if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) { + continue; + } /* Create operation for flushing results. */ /* Object's transform component - where the rigidbody operation * lives. */ @@ -1466,6 +1472,18 @@ void DepsgraphNodeBuilder::build_light(Light *lamp) function_bind(BKE_light_eval, _1, lamp_cow)); } +void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket) +{ + build_idproperties(socket->prop); + + if (socket->type == SOCK_OBJECT) { + build_id((ID *)((bNodeSocketValueObject *)socket->default_value)->value); + } + else if (socket->type == SOCK_IMAGE) { + build_id((ID *)((bNodeSocketValueImage *)socket->default_value)->value); + } +} + void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) { if (ntree == nullptr) { @@ -1494,10 +1512,10 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } ID *id = bnode->id; @@ -1780,8 +1798,10 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation) return; } add_id_node(&simulation->id); + build_idproperties(simulation->id.properties); build_animdata(&simulation->id); build_parameters(&simulation->id); + build_nodetree(simulation->nodetree); Simulation *simulation_cow = get_cow_datablock(simulation); Scene *scene_cow = get_cow_datablock(scene_); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 256fa3450a6..40f42705a52 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -31,6 +31,7 @@ #include "DEG_depsgraph.h" +struct bNodeSocket; struct CacheFile; struct Camera; struct Collection; @@ -211,6 +212,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_camera(Camera *camera); virtual void build_light(Light *lamp); virtual void build_nodetree(bNodeTree *ntree); + virtual void build_nodetree_socket(bNodeSocket *socket); virtual void build_material(Material *ma); virtual void build_materials(Material **materials, int num_materials); virtual void build_freestyle_lineset(FreestyleLineSet *fls); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c5c509ee853..5ae71dd1792 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1698,6 +1698,22 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) if (object->type != OB_MESH) { continue; } + if (object->rigidbody_object == nullptr) { + continue; + } + if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) { + continue; + } + + if (object->parent != nullptr && object->parent->rigidbody_object != nullptr && + object->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) { + /* If we are a child of a compound shape object, the transforms and sim evaluation will be + * handled by the parent compound shape object. Do not add any evaluation triggers + * for the child objects. + */ + continue; + } + OperationKey rb_transform_copy_key( &object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); /* Rigid body synchronization depends on the actual simulation. */ @@ -1747,13 +1763,18 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) /* final result of the constraint object's transform controls how * the constraint affects the physics sim for these objects. */ ComponentKey trans_key(&object->id, NodeType::TRANSFORM); - OperationKey ob1_key( - &rbc->ob1->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - OperationKey ob2_key( - &rbc->ob2->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - /* Constrained-objects sync depends on the constraint-holder. */ - add_relation(trans_key, ob1_key, "RigidBodyConstraint -> RBC.Object_1"); - add_relation(trans_key, ob2_key, "RigidBodyConstraint -> RBC.Object_2"); + if (rbc->ob1->rigidbody_object->type == RBO_TYPE_ACTIVE) { + OperationKey ob1_key( + &rbc->ob1->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); + /* Constrained-objects sync depends on the constraint-holder. */ + add_relation(trans_key, ob1_key, "RigidBodyConstraint -> RBC.Object_1"); + } + if (rbc->ob2->rigidbody_object->type == RBO_TYPE_ACTIVE) { + OperationKey ob2_key( + &rbc->ob2->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); + /* Constrained-objects sync depends on the constraint-holder. */ + add_relation(trans_key, ob2_key, "RigidBodyConstraint -> RBC.Object_2"); + } /* Ensure that sim depends on this constraint's transform. */ add_relation(trans_key, rb_simulate_key, "RigidBodyConstraint Transform -> RB Simulation"); } @@ -2276,6 +2297,24 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) add_relation(lamp_parameters_key, shading_key, "Light Shading Parameters"); } +void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket) +{ + build_idproperties(socket->prop); + + if (socket->type == SOCK_OBJECT) { + Object *object = ((bNodeSocketValueObject *)socket->default_value)->value; + if (object != nullptr) { + build_object(object); + } + } + else if (socket->type == SOCK_IMAGE) { + Image *image = ((bNodeSocketValueImage *)socket->default_value)->value; + if (image != nullptr) { + build_image(image); + } + } +} + void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) { if (ntree == nullptr) { @@ -2292,10 +2331,10 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } ID *id = bnode->id; @@ -2602,13 +2641,39 @@ void DepsgraphRelationBuilder::build_simulation(Simulation *simulation) if (built_map_.checkIsBuiltAndTag(simulation)) { return; } + build_idproperties(simulation->id.properties); build_animdata(&simulation->id); build_parameters(&simulation->id); - OperationKey simulation_update_key( + build_nodetree(simulation->nodetree); + build_nested_nodetree(&simulation->id, simulation->nodetree); + + OperationKey simulation_eval_key( &simulation->id, NodeType::SIMULATION, OperationCode::SIMULATION_EVAL); TimeSourceKey time_src_key; - add_relation(time_src_key, simulation_update_key, "TimeSrc -> Simulation"); + add_relation(time_src_key, simulation_eval_key, "TimeSrc -> Simulation"); + + OperationKey nodetree_key( + &simulation->nodetree->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT); + add_relation(nodetree_key, simulation_eval_key, "NodeTree -> Simulation", 0); + + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + if (dependency->id == nullptr) { + continue; + } + build_id(dependency->id); + if (GS(dependency->id->name) == ID_OB) { + Object *object = (Object *)dependency->id; + if (dependency->flag & SIM_DEPENDS_ON_TRANSFORM) { + ComponentKey object_transform_key(&object->id, NodeType::TRANSFORM); + add_relation(object_transform_key, simulation_eval_key, "Object Transform -> Simulation"); + } + if (dependency->flag & SIM_DEPENDS_ON_GEOMETRY) { + ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY); + add_relation(object_geometry_key, simulation_eval_key, "Object Geometry -> Simulation"); + } + } + } } void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index b4b0dc71f85..04f2a3f911d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -46,6 +46,7 @@ #include "intern/node/deg_node_operation.h" struct Base; +struct bNodeSocket; struct CacheFile; struct Camera; struct Collection; @@ -275,6 +276,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_camera(Camera *camera); virtual void build_light(Light *lamp); virtual void build_nodetree(bNodeTree *ntree); + virtual void build_nodetree_socket(bNodeSocket *socket); virtual void build_material(Material *ma); virtual void build_materials(Material **materials, int num_materials); virtual void build_freestyle_lineset(FreestyleLineSet *fls); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h index c48c6489c47..d03903d508c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h @@ -96,7 +96,7 @@ class RNANodeQuery { /* Check whether prop_identifier contains rna_path_component. * - * This checks more than a substring: + * This checks more than a sub-string: * * prop_identifier contains(prop_identifier, "location") * ------------------------ ------------------------------------- diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc new file mode 100644 index 00000000000..d6893ba11d8 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline.cc @@ -0,0 +1,132 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "pipeline.h" + +#include "PIL_time.h" + +#include "BKE_global.h" + +#include "DNA_scene_types.h" + +#include "deg_builder_cycle.h" +#include "deg_builder_nodes.h" +#include "deg_builder_relations.h" +#include "deg_builder_transitive.h" + +namespace blender { +namespace deg { + +AbstractBuilderPipeline::AbstractBuilderPipeline(::Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) + : deg_graph_(reinterpret_cast<Depsgraph *>(graph)), + bmain_(bmain), + scene_(scene), + view_layer_(view_layer), + builder_cache_() +{ +} + +AbstractBuilderPipeline::~AbstractBuilderPipeline() +{ +} + +void AbstractBuilderPipeline::build() +{ + double start_time = 0.0; + if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { + start_time = PIL_check_seconds_timer(); + } + + build_step_sanity_check(); + build_step_nodes(); + build_step_relations(); + build_step_finalize(); + + if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { + printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); + } +} + +void AbstractBuilderPipeline::build_step_sanity_check() +{ + BLI_assert(BLI_findindex(&scene_->view_layers, view_layer_) != -1); + BLI_assert(deg_graph_->scene == scene_); + BLI_assert(deg_graph_->view_layer == view_layer_); +} + +void AbstractBuilderPipeline::build_step_nodes() +{ + /* Generate all the nodes in the graph first */ + unique_ptr<DepsgraphNodeBuilder> node_builder = construct_node_builder(); + node_builder->begin_build(); + build_nodes(*node_builder); + node_builder->end_build(); +} + +void AbstractBuilderPipeline::build_step_relations() +{ + /* Hook up relationships between operations - to determine evaluation order. */ + unique_ptr<DepsgraphRelationBuilder> relation_builder = construct_relation_builder(); + relation_builder->begin_build(); + build_relations(*relation_builder); + relation_builder->build_copy_on_write_relations(); + relation_builder->build_driver_relations(); +} + +void AbstractBuilderPipeline::build_step_finalize() +{ + /* Detect and solve cycles. */ + deg_graph_detect_cycles(deg_graph_); + /* Simplify the graph by removing redundant relations (to optimize + * traversal later). */ + /* TODO: it would be useful to have an option to disable this in cases where + * it is causing trouble. */ + if (G.debug_value == 799) { + deg_graph_transitive_reduction(deg_graph_); + } + /* Store pointers to commonly used valuated datablocks. */ + deg_graph_->scene_cow = (Scene *)deg_graph_->get_cow_id(°_graph_->scene->id); + /* Flush visibility layer and re-schedule nodes for update. */ + deg_graph_build_finalize(bmain_, deg_graph_); + DEG_graph_on_visible_update(bmain_, reinterpret_cast<::Depsgraph *>(deg_graph_), false); +#if 0 + if (!DEG_debug_consistency_check(deg_graph_)) { + printf("Consistency validation failed, ABORTING!\n"); + abort(); + } +#endif + /* Relations are up to date. */ + deg_graph_->need_update = false; +} + +unique_ptr<DepsgraphNodeBuilder> AbstractBuilderPipeline::construct_node_builder() +{ + return std::make_unique<DepsgraphNodeBuilder>(bmain_, deg_graph_, &builder_cache_); +} + +unique_ptr<DepsgraphRelationBuilder> AbstractBuilderPipeline::construct_relation_builder() +{ + return std::make_unique<DepsgraphRelationBuilder>(bmain_, deg_graph_, &builder_cache_); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h new file mode 100644 index 00000000000..2c9c78bb2cb --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline.h @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "deg_builder_cache.h" + +#include "intern/depsgraph_type.h" + +struct Main; +struct Scene; +struct ViewLayer; +struct Depsgraph; + +namespace blender { +namespace deg { + +struct Depsgraph; +class DepsgraphNodeBuilder; +class DepsgraphRelationBuilder; + +/* Base class for Depsgraph Builder pipelines. + * + * Basically it runs through the following steps: + * - sanity check + * - build nodes + * - build relations + * - finalize + */ +class AbstractBuilderPipeline { + public: + AbstractBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + virtual ~AbstractBuilderPipeline(); + + void build(); + + protected: + Depsgraph *deg_graph_; + Main *bmain_; + Scene *scene_; + ViewLayer *view_layer_; + DepsgraphBuilderCache builder_cache_; + + virtual unique_ptr<DepsgraphNodeBuilder> construct_node_builder(); + virtual unique_ptr<DepsgraphRelationBuilder> construct_relation_builder(); + + virtual void build_step_sanity_check(); + void build_step_nodes(); + void build_step_relations(); + void build_step_finalize(); + + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) = 0; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) = 0; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.cc b/source/blender/depsgraph/intern/builder/pipeline_compositor.cc new file mode 100644 index 00000000000..3e56f17fc7e --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.cc @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "pipeline_compositor.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +namespace blender { +namespace deg { + +CompositorBuilderPipeline::CompositorBuilderPipeline( + ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, bNodeTree *nodetree) + : AbstractBuilderPipeline(graph, bmain, scene, view_layer), nodetree_(nodetree) +{ + deg_graph_->is_render_pipeline_depsgraph = true; +} + +void CompositorBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder) +{ + node_builder.build_scene_render(scene_, view_layer_); + node_builder.build_nodetree(nodetree_); +} + +void CompositorBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder) +{ + relation_builder.build_scene_render(scene_, view_layer_); + relation_builder.build_nodetree(nodetree_); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.h b/source/blender/depsgraph/intern/builder/pipeline_compositor.h new file mode 100644 index 00000000000..892ece7c2a4 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.h @@ -0,0 +1,47 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "pipeline.h" + +struct bNodeTree; + +namespace blender { +namespace deg { + +class CompositorBuilderPipeline : public AbstractBuilderPipeline { + public: + CompositorBuilderPipeline( + ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, bNodeTree *nodetree); + + protected: + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; + + private: + bNodeTree *nodetree_; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc new file mode 100644 index 00000000000..e44f554f197 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc @@ -0,0 +1,154 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "pipeline_from_ids.h" + +#include "DNA_layer_types.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +namespace blender { +namespace deg { + +namespace { + +class DepsgraphFromIDsFilter { + public: + DepsgraphFromIDsFilter(ID **ids, const int num_ids) + { + for (int i = 0; i < num_ids; ++i) { + ids_.add(ids[i]); + } + } + + bool contains(ID *id) + { + return ids_.contains(id); + } + + protected: + Set<ID *> ids_; +}; + +class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder { + public: + DepsgraphFromIDsNodeBuilder( + Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache, ID **ids, const int num_ids) + : DepsgraphNodeBuilder(bmain, graph, cache), filter_(ids, num_ids) + { + } + + virtual bool need_pull_base_into_graph(Base *base) override + { + if (!filter_.contains(&base->object->id)) { + return false; + } + return DepsgraphNodeBuilder::need_pull_base_into_graph(base); + } + + virtual void build_object_proxy_group(Object *object, bool is_visible) override + { + if (object->proxy_group == nullptr) { + return; + } + if (!filter_.contains(&object->proxy_group->id)) { + return; + } + DepsgraphNodeBuilder::build_object_proxy_group(object, is_visible); + } + + protected: + DepsgraphFromIDsFilter filter_; +}; + +class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder { + public: + DepsgraphFromIDsRelationBuilder( + Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache, ID **ids, const int num_ids) + : DepsgraphRelationBuilder(bmain, graph, cache), filter_(ids, num_ids) + { + } + + virtual bool need_pull_base_into_graph(Base *base) override + { + if (!filter_.contains(&base->object->id)) { + return false; + } + return DepsgraphRelationBuilder::need_pull_base_into_graph(base); + } + + virtual void build_object_proxy_group(Object *object) override + { + if (object->proxy_group == nullptr) { + return; + } + if (!filter_.contains(&object->proxy_group->id)) { + return; + } + DepsgraphRelationBuilder::build_object_proxy_group(object); + } + + protected: + DepsgraphFromIDsFilter filter_; +}; + +} // namespace + +FromIDsBuilderPipeline::FromIDsBuilderPipeline(::Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + ID **ids, + const int num_ids) + : AbstractBuilderPipeline(graph, bmain, scene, view_layer), ids_(ids), num_ids_(num_ids) +{ +} + +unique_ptr<DepsgraphNodeBuilder> FromIDsBuilderPipeline::construct_node_builder() +{ + return std::make_unique<DepsgraphFromIDsNodeBuilder>( + bmain_, deg_graph_, &builder_cache_, ids_, num_ids_); +} + +unique_ptr<DepsgraphRelationBuilder> FromIDsBuilderPipeline::construct_relation_builder() +{ + return std::make_unique<DepsgraphFromIDsRelationBuilder>( + bmain_, deg_graph_, &builder_cache_, ids_, num_ids_); +} + +void FromIDsBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder) +{ + node_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY); + for (int i = 0; i < num_ids_; ++i) { + node_builder.build_id(ids_[i]); + } +} + +void FromIDsBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder) +{ + relation_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY); + for (int i = 0; i < num_ids_; ++i) { + relation_builder.build_id(ids_[i]); + } +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h new file mode 100644 index 00000000000..4a507f2c728 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "pipeline.h" + +namespace blender { +namespace deg { + +/* Optimized builders for dependency graph built from a given set of IDs. + * + * General notes: + * + * - We pull in all bases if their objects are in the set of IDs. This allows to have proper + * visibility and other flags assigned to the objects. + * All other bases (the ones which points to object which is outside of the set of IDs) are + * completely ignored. + * + * - Proxy groups pointing to objects which are outside of the IDs set are also ignored. + * This way we avoid high-poly character body pulled into the dependency graph when it's coming + * from a library into an animation file and the dependency graph constructed for a proxy rig. */ + +class FromIDsBuilderPipeline : public AbstractBuilderPipeline { + public: + FromIDsBuilderPipeline( + ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, ID **ids, int num_ids); + + protected: + virtual unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override; + virtual unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override; + + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; + + private: + ID **ids_; + const int num_ids_; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.cc b/source/blender/depsgraph/intern/builder/pipeline_render.cc new file mode 100644 index 00000000000..50a37d0d3e4 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_render.cc @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "pipeline_render.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +namespace blender { +namespace deg { + +RenderBuilderPipeline::RenderBuilderPipeline(::Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) + : AbstractBuilderPipeline(graph, bmain, scene, view_layer) +{ + deg_graph_->is_render_pipeline_depsgraph = true; +} + +void RenderBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder) +{ + node_builder.build_scene_render(scene_, view_layer_); +} + +void RenderBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder) +{ + relation_builder.build_scene_render(scene_, view_layer_); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.h b/source/blender/depsgraph/intern/builder/pipeline_render.h new file mode 100644 index 00000000000..df7f9e0de68 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_render.h @@ -0,0 +1,41 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "pipeline.h" + +namespace blender { +namespace deg { + +class RenderBuilderPipeline : public AbstractBuilderPipeline { + public: + RenderBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + + protected: + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc b/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc new file mode 100644 index 00000000000..3223f17f349 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "pipeline_view_layer.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +namespace blender { +namespace deg { + +ViewLayerBuilderPipeline::ViewLayerBuilderPipeline(::Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) + : AbstractBuilderPipeline(graph, bmain, scene, view_layer) +{ +} + +void ViewLayerBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder) +{ + node_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY); +} + +void ViewLayerBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder) +{ + relation_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h new file mode 100644 index 00000000000..fbd7b98acad --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h @@ -0,0 +1,41 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "pipeline.h" + +namespace blender { +namespace deg { + +class ViewLayerBuilderPipeline : public AbstractBuilderPipeline { + public: + ViewLayerBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + + protected: + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc index 458baf4fb1e..d0356f44022 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc @@ -25,6 +25,7 @@ #include <cstdarg> +#include "BLI_dot_export.hh" #include "BLI_utildefines.h" #include "DNA_listBase.h" @@ -41,6 +42,7 @@ #include "intern/node/deg_node_time.h" namespace deg = blender::deg; +namespace dot = blender::dot; /* ****************** */ /* Graphviz Debugging */ @@ -48,8 +50,6 @@ namespace deg = blender::deg; namespace blender { namespace deg { -#define NL "\r\n" - /* Only one should be enabled, defines whether graphviz nodes * get colored by individual types or classes. */ @@ -158,47 +158,43 @@ static int deg_debug_node_color_index(const Node *node) #endif } -struct DebugContext { - FILE *file; +struct DotExportContext { bool show_tags; + dot::DirectedGraph &digraph; + Map<const Node *, dot::Node *> nodes_map; + Map<const Node *, dot::Cluster *> clusters_map; }; -static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) - ATTR_PRINTF_FORMAT(2, 3); -static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) +static void deg_debug_graphviz_legend_color(const char *name, + const char *color, + std::stringstream &ss) { - va_list args; - va_start(args, fmt); - vfprintf(ctx.file, fmt, args); - va_end(args); -} -static void deg_debug_graphviz_legend_color(const DebugContext &ctx, - const char *name, - const char *color) -{ - deg_debug_fprintf(ctx, "<TR>"); - deg_debug_fprintf(ctx, "<TD>%s</TD>", name); - deg_debug_fprintf(ctx, "<TD BGCOLOR=\"%s\"></TD>", color); - deg_debug_fprintf(ctx, "</TR>" NL); + ss << "<TR>"; + ss << "<TD>" << name << "</TD>"; + ss << "<TD BGCOLOR=\"" << color << "\"></TD>"; + ss << "</TR>"; } -static void deg_debug_graphviz_legend(const DebugContext &ctx) +static void deg_debug_graphviz_legend(DotExportContext &ctx) { - deg_debug_fprintf(ctx, "{" NL); - deg_debug_fprintf(ctx, "rank = sink;" NL); - deg_debug_fprintf(ctx, "Legend [shape=none, margin=0, label=<" NL); - deg_debug_fprintf( - ctx, " <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL); - deg_debug_fprintf(ctx, "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>" NL); + dot::Node &legend_node = ctx.digraph.new_node(""); + legend_node.attributes.set("rank", "sink"); + legend_node.attributes.set("shape", "none"); + legend_node.attributes.set("margin", 0); + + std::stringstream ss; + ss << "<"; + ss << "<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">"; + ss << "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>"; #ifdef COLOR_SCHEME_NODE_CLASS const char **colors = deg_debug_colors_light; - deg_debug_graphviz_legend_color(ctx, "Operation", colors[4]); - deg_debug_graphviz_legend_color(ctx, "Component", colors[1]); - deg_debug_graphviz_legend_color(ctx, "ID Node", colors[5]); - deg_debug_graphviz_legend_color(ctx, "NOOP", colors[8]); - deg_debug_graphviz_legend_color(ctx, "Pinned OP", colors[7]); + deg_debug_graphviz_legend_color("Operation", colors[4], ss); + deg_debug_graphviz_legend_color("Component", colors[1], ss); + deg_debug_graphviz_legend_color("ID Node", colors[5], ss); + deg_debug_graphviz_legend_color("NOOP", colors[8], ss); + deg_debug_graphviz_legend_color("Pinned OP", colors[7], ss); #endif #ifdef COLOR_SCHEME_NODE_TYPE @@ -206,18 +202,19 @@ static void deg_debug_graphviz_legend(const DebugContext &ctx) for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; pair++) { DepsNodeFactory *nti = type_get_factory((NodeType)(*pair)[0]); deg_debug_graphviz_legend_color( - ctx, nti->tname().c_str(), deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors]); + ctx, nti->tname().c_str(), deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors], ss); } #endif - deg_debug_fprintf(ctx, "</TABLE>" NL); - deg_debug_fprintf(ctx, ">" NL); - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, "}" NL); + ss << "</TABLE>"; + ss << ">"; + legend_node.attributes.set("label", ss.str()); + legend_node.attributes.set("fontname", deg_debug_graphviz_fontname); } -static void deg_debug_graphviz_node_color(const DebugContext &ctx, const Node *node) +static void deg_debug_graphviz_node_color(DotExportContext &ctx, + const Node *node, + dot::Attributes &dot_attributes) { const char *color_default = "black"; const char *color_modified = "orangered4"; @@ -234,10 +231,12 @@ static void deg_debug_graphviz_node_color(const DebugContext &ctx, const Node *n } } } - deg_debug_fprintf(ctx, "\"%s\"", color); + dot_attributes.set("color", color); } -static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx, const Node *node) +static void deg_debug_graphviz_node_penwidth(DotExportContext &ctx, + const Node *node, + dot::Attributes &dot_attributes) { float penwidth_default = 1.0f; float penwidth_modified = 4.0f; @@ -254,20 +253,20 @@ static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx, const Node } } } - deg_debug_fprintf(ctx, "\"%f\"", penwidth); + dot_attributes.set("penwidth", penwidth); } -static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx, const Node *node) +static void deg_debug_graphviz_node_fillcolor(const Node *node, dot::Attributes &dot_attributes) { const char *defaultcolor = "gainsboro"; int color_index = deg_debug_node_color_index(node); const char *fillcolor = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors]; - deg_debug_fprintf(ctx, "\"%s\"", fillcolor); + dot_attributes.set("fillcolor", fillcolor); } -static void deg_debug_graphviz_relation_color(const DebugContext &ctx, const Relation *rel) +static void deg_debug_graphviz_relation_color(const Relation *rel, dot::DirectedEdge &edge) { const char *color_default = "black"; const char *color_cyclic = "red4"; /* The color of crime scene. */ @@ -279,10 +278,10 @@ static void deg_debug_graphviz_relation_color(const DebugContext &ctx, const Rel else if (rel->flag & RELATION_FLAG_GODMODE) { color = color_godmode; } - deg_debug_fprintf(ctx, "%s", color); + edge.attributes.set("color", color); } -static void deg_debug_graphviz_relation_style(const DebugContext &ctx, const Relation *rel) +static void deg_debug_graphviz_relation_style(const Relation *rel, dot::DirectedEdge &edge) { const char *style_default = "solid"; const char *style_no_flush = "dashed"; @@ -294,10 +293,10 @@ static void deg_debug_graphviz_relation_style(const DebugContext &ctx, const Rel if (rel->flag & RELATION_FLAG_FLUSH_USER_EDIT_ONLY) { style = style_flush_user_only; } - deg_debug_fprintf(ctx, "%s", style); + edge.attributes.set("style", style); } -static void deg_debug_graphviz_relation_arrowhead(const DebugContext &ctx, const Relation *rel) +static void deg_debug_graphviz_relation_arrowhead(const Relation *rel, dot::DirectedEdge &edge) { const char *shape_default = "normal"; const char *shape_no_cow = "box"; @@ -311,12 +310,14 @@ static void deg_debug_graphviz_relation_arrowhead(const DebugContext &ctx, const shape = shape_no_cow; } } - deg_debug_fprintf(ctx, "%s", shape); + edge.attributes.set("arrowhead", shape); } -static void deg_debug_graphviz_node_style(const DebugContext &ctx, const Node *node) +static void deg_debug_graphviz_node_style(DotExportContext &ctx, + const Node *node, + dot::Attributes &dot_attributes) { - const char *base_style = "filled"; /* default style */ + StringRef base_style = "filled"; /* default style */ if (ctx.show_tags) { if (node->get_class() == NodeClass::OPERATION) { OperationNode *op_node = (OperationNode *)node; @@ -327,95 +328,78 @@ static void deg_debug_graphviz_node_style(const DebugContext &ctx, const Node *n } switch (node->get_class()) { case NodeClass::GENERIC: - deg_debug_fprintf(ctx, "\"%s\"", base_style); + dot_attributes.set("style", base_style); break; case NodeClass::COMPONENT: - deg_debug_fprintf(ctx, "\"%s\"", base_style); + dot_attributes.set("style", base_style); break; case NodeClass::OPERATION: - deg_debug_fprintf(ctx, "\"%s,rounded\"", base_style); + dot_attributes.set("style", base_style + ",rounded"); break; } } -static void deg_debug_graphviz_node_single(const DebugContext &ctx, const Node *node) +static void deg_debug_graphviz_node_single(DotExportContext &ctx, + const Node *node, + dot::Cluster *parent_cluster) { - const char *shape = "box"; string name = node->identifier(); - deg_debug_fprintf(ctx, "// %s\n", name.c_str()); - deg_debug_fprintf(ctx, "\"node_%p\"", node); - deg_debug_fprintf(ctx, "["); - // deg_debug_fprintf(ctx, "label=<<B>%s</B>>", name); - deg_debug_fprintf(ctx, "label=<%s>", name.c_str()); - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_node_label_size); - deg_debug_fprintf(ctx, ",shape=%s", shape); - deg_debug_fprintf(ctx, ",style="); - deg_debug_graphviz_node_style(ctx, node); - deg_debug_fprintf(ctx, ",color="); - deg_debug_graphviz_node_color(ctx, node); - deg_debug_fprintf(ctx, ",fillcolor="); - deg_debug_graphviz_node_fillcolor(ctx, node); - deg_debug_fprintf(ctx, ",penwidth="); - deg_debug_graphviz_node_penwidth(ctx, node); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); + + dot::Node &dot_node = ctx.digraph.new_node(name); + ctx.nodes_map.add_new(node, &dot_node); + dot_node.set_parent_cluster(parent_cluster); + dot_node.attributes.set("fontname", deg_debug_graphviz_fontname); + dot_node.attributes.set("frontsize", deg_debug_graphviz_node_label_size); + dot_node.attributes.set("shape", "box"); + + deg_debug_graphviz_node_style(ctx, node, dot_node.attributes); + deg_debug_graphviz_node_color(ctx, node, dot_node.attributes); + deg_debug_graphviz_node_fillcolor(node, dot_node.attributes); + deg_debug_graphviz_node_penwidth(ctx, node, dot_node.attributes); } -static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx, const Node *node) +static dot::Cluster °_debug_graphviz_node_cluster_create(DotExportContext &ctx, + const Node *node, + dot::Cluster *parent_cluster) { string name = node->identifier(); - deg_debug_fprintf(ctx, "// %s\n", name.c_str()); - deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node); - // deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name); - deg_debug_fprintf(ctx, "label=<%s>;" NL, name.c_str()); - deg_debug_fprintf(ctx, "fontname=\"%s\";" NL, deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, "fontsize=%f;" NL, deg_debug_graphviz_node_label_size); - deg_debug_fprintf(ctx, "margin=\"%d\";" NL, 16); - deg_debug_fprintf(ctx, "style="); - deg_debug_graphviz_node_style(ctx, node); - deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "color="); - deg_debug_graphviz_node_color(ctx, node); - deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "fillcolor="); - deg_debug_graphviz_node_fillcolor(ctx, node); - deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "penwidth="); - deg_debug_graphviz_node_penwidth(ctx, node); - deg_debug_fprintf(ctx, ";" NL); + dot::Cluster &cluster = ctx.digraph.new_cluster(name); + cluster.set_parent_cluster(parent_cluster); + cluster.attributes.set("fontname", deg_debug_graphviz_fontname); + cluster.attributes.set("fontsize", deg_debug_graphviz_node_label_size); + cluster.attributes.set("margin", 16); + deg_debug_graphviz_node_style(ctx, node, cluster.attributes); + deg_debug_graphviz_node_color(ctx, node, cluster.attributes); + deg_debug_graphviz_node_fillcolor(node, cluster.attributes); + deg_debug_graphviz_node_penwidth(ctx, node, cluster.attributes); /* dummy node, so we can add edges between clusters */ - deg_debug_fprintf(ctx, "\"node_%p\"", node); - deg_debug_fprintf(ctx, "["); - deg_debug_fprintf(ctx, "shape=%s", "point"); - deg_debug_fprintf(ctx, ",style=%s", "invis"); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); + dot::Node &dot_node = ctx.digraph.new_node(""); + dot_node.attributes.set("shape", "point"); + dot_node.attributes.set("style", "invis"); + dot_node.set_parent_cluster(&cluster); + ctx.nodes_map.add_new(node, &dot_node); + ctx.clusters_map.add_new(node, &cluster); + return cluster; } -static void deg_debug_graphviz_node_cluster_end(const DebugContext &ctx) -{ - deg_debug_fprintf(ctx, "}" NL); - deg_debug_fprintf(ctx, NL); -} +static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph); +static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph); -static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, const Depsgraph *graph); -static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, const Depsgraph *graph); - -static void deg_debug_graphviz_node(const DebugContext &ctx, const Node *node) +static void deg_debug_graphviz_node(DotExportContext &ctx, + const Node *node, + dot::Cluster *parent_cluster) { switch (node->type) { case NodeType::ID_REF: { const IDNode *id_node = (const IDNode *)node; if (id_node->components.is_empty()) { - deg_debug_graphviz_node_single(ctx, node); + deg_debug_graphviz_node_single(ctx, node, parent_cluster); } else { - deg_debug_graphviz_node_cluster_begin(ctx, node); + dot::Cluster &cluster = deg_debug_graphviz_node_cluster_create(ctx, node, parent_cluster); for (const ComponentNode *comp : id_node->components.values()) { - deg_debug_graphviz_node(ctx, comp); + deg_debug_graphviz_node(ctx, comp, &cluster); } - deg_debug_graphviz_node_cluster_end(ctx); } break; } @@ -445,127 +429,72 @@ static void deg_debug_graphviz_node(const DebugContext &ctx, const Node *node) case NodeType::GENERIC_DATABLOCK: case NodeType::SIMULATION: { ComponentNode *comp_node = (ComponentNode *)node; - if (!comp_node->operations.is_empty()) { - deg_debug_graphviz_node_cluster_begin(ctx, node); - for (Node *op_node : comp_node->operations) { - deg_debug_graphviz_node(ctx, op_node); - } - deg_debug_graphviz_node_cluster_end(ctx); + if (comp_node->operations.is_empty()) { + deg_debug_graphviz_node_single(ctx, node, parent_cluster); } else { - deg_debug_graphviz_node_single(ctx, node); + dot::Cluster &cluster = deg_debug_graphviz_node_cluster_create(ctx, node, parent_cluster); + for (Node *op_node : comp_node->operations) { + deg_debug_graphviz_node(ctx, op_node, &cluster); + } } break; } case NodeType::UNDEFINED: case NodeType::TIMESOURCE: case NodeType::OPERATION: - deg_debug_graphviz_node_single(ctx, node); + deg_debug_graphviz_node_single(ctx, node, parent_cluster); break; case NodeType::NUM_TYPES: break; } } -static bool deg_debug_graphviz_is_cluster(const Node *node) -{ - switch (node->type) { - case NodeType::ID_REF: { - const IDNode *id_node = (const IDNode *)node; - return !id_node->components.is_empty(); - } - case NodeType::PARAMETERS: - case NodeType::ANIMATION: - case NodeType::TRANSFORM: - case NodeType::PROXY: - case NodeType::GEOMETRY: - case NodeType::SEQUENCER: - case NodeType::EVAL_POSE: - case NodeType::BONE: { - ComponentNode *comp_node = (ComponentNode *)node; - return !comp_node->operations.is_empty(); - } - default: - return false; - } -} - -static bool deg_debug_graphviz_is_owner(const Node *node, const Node *other) -{ - switch (node->get_class()) { - case NodeClass::COMPONENT: { - ComponentNode *comp_node = (ComponentNode *)node; - if (comp_node->owner == other) { - return true; - } - break; - } - case NodeClass::OPERATION: { - OperationNode *op_node = (OperationNode *)node; - if (op_node->owner == other) { - return true; - } - else if (op_node->owner->owner == other) { - return true; - } - break; - } - default: - break; - } - return false; -} - -static void deg_debug_graphviz_node_relations(const DebugContext &ctx, const Node *node) +static void deg_debug_graphviz_node_relations(DotExportContext &ctx, const Node *node) { for (Relation *rel : node->inlinks) { float penwidth = 2.0f; - const Node *tail = rel->to; /* same as node */ - const Node *head = rel->from; - deg_debug_fprintf( - ctx, "// %s -> %s\n", head->identifier().c_str(), tail->identifier().c_str()); - deg_debug_fprintf(ctx, "\"node_%p\"", head); - deg_debug_fprintf(ctx, " -> "); - deg_debug_fprintf(ctx, "\"node_%p\"", tail); + const Node *head = rel->to; /* same as node */ + const Node *tail = rel->from; + dot::Node &dot_tail = *ctx.nodes_map.lookup(tail); + dot::Node &dot_head = *ctx.nodes_map.lookup(head); + + dot::DirectedEdge &edge = ctx.digraph.new_edge(dot_tail, dot_head); - deg_debug_fprintf(ctx, "["); /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */ - deg_debug_fprintf(ctx, "id=\"%s\"", rel->name); - // deg_debug_fprintf(ctx, "label=\"%s\"", rel->name); - deg_debug_fprintf(ctx, ",color="); - deg_debug_graphviz_relation_color(ctx, rel); - deg_debug_fprintf(ctx, ",style="); - deg_debug_graphviz_relation_style(ctx, rel); - deg_debug_fprintf(ctx, ",arrowhead="); - deg_debug_graphviz_relation_arrowhead(ctx, rel); - deg_debug_fprintf(ctx, ",penwidth=\"%f\"", penwidth); + edge.attributes.set("id", rel->name); + deg_debug_graphviz_relation_color(rel, edge); + deg_debug_graphviz_relation_style(rel, edge); + deg_debug_graphviz_relation_arrowhead(rel, edge); + edge.attributes.set("penwidth", penwidth); + /* NOTE: edge from node to own cluster is not possible and gives graphviz * warning, avoid this here by just linking directly to the invisible * placeholder node. */ - if (deg_debug_graphviz_is_cluster(tail) && !deg_debug_graphviz_is_owner(head, tail)) { - deg_debug_fprintf(ctx, ",ltail=\"cluster_%p\"", tail); + dot::Cluster *tail_cluster = ctx.clusters_map.lookup_default(tail, nullptr); + if (tail_cluster != nullptr && tail_cluster->contains(dot_head)) { + edge.attributes.set("ltail", tail_cluster->name()); } - if (deg_debug_graphviz_is_cluster(head) && !deg_debug_graphviz_is_owner(tail, head)) { - deg_debug_fprintf(ctx, ",lhead=\"cluster_%p\"", head); + dot::Cluster *head_cluster = ctx.clusters_map.lookup_default(head, nullptr); + if (head_cluster != nullptr && head_cluster->contains(dot_tail)) { + edge.attributes.set("lhead", head_cluster->name()); } - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); } } -static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, const Depsgraph *graph) +static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph) { for (Node *node : graph->id_nodes) { - deg_debug_graphviz_node(ctx, node); + deg_debug_graphviz_node(ctx, node, nullptr); } TimeSourceNode *time_source = graph->find_time_source(); if (time_source != nullptr) { - deg_debug_graphviz_node(ctx, time_source); + deg_debug_graphviz_node(ctx, time_source, nullptr); } } -static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, const Depsgraph *graph) +static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph) { for (IDNode *id_node : graph->id_nodes) { for (ComponentNode *comp_node : id_node->components.values()) { @@ -592,27 +521,23 @@ void DEG_debug_relations_graphviz(const Depsgraph *graph, FILE *f, const char *l const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph); - deg::DebugContext ctx; - ctx.file = f; - - deg::deg_debug_fprintf(ctx, "digraph depgraph {" NL); - deg::deg_debug_fprintf(ctx, "rankdir=LR;" NL); - deg::deg_debug_fprintf(ctx, "graph ["); - deg::deg_debug_fprintf(ctx, "compound=true"); - deg::deg_debug_fprintf(ctx, ",labelloc=\"t\""); - deg::deg_debug_fprintf(ctx, ",fontsize=%f", deg::deg_debug_graphviz_graph_label_size); - deg::deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg::deg_debug_graphviz_fontname); - deg::deg_debug_fprintf(ctx, ",label=\"%s\"", label); - deg::deg_debug_fprintf(ctx, ",splines=ortho"); - deg::deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato - deg::deg_debug_fprintf(ctx, "];" NL); + dot::DirectedGraph digraph; + deg::DotExportContext ctx{false, digraph}; + + digraph.set_rankdir(dot::Attr_rankdir::LeftToRight); + digraph.attributes.set("compound", "true"); + digraph.attributes.set("labelloc", "t"); + digraph.attributes.set("fontsize", deg::deg_debug_graphviz_graph_label_size); + digraph.attributes.set("fontname", deg::deg_debug_graphviz_fontname); + digraph.attributes.set("label", label); + digraph.attributes.set("splines", "ortho"); + digraph.attributes.set("overlap", "scalexy"); deg::deg_debug_graphviz_graph_nodes(ctx, deg_graph); deg::deg_debug_graphviz_graph_relations(ctx, deg_graph); deg::deg_debug_graphviz_legend(ctx); - deg::deg_debug_fprintf(ctx, "}" NL); + std::string dot_string = digraph.to_dot_string(); + fprintf(f, "%s", dot_string.c_str()); } - -#undef NL diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index c11d051e663..fb933cb38f3 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -43,12 +43,11 @@ #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_debug.h" -#include "builder/deg_builder.h" -#include "builder/deg_builder_cache.h" -#include "builder/deg_builder_cycle.h" -#include "builder/deg_builder_nodes.h" #include "builder/deg_builder_relations.h" -#include "builder/deg_builder_transitive.h" +#include "builder/pipeline_compositor.h" +#include "builder/pipeline_from_ids.h" +#include "builder/pipeline_render.h" +#include "builder/pipeline_view_layer.h" #include "intern/debug/deg_debug.h" @@ -209,65 +208,14 @@ struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle) /* ******************** */ /* Graph Building API's */ -static void graph_build_finalize_common(deg::Depsgraph *deg_graph, Main *bmain) -{ - /* Detect and solve cycles. */ - deg::deg_graph_detect_cycles(deg_graph); - /* Simplify the graph by removing redundant relations (to optimize - * traversal later). */ - /* TODO: it would be useful to have an option to disable this in cases where - * it is causing trouble. */ - if (G.debug_value == 799) { - deg::deg_graph_transitive_reduction(deg_graph); - } - /* Store pointers to commonly used valuated datablocks. */ - deg_graph->scene_cow = (Scene *)deg_graph->get_cow_id(°_graph->scene->id); - /* Flush visibility layer and re-schedule nodes for update. */ - deg::deg_graph_build_finalize(bmain, deg_graph); - DEG_graph_on_visible_update(bmain, reinterpret_cast<::Depsgraph *>(deg_graph), false); -#if 0 - if (!DEG_debug_consistency_check(deg_graph)) { - printf("Consistency validation failed, ABORTING!\n"); - abort(); - } -#endif - /* Relations are up to date. */ - deg_graph->need_update = false; -} - /* Build depsgraph for the given scene layer, and dump results in given graph container. */ void DEG_graph_build_from_view_layer(Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer) { - double start_time = 0.0; - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - start_time = PIL_check_seconds_timer(); - } - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - /* Perform sanity checks. */ - BLI_assert(BLI_findindex(&scene->view_layers, view_layer) != -1); - BLI_assert(deg_graph->scene == scene); - BLI_assert(deg_graph->view_layer == view_layer); - deg::DepsgraphBuilderCache builder_cache; - /* Generate all the nodes in the graph first */ - deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache); - node_builder.begin_build(); - node_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY); - node_builder.end_build(); - /* Hook up relationships between operations - to determine evaluation order. */ - deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache); - relation_builder.begin_build(); - relation_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY); - relation_builder.build_copy_on_write_relations(); - relation_builder.build_driver_relations(); - /* Finalize building. */ - graph_build_finalize_common(deg_graph, bmain); - /* Finish statistics. */ - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + deg::ViewLayerBuilderPipeline builder(graph, bmain, scene, view_layer); + builder.build(); } void DEG_graph_build_for_render_pipeline(Depsgraph *graph, @@ -275,170 +223,17 @@ void DEG_graph_build_for_render_pipeline(Depsgraph *graph, Scene *scene, ViewLayer *view_layer) { - double start_time = 0.0; - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - start_time = PIL_check_seconds_timer(); - } - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - /* Perform sanity checks. */ - BLI_assert(deg_graph->scene == scene); - deg_graph->is_render_pipeline_depsgraph = true; - deg::DepsgraphBuilderCache builder_cache; - /* Generate all the nodes in the graph first */ - deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache); - node_builder.begin_build(); - node_builder.build_scene_render(scene, view_layer); - node_builder.end_build(); - /* Hook up relationships between operations - to determine evaluation - * order. */ - deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache); - relation_builder.begin_build(); - relation_builder.build_scene_render(scene, view_layer); - relation_builder.build_copy_on_write_relations(); - relation_builder.build_driver_relations(); - /* Finalize building. */ - graph_build_finalize_common(deg_graph, bmain); - /* Finish statistics. */ - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + deg::RenderBuilderPipeline builder(graph, bmain, scene, view_layer); + builder.build(); } void DEG_graph_build_for_compositor_preview( Depsgraph *graph, Main *bmain, Scene *scene, struct ViewLayer *view_layer, bNodeTree *nodetree) { - double start_time = 0.0; - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - start_time = PIL_check_seconds_timer(); - } - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - /* Perform sanity checks. */ - BLI_assert(deg_graph->scene == scene); - deg_graph->is_render_pipeline_depsgraph = true; - deg::DepsgraphBuilderCache builder_cache; - /* Generate all the nodes in the graph first */ - deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache); - node_builder.begin_build(); - node_builder.build_scene_render(scene, view_layer); - node_builder.build_nodetree(nodetree); - node_builder.end_build(); - /* Hook up relationships between operations - to determine evaluation - * order. */ - deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache); - relation_builder.begin_build(); - relation_builder.build_scene_render(scene, view_layer); - relation_builder.build_nodetree(nodetree); - relation_builder.build_copy_on_write_relations(); - relation_builder.build_driver_relations(); - /* Finalize building. */ - graph_build_finalize_common(deg_graph, bmain); - /* Finish statistics. */ - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + deg::CompositorBuilderPipeline builder(graph, bmain, scene, view_layer, nodetree); + builder.build(); } -/* Optimized builders for dependency graph built from a given set of IDs. - * - * General notes: - * - * - We pull in all bases if their objects are in the set of IDs. This allows to have proper - * visibility and other flags assigned to the objects. - * All other bases (the ones which points to object which is outside of the set of IDs) are - * completely ignored. - * - * - Proxy groups pointing to objects which are outside of the IDs set are also ignored. - * This way we avoid high-poly character body pulled into the dependency graph when it's coming - * from a library into an animation file and the dependency graph constructed for a proxy rig. */ - -namespace blender { -namespace deg { -namespace { - -class DepsgraphFromIDsFilter { - public: - DepsgraphFromIDsFilter(ID **ids, const int num_ids) - { - for (int i = 0; i < num_ids; ++i) { - ids_.add(ids[i]); - } - } - - bool contains(ID *id) - { - return ids_.contains(id); - } - - protected: - Set<ID *> ids_; -}; - -class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder { - public: - DepsgraphFromIDsNodeBuilder( - Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache, ID **ids, const int num_ids) - : DepsgraphNodeBuilder(bmain, graph, cache), filter_(ids, num_ids) - { - } - - virtual bool need_pull_base_into_graph(Base *base) override - { - if (!filter_.contains(&base->object->id)) { - return false; - } - return DepsgraphNodeBuilder::need_pull_base_into_graph(base); - } - - virtual void build_object_proxy_group(Object *object, bool is_visible) override - { - if (object->proxy_group == nullptr) { - return; - } - if (!filter_.contains(&object->proxy_group->id)) { - return; - } - DepsgraphNodeBuilder::build_object_proxy_group(object, is_visible); - } - - protected: - DepsgraphFromIDsFilter filter_; -}; - -class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder { - public: - DepsgraphFromIDsRelationBuilder( - Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache, ID **ids, const int num_ids) - : DepsgraphRelationBuilder(bmain, graph, cache), filter_(ids, num_ids) - { - } - - virtual bool need_pull_base_into_graph(Base *base) override - { - if (!filter_.contains(&base->object->id)) { - return false; - } - return DepsgraphRelationBuilder::need_pull_base_into_graph(base); - } - - virtual void build_object_proxy_group(Object *object) override - { - if (object->proxy_group == nullptr) { - return; - } - if (!filter_.contains(&object->proxy_group->id)) { - return; - } - DepsgraphRelationBuilder::build_object_proxy_group(object); - } - - protected: - DepsgraphFromIDsFilter filter_; -}; - -} // namespace -} // namespace deg -} // namespace blender - void DEG_graph_build_from_ids(Depsgraph *graph, Main *bmain, Scene *scene, @@ -446,40 +241,8 @@ void DEG_graph_build_from_ids(Depsgraph *graph, ID **ids, const int num_ids) { - double start_time = 0.0; - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - start_time = PIL_check_seconds_timer(); - } - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - /* Perform sanity checks. */ - BLI_assert(BLI_findindex(&scene->view_layers, view_layer) != -1); - BLI_assert(deg_graph->scene == scene); - BLI_assert(deg_graph->view_layer == view_layer); - deg::DepsgraphBuilderCache builder_cache; - /* Generate all the nodes in the graph first */ - deg::DepsgraphFromIDsNodeBuilder node_builder(bmain, deg_graph, &builder_cache, ids, num_ids); - node_builder.begin_build(); - node_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY); - for (int i = 0; i < num_ids; ++i) { - node_builder.build_id(ids[i]); - } - node_builder.end_build(); - /* Hook up relationships between operations - to determine evaluation order. */ - deg::DepsgraphFromIDsRelationBuilder relation_builder( - bmain, deg_graph, &builder_cache, ids, num_ids); - relation_builder.begin_build(); - relation_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY); - for (int i = 0; i < num_ids; ++i) { - relation_builder.build_id(ids[i]); - } - relation_builder.build_copy_on_write_relations(); - relation_builder.build_driver_relations(); - /* Finalize building. */ - graph_build_finalize_common(deg_graph, bmain); - /* Finish statistics. */ - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + deg::FromIDsBuilderPipeline builder(graph, bmain, scene, view_layer, ids, num_ids); + builder.build(); } /* Tag graph relations for update. */ diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 848275eb899..1d8fba21857 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -48,11 +48,8 @@ #include "BKE_idtype.h" #include "BKE_node.h" #include "BKE_scene.h" -#include "BKE_workspace.h" - -#define new new_ #include "BKE_screen.h" -#undef new +#include "BKE_workspace.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" @@ -786,6 +783,7 @@ void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type) DEG_graph_id_type_tag(depsgraph, ID_LA); DEG_graph_id_type_tag(depsgraph, ID_WO); DEG_graph_id_type_tag(depsgraph, ID_SCE); + DEG_graph_id_type_tag(depsgraph, ID_SIM); } const int id_type_index = BKE_idtype_idcode_to_index(id_type); deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph); 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 c1e1ed3036d..89df41944e5 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 @@ -120,6 +120,7 @@ union NestedIDHackTempStorage { Scene scene; Tex tex; World world; + Simulation simulation; }; /* Set nested owned ID pointers to nullptr. */ @@ -137,6 +138,7 @@ void nested_id_hack_discard_pointers(ID *id_cow) SPECIAL_CASE(ID_MA, Material, nodetree) SPECIAL_CASE(ID_TE, Tex, nodetree) SPECIAL_CASE(ID_WO, World, nodetree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree) SPECIAL_CASE(ID_CU, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) @@ -185,6 +187,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage SPECIAL_CASE(ID_MA, Material, nodetree, material) SPECIAL_CASE(ID_TE, Tex, nodetree, tex) SPECIAL_CASE(ID_WO, World, nodetree, world) + SPECIAL_CASE(ID_SIM, Simulation, nodetree, simulation) SPECIAL_CASE(ID_CU, Curve, key, curve) SPECIAL_CASE(ID_LT, Lattice, key, lattice) @@ -224,6 +227,7 @@ void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id) SPECIAL_CASE(ID_SCE, Scene, nodetree) SPECIAL_CASE(ID_TE, Tex, nodetree) SPECIAL_CASE(ID_WO, World, nodetree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree) SPECIAL_CASE(ID_CU, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) @@ -261,6 +265,7 @@ void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow) SPECIAL_CASE(ID_SCE, Scene, nodetree, bNodeTree) SPECIAL_CASE(ID_TE, Tex, nodetree, bNodeTree) SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree, bNodeTree) SPECIAL_CASE(ID_CU, Curve, key, Key) SPECIAL_CASE(ID_LT, Lattice, key, Key) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index cfcd4e0c65a..f85b03dc517 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC intern/draw_color_management.c intern/draw_common.c intern/draw_debug.c + intern/draw_fluid.c intern/draw_hair.c intern/draw_instance_data.c intern/draw_manager.c @@ -186,10 +187,10 @@ set(LIB ) data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC) -data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC) -data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC) +data_to_c_simple(engines/eevee/shaders/closure_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/common_uniforms_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/common_utiltex_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lights_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC) @@ -204,8 +205,8 @@ data_to_c_simple(engines/eevee/shaders/lightprobe_grid_display_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_grid_fill_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_vert.glsl SRC) -data_to_c_simple(engines/eevee/shaders/lit_surface_frag.glsl SRC) -data_to_c_simple(engines/eevee/shaders/lit_surface_vert.glsl SRC) +data_to_c_simple(engines/eevee/shaders/lookdev_world_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/closure_lit_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_bloom_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_dof_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_dof_frag.glsl SRC) @@ -229,7 +230,6 @@ data_to_c_simple(engines/eevee/shaders/object_motion_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/prepass_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/prepass_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/shadow_accum_frag.glsl SRC) - data_to_c_simple(engines/eevee/shaders/shadow_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/shadow_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/bsdf_lut_frag.glsl SRC) @@ -240,9 +240,14 @@ data_to_c_simple(engines/eevee/shaders/octahedron_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/cubemap_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/bsdf_sampling_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/raytrace_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/renderpass_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/renderpass_postprocess_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/ltc_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/ssr_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/surface_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/surface_geom.glsl SRC) +data_to_c_simple(engines/eevee/shaders/surface_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/surface_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/update_noise_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/volumetric_accum_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/volumetric_lib.glsl SRC) @@ -288,6 +293,8 @@ data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC) data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC) +data_to_c_simple(intern/shaders/common_math_lib.glsl SRC) +data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC) data_to_c_simple(intern/shaders/common_view_lib.glsl SRC) data_to_c_simple(intern/shaders/common_fxaa_lib.glsl SRC) data_to_c_simple(intern/shaders/common_smaa_lib.glsl SRC) @@ -397,6 +404,13 @@ data_to_c_simple(engines/overlay/shaders/xray_fade_frag.glsl SRC) list(APPEND INC ) +if(WITH_MOD_FLUID) + list(APPEND INC + ../../../intern/mantaflow/extern + ) + add_definitions(-DWITH_FLUID) +endif() + if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c index 4a3cc36ddef..05cd6426911 100644 --- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c @@ -54,24 +54,30 @@ extern char datatoc_common_view_lib_glsl[]; static void eevee_create_shader_depth_of_field(const bool use_alpha) { - char *frag = BLI_string_joinN(datatoc_common_view_lib_glsl, datatoc_effect_dof_frag_glsl); - e_data.dof_downsample_sh[use_alpha] = DRW_shader_create_fullscreen( - frag, + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.dof_downsample_sh[use_alpha] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_dof_frag_glsl, + lib, use_alpha ? "#define USE_ALPHA_DOF\n" "#define STEP_DOWNSAMPLE\n" : "#define STEP_DOWNSAMPLE\n"); - e_data.dof_scatter_sh[use_alpha] = DRW_shader_create(datatoc_effect_dof_vert_glsl, - NULL, - frag, - use_alpha ? "#define USE_ALPHA_DOF\n" - "#define STEP_SCATTER\n" : - "#define STEP_SCATTER\n"); - e_data.dof_resolve_sh[use_alpha] = DRW_shader_create_fullscreen(frag, - use_alpha ? - "#define USE_ALPHA_DOF\n" - "#define STEP_RESOLVE\n" : - "#define STEP_RESOLVE\n"); - MEM_freeN(frag); + + e_data.dof_scatter_sh[use_alpha] = DRW_shader_create_with_shaderlib( + datatoc_effect_dof_vert_glsl, + NULL, + datatoc_effect_dof_frag_glsl, + lib, + use_alpha ? "#define USE_ALPHA_DOF\n" + "#define STEP_SCATTER\n" : + "#define STEP_SCATTER\n"); + + e_data.dof_resolve_sh[use_alpha] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_dof_frag_glsl, + lib, + use_alpha ? "#define USE_ALPHA_DOF\n" + "#define STEP_RESOLVE\n" : + "#define STEP_RESOLVE\n"); } int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata), diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index a142648d08d..d77c6600026 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -32,6 +32,8 @@ #include "DNA_world_types.h" +#include "IMB_imbuf.h" + #include "eevee_private.h" #include "eevee_engine.h" /* own include */ diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index a2f7686619f..47a913640c7 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -335,38 +335,28 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat { DRW_PASS_CREATE(psl->probe_background, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); DRWShadingGroup *grp = NULL; + EEVEE_lookdev_cache_init(vedata, sldata, psl->probe_background, pinfo, &grp); - Scene *scene = draw_ctx->scene; - World *wo = scene->world; - - /* LookDev */ - EEVEE_lookdev_cache_init(vedata, sldata, &grp, psl->probe_background, wo, pinfo); + if (grp == NULL) { + Scene *scene = draw_ctx->scene; + World *world = (scene->world) ? scene->world : EEVEE_world_default_get(); - if (!grp && wo) { - struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, wo, VAR_WORLD_PROBE); + const int options = VAR_WORLD_BACKGROUND | VAR_WORLD_PROBE; + struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options); grp = DRW_shgroup_material_create(gpumat, psl->probe_background); DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); - /* TODO (fclem): remove those (need to clean the GLSL files). */ - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_call(grp, geom, NULL); } - /* Fallback if shader fails or if not using nodetree. */ - if (grp == NULL) { - grp = DRW_shgroup_create(EEVEE_shaders_probe_default_sh_get(), psl->probe_background); - DRW_shgroup_uniform_vec3(grp, "color", G_draw.block.colorBackground, 1); - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); - DRW_shgroup_call(grp, geom, NULL); - } + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); + DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); } if (DRW_state_draw_support()) { diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index b044213e029..403a8e2af55 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -97,10 +97,9 @@ static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerD void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, - DRWShadingGroup **r_grp, DRWPass *pass, - World *UNUSED(world), - EEVEE_LightProbesInfo *pinfo) + EEVEE_LightProbesInfo *pinfo, + DRWShadingGroup **r_shgrp) { EEVEE_StorageList *stl = vedata->stl; EEVEE_TextureList *txl = vedata->txl; @@ -153,89 +152,88 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, const View3DShading *shading = &v3d->shading; StudioLight *sl = BKE_studiolight_find(shading->lookdev_light, STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE); - if (sl && (sl->flag & STUDIOLIGHT_TYPE_WORLD)) { - GPUShader *shader = probe_render ? EEVEE_shaders_default_studiolight_sh_get() : - EEVEE_shaders_background_studiolight_sh_get(); + if (sl == NULL || (sl->flag & STUDIOLIGHT_TYPE_WORLD) == 0) { + return; + } + + GPUShader *shader = probe_render ? EEVEE_shaders_studiolight_probe_sh_get() : + EEVEE_shaders_studiolight_background_sh_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - int cube_res = scene_eval->eevee.gi_cubemap_resolution; + const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); + int cube_res = scene_eval->eevee.gi_cubemap_resolution; - /* If one of the component is missing we start from scratch. */ - if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) || - (txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) || - (g_data->light_cache && g_data->light_cache->ref_res != cube_res)) { - eevee_lookdev_lightcache_delete(vedata); - } + /* If one of the component is missing we start from scratch. */ + if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) || + (txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) || + (g_data->light_cache && g_data->light_cache->ref_res != cube_res)) { + eevee_lookdev_lightcache_delete(vedata); + } - if (stl->lookdev_lightcache == NULL) { + if (stl->lookdev_lightcache == NULL) { #if defined(IRRADIANCE_SH_L2) - int grid_res = 4; + int grid_res = 4; #elif defined(IRRADIANCE_HL2) - int grid_res = 4; + int grid_res = 4; #endif - stl->lookdev_lightcache = EEVEE_lightcache_create( - 1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1}); - - /* XXX: Fix memleak. TODO find out why. */ - MEM_SAFE_FREE(stl->lookdev_cube_mips); - - /* We do this to use a special light cache for lookdev. - * This light-cache needs to be per viewport. But we need to - * have correct freeing when the viewport is closed. So we - * need to reference all textures to the txl and the memblocks - * to the stl. */ - stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data; - stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data; - stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips; - txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex; - txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex; - } - - g_data->light_cache = stl->lookdev_lightcache; - - DRWShadingGroup *grp = *r_grp = DRW_shgroup_create(shader, pass); - axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z); - DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix); - - if (probe_render) { - DRW_shgroup_uniform_float_copy( - grp, "studioLightIntensity", shading->studiolight_intensity); - BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); - DRW_shgroup_uniform_texture(grp, "image", sl->equirect_radiance_gputexture); - /* Do not fadeout when doing probe rendering, only when drawing the background */ - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); - } - else { - float background_alpha = g_data->background_alpha * shading->studiolight_background; - float studiolight_blur = powf(shading->studiolight_blur, 2.5f); - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha); - DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur); - DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - } - - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - - /* Do we need to recalc the lightprobes? */ - if (g_data->studiolight_index != sl->index || - g_data->studiolight_rot_z != shading->studiolight_rot_z || - g_data->studiolight_intensity != shading->studiolight_intensity || - g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution || - g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp || - g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) { - stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD; - g_data->studiolight_index = sl->index; - g_data->studiolight_rot_z = shading->studiolight_rot_z; - g_data->studiolight_intensity = shading->studiolight_intensity; - g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution; - g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp; - g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality; - } + stl->lookdev_lightcache = EEVEE_lightcache_create( + 1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1}); + + /* XXX: Fix memleak. TODO find out why. */ + MEM_SAFE_FREE(stl->lookdev_cube_mips); + + /* We do this to use a special light cache for lookdev. + * This light-cache needs to be per viewport. But we need to + * have correct freeing when the viewport is closed. So we + * need to reference all textures to the txl and the memblocks + * to the stl. */ + stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data; + stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data; + stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips; + txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex; + txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex; + } + + g_data->light_cache = stl->lookdev_lightcache; + + DRWShadingGroup *grp = DRW_shgroup_create(shader, pass); + axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z); + DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix); + + if (probe_render) { + /* Avoid artifact with equirectangular mapping. */ + eGPUSamplerState state = (GPU_SAMPLER_FILTER | GPU_SAMPLER_REPEAT_S); + DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity); + BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); + DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state); + /* Do not fadeout when doing probe rendering, only when drawing the background */ + DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); + } + else { + float background_alpha = g_data->background_alpha * shading->studiolight_background; + float studiolight_blur = powf(shading->studiolight_blur, 2.5f); + DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha); + DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur); + DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx); + } + + /* Common UBOs are setup latter. */ + *r_shgrp = grp; + + /* Do we need to recalc the lightprobes? */ + if (g_data->studiolight_index != sl->index || + g_data->studiolight_rot_z != shading->studiolight_rot_z || + g_data->studiolight_intensity != shading->studiolight_intensity || + g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution || + g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp || + g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) { + stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD; + g_data->studiolight_index = sl->index; + g_data->studiolight_rot_z = shading->studiolight_rot_z; + g_data->studiolight_intensity = shading->studiolight_intensity; + g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution; + g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp; + g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality; } } } diff --git a/source/blender/draw/engines/eevee/eevee_lut_gen.c b/source/blender/draw/engines/eevee/eevee_lut_gen.c index 6cee05bf015..9b07a6908c3 100644 --- a/source/blender/draw/engines/eevee/eevee_lut_gen.c +++ b/source/blender/draw/engines/eevee/eevee_lut_gen.c @@ -31,6 +31,8 @@ #include "BLI_rand.h" #include "BLI_string_utils.h" +#include "eevee_private.h" + extern char datatoc_bsdf_lut_frag_glsl[]; extern char datatoc_btdf_lut_frag_glsl[]; extern char datatoc_bsdf_common_lib_glsl[]; @@ -45,15 +47,13 @@ static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h)) static float samples_len = 8192.0f; static float inv_samples_len = 1.0f / 8192.0f; - char *lib_str = BLI_string_joinN(datatoc_bsdf_common_lib_glsl, datatoc_bsdf_sampling_lib_glsl); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); - struct GPUShader *sh = DRW_shader_create_with_lib(datatoc_lightprobe_vert_glsl, - datatoc_lightprobe_geom_glsl, - datatoc_bsdf_lut_frag_glsl, - lib_str, - "#define HAMMERSLEY_SIZE 8192\n" - "#define BRDF_LUT_SIZE 64\n" - "#define NOISE_SIZE 64\n"); + struct GPUShader *sh = DRW_shader_create_with_shaderlib(datatoc_lightprobe_vert_glsl, + datatoc_lightprobe_geom_glsl, + datatoc_bsdf_lut_frag_glsl, + lib, + "#define HAMMERSLEY_SIZE 8192\n"); DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); @@ -105,16 +105,10 @@ static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h) static float a2 = 0.0f; static float inv_samples_len = 1.0f / 8192.0f; - char *frag_str = BLI_string_joinN( - datatoc_bsdf_common_lib_glsl, datatoc_bsdf_sampling_lib_glsl, datatoc_btdf_lut_frag_glsl); - - struct GPUShader *sh = DRW_shader_create_fullscreen(frag_str, - "#define HAMMERSLEY_SIZE 8192\n" - "#define BRDF_LUT_SIZE 64\n" - "#define NOISE_SIZE 64\n" - "#define LUT_SIZE 64\n"); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); - MEM_freeN(frag_str); + struct GPUShader *sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_btdf_lut_frag_glsl, lib, "#define HAMMERSLEY_SIZE 8192\n"); DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); @@ -194,4 +188,4 @@ static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h) MEM_freeN(data); return tex; -}
\ No newline at end of file +} diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 143945b637a..59e8c1407e6 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -56,37 +56,6 @@ static struct { float noise_offsets[3]; } e_data = {NULL}; /* Engine data */ -extern char datatoc_lights_lib_glsl[]; -extern char datatoc_lightprobe_lib_glsl[]; -extern char datatoc_ambient_occlusion_lib_glsl[]; -extern char datatoc_prepass_frag_glsl[]; -extern char datatoc_prepass_vert_glsl[]; -extern char datatoc_default_frag_glsl[]; -extern char datatoc_default_world_frag_glsl[]; -extern char datatoc_ltc_lib_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_irradiance_lib_glsl[]; -extern char datatoc_octahedron_lib_glsl[]; -extern char datatoc_cubemap_lib_glsl[]; -extern char datatoc_lit_surface_frag_glsl[]; -extern char datatoc_lit_surface_vert_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; -extern char datatoc_ssr_lib_glsl[]; -extern char datatoc_shadow_vert_glsl[]; -extern char datatoc_lightprobe_geom_glsl[]; -extern char datatoc_lightprobe_vert_glsl[]; -extern char datatoc_background_vert_glsl[]; -extern char datatoc_update_noise_frag_glsl[]; -extern char datatoc_volumetric_vert_glsl[]; -extern char datatoc_volumetric_geom_glsl[]; -extern char datatoc_volumetric_frag_glsl[]; -extern char datatoc_volumetric_lib_glsl[]; -extern char datatoc_gpu_shader_uniform_color_frag_glsl[]; - typedef struct EeveeMaterialCache { struct DRWShadingGroup *depth_grp; struct DRWShadingGroup *shading_grp; @@ -238,46 +207,6 @@ void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const d DRW_draw_pass(psl->update_noise_pass); } -void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float (*r_viewvecs)[4]) -{ - /* view vectors for the corners of the view frustum. - * Can be used to recreate the world space position easily */ - float view_vecs[4][4] = { - {-1.0f, -1.0f, -1.0f, 1.0f}, - {1.0f, -1.0f, -1.0f, 1.0f}, - {-1.0f, 1.0f, -1.0f, 1.0f}, - {-1.0f, -1.0f, 1.0f, 1.0f}, - }; - - /* convert the view vectors to view space */ - const bool is_persp = (winmat[3][3] == 0.0f); - for (int i = 0; i < 4; i++) { - mul_project_m4_v3(invproj, view_vecs[i]); - /* normalized trick see: - * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ - if (is_persp) { - /* Divide XY by Z. */ - mul_v2_fl(view_vecs[i], 1.0f / view_vecs[i][2]); - } - } - - /** - * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and - * view_vecs[1] is the vector going from the near-bottom-left corner to - * the far-top-right corner. - * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner - * when Z = 1, and top-left corner if Z = 1. - * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed) - * distance from the near plane to the far clip plane. - */ - copy_v4_v4(r_viewvecs[0], view_vecs[0]); - - /* we need to store the differences */ - r_viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0]; - r_viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1]; - r_viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2]; -} - void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, EEVEE_StorageList *stl, @@ -305,15 +234,6 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, } { - /* Update view_vecs */ - float invproj[4][4], winmat[4][4]; - DRW_view_winmat_get(NULL, winmat, false); - DRW_view_winmat_get(NULL, invproj, true); - - EEVEE_update_viewvecs(invproj, winmat, sldata->common_data.view_vecs); - } - - { /* Update noise Framebuffer. */ GPU_framebuffer_ensure_config( &fbl->update_noise_fb, @@ -391,39 +311,28 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { DRW_PASS_CREATE(psl->background_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); DRWShadingGroup *grp = NULL; + EEVEE_lookdev_cache_init(vedata, sldata, psl->background_ps, NULL, &grp); - Scene *scene = draw_ctx->scene; - World *wo = scene->world; - - EEVEE_lookdev_cache_init(vedata, sldata, &grp, psl->background_ps, wo, NULL); + if (grp == NULL) { + Scene *scene = draw_ctx->scene; + World *world = (scene->world) ? scene->world : EEVEE_world_default_get(); - if (!grp && wo) { - struct GPUMaterial *gpumat = EEVEE_material_get( - vedata, scene, NULL, wo, VAR_WORLD_BACKGROUND); + const int options = VAR_WORLD_BACKGROUND; + struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options); grp = DRW_shgroup_material_create(gpumat, psl->background_ps); DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); - /* TODO (fclem): remove those (need to clean the GLSL files). */ - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); - DRW_shgroup_call(grp, geom, NULL); } - /* Fallback if shader fails or if not using nodetree. */ - if (grp == NULL) { - GPUShader *sh = EEVEE_shaders_default_background_sh_get(); - grp = DRW_shgroup_create(sh, psl->background_ps); - DRW_shgroup_uniform_vec3(grp, "color", G_draw.block.colorBackground, 1); - DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); - DRW_shgroup_call(grp, geom, NULL); - } + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); + DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); } #define EEVEE_PASS_CREATE(pass, state) \ diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c index 1cedd334d67..d2f3a13eb7c 100644 --- a/source/blender/draw/engines/eevee/eevee_mist.c +++ b/source/blender/draw/engines/eevee/eevee_mist.c @@ -56,14 +56,10 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; if (e_data.mist_sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_effect_mist_frag_glsl); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); - e_data.mist_sh = DRW_shader_create_fullscreen(frag_str, "#define FIRST_PASS\n"); - - MEM_freeN(frag_str); + e_data.mist_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_mist_frag_glsl, lib, "#define FIRST_PASS\n"); } /* Create FrameBuffer. */ @@ -98,11 +94,11 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) } } else { - float near = -sldata->common_data.view_vecs[0][2]; - float range = sldata->common_data.view_vecs[1][2]; + float near = DRW_view_near_distance_get(NULL); + float far = DRW_view_far_distance_get(NULL); /* Fallback */ g_data->mist_start = near; - g_data->mist_inv_dist = 1.0f / fabsf(range); + g_data->mist_inv_dist = 1.0f / fabsf(far - near); g_data->mist_falloff = 1.0f; } diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index 400b309de07..0b79c8016d1 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -69,27 +69,23 @@ extern char datatoc_common_view_lib_glsl[]; static void eevee_create_shader_motion_blur(void) { - e_data.motion_blur_sh = DRW_shader_create_fullscreen( - datatoc_effect_motion_blur_frag_glsl, - "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n"); - e_data.motion_blur_object_sh = DRW_shader_create_with_lib(datatoc_object_motion_vert_glsl, - NULL, - datatoc_object_motion_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); - e_data.velocity_tiles_sh = DRW_shader_create_fullscreen( - datatoc_effect_velocity_tile_frag_glsl, - "#define TILE_GATHER\n" - "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n"); +#define TILE_SIZE_STR "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n" + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + e_data.motion_blur_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_motion_blur_frag_glsl, lib, TILE_SIZE_STR); + e_data.motion_blur_object_sh = DRW_shader_create_with_shaderlib( + datatoc_object_motion_vert_glsl, NULL, datatoc_object_motion_frag_glsl, lib, NULL); + + e_data.motion_blur_hair_sh = DRW_shader_create_with_shaderlib(datatoc_object_motion_vert_glsl, + NULL, + datatoc_object_motion_frag_glsl, + lib, + "#define HAIR\n"); + + e_data.velocity_tiles_sh = DRW_shader_create_fullscreen(datatoc_effect_velocity_tile_frag_glsl, + "#define TILE_GATHER\n" TILE_SIZE_STR); e_data.velocity_tiles_expand_sh = DRW_shader_create_fullscreen( - datatoc_effect_velocity_tile_frag_glsl, - "#define TILE_EXPANSION\n" - "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n"); - - char *vert = BLI_string_joinN(datatoc_common_hair_lib_glsl, datatoc_object_motion_vert_glsl); - e_data.motion_blur_hair_sh = DRW_shader_create_with_lib( - vert, NULL, datatoc_object_motion_frag_glsl, datatoc_common_view_lib_glsl, "#define HAIR\n"); - MEM_freeN(vert); + datatoc_effect_velocity_tile_frag_glsl, "#define TILE_EXPANSION\n" TILE_SIZE_STR); } int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c index a075210967c..1929bbb9b98 100644 --- a/source/blender/draw/engines/eevee/eevee_occlusion.c +++ b/source/blender/draw/engines/eevee/eevee_occlusion.c @@ -53,17 +53,14 @@ extern char datatoc_effect_gtao_frag_glsl[]; static void eevee_create_shader_occlusion(void) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_ambient_occlusion_lib_glsl, - datatoc_effect_gtao_frag_glsl); - - e_data.gtao_sh = DRW_shader_create_fullscreen(frag_str, NULL); - e_data.gtao_layer_sh = DRW_shader_create_fullscreen(frag_str, "#define LAYERED_DEPTH\n"); - e_data.gtao_debug_sh = DRW_shader_create_fullscreen(frag_str, "#define DEBUG_AO\n"); - - MEM_freeN(frag_str); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.gtao_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_gtao_frag_glsl, lib, NULL); + e_data.gtao_layer_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_gtao_frag_glsl, lib, "#define LAYERED_DEPTH\n"); + e_data.gtao_debug_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_gtao_frag_glsl, lib, "#define DEBUG_AO\n"); } int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 829d26ef876..34cd22ad13c 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -164,7 +164,7 @@ enum { VAR_MAT_MESH = (1 << 0), VAR_MAT_VOLUME = (1 << 1), VAR_MAT_HAIR = (1 << 2), - VAR_MAT_PROBE = (1 << 3), + /* VAR_MAT_PROBE = (1 << 3), UNUSED */ VAR_MAT_BLEND = (1 << 4), VAR_MAT_LOOKDEV = (1 << 5), VAR_MAT_HOLDOUT = (1 << 6), @@ -748,7 +748,6 @@ typedef struct EEVEE_EffectsInfo { * - sizeof(bool) == sizeof(int) in GLSL so use int in C */ typedef struct EEVEE_CommonUniformBuffer { float prev_persmat[4][4]; /* mat4 */ - float view_vecs[2][4]; /* vec4[2] */ float mip_ratio[10][4]; /* vec2[10] */ /* Ambient Occlusion */ /* -- 16 byte aligned -- */ @@ -1006,7 +1005,6 @@ void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_materials_free(void); void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]); -void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float (*r_viewvecs)[4]); void EEVEE_material_renderpasses_init(EEVEE_Data *vedata); void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); @@ -1062,15 +1060,14 @@ void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]); /* eevee_shaders.c */ void EEVEE_shaders_lightprobe_shaders_init(void); void EEVEE_shaders_material_shaders_init(void); +struct DRWShaderLibrary *EEVEE_shader_lib_get(void); struct GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_default_sh_get(void); struct GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void); struct GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void); struct GPUShader *EEVEE_shaders_probe_grid_fill_sh_get(void); struct GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void); -struct GPUShader *EEVEE_shaders_default_studiolight_sh_get(void); -struct GPUShader *EEVEE_shaders_default_background_sh_get(void); -struct GPUShader *EEVEE_shaders_background_studiolight_sh_get(void); +struct GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void); +struct GPUShader *EEVEE_shaders_studiolight_background_sh_get(void); struct GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void); struct GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void); struct GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void); @@ -1082,6 +1079,7 @@ struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo); Material *EEVEE_material_default_diffuse_get(void); Material *EEVEE_material_default_glossy_get(void); Material *EEVEE_material_default_error_get(void); +World *EEVEE_world_default_get(void); struct GPUMaterial *EEVEE_material_default_get(struct Scene *scene, Material *ma, int options); struct GPUMaterial *EEVEE_material_get( EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options); @@ -1308,10 +1306,9 @@ void EEVEE_render_update_passes(struct RenderEngine *engine, /** eevee_lookdev.c */ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, - DRWShadingGroup **grp, DRWPass *pass, - struct World *world, - EEVEE_LightProbesInfo *pinfo); + EEVEE_LightProbesInfo *pinfo, + DRWShadingGroup **r_shgrp); void EEVEE_lookdev_draw(EEVEE_Data *vedata); /** eevee_engine.c */ diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index be771d7cf42..089d8b7a287 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -199,12 +199,10 @@ void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ve EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0; if (needs_post_processing) { if (e_data.postprocess_sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_renderpass_postprocess_frag_glsl); - e_data.postprocess_sh = DRW_shader_create_fullscreen(frag_str, NULL); - MEM_freeN(frag_str); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.postprocess_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_renderpass_postprocess_frag_glsl, lib, NULL); } DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR); diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c index 32d758dba4b..a1755e60c06 100644 --- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c +++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c @@ -48,30 +48,12 @@ static struct { struct GPUTexture *depth_src; } e_data = {{NULL}}; /* Engine data */ -extern char datatoc_ambient_occlusion_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_octahedron_lib_glsl[]; -extern char datatoc_cubemap_lib_glsl[]; extern char datatoc_effect_ssr_frag_glsl[]; -extern char datatoc_lightprobe_lib_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; static struct GPUShader *eevee_effects_screen_raytrace_shader_get(int options) { if (e_data.ssr_sh[options] == NULL) { - char *ssr_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_ambient_occlusion_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_raytrace_lib_glsl, - datatoc_effect_ssr_frag_glsl); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); DynStr *ds_defines = BLI_dynstr_new(); BLI_dynstr_append(ds_defines, SHADER_DEFINES); @@ -91,9 +73,9 @@ static struct GPUShader *eevee_effects_screen_raytrace_shader_get(int options) char *ssr_define_str = BLI_dynstr_get_cstring(ds_defines); BLI_dynstr_free(ds_defines); - e_data.ssr_sh[options] = DRW_shader_create_fullscreen(ssr_shader_str, ssr_define_str); + e_data.ssr_sh[options] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_ssr_frag_glsl, lib, ssr_define_str); - MEM_freeN(ssr_shader_str); MEM_freeN(ssr_define_str); } diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 09e74c84948..5f125d395d3 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -28,6 +28,8 @@ #include "BLI_dynstr.h" #include "BLI_string_utils.h" +#include "DNA_world_types.h" + #include "MEM_guardedalloc.h" #include "GPU_material.h" @@ -40,19 +42,17 @@ static const char *filter_defines = "#define HAMMERSLEY_SIZE " STRINGIFY(HAMMERSLEY_SIZE) "\n" #if defined(IRRADIANCE_SH_L2) - "#define IRRADIANCE_SH_L2\n" -#elif defined(IRRADIANCE_CUBEMAP) - "#define IRRADIANCE_CUBEMAP\n" + "#define IRRADIANCE_SH_L2\n"; #elif defined(IRRADIANCE_HL2) - "#define IRRADIANCE_HL2\n" + "#define IRRADIANCE_HL2\n"; #endif - "#define NOISE_SIZE 64\n"; static struct { + /* Lookdev */ + struct GPUShader *studiolight_probe_sh; + struct GPUShader *studiolight_background_sh; + /* Probes */ - struct GPUShader *probe_default_sh; - struct GPUShader *probe_default_studiolight_sh; - struct GPUShader *probe_background_studiolight_sh; struct GPUShader *probe_grid_display_sh; struct GPUShader *probe_cube_display_sh; struct GPUShader *probe_planar_display_sh; @@ -70,17 +70,16 @@ static struct { struct GPUShader *taa_resolve_reproject_sh; /* General purpose Shaders. */ - struct GPUShader *default_background; + struct GPUShader *lookdev_background; struct GPUShader *update_noise_sh; /* Shader strings */ - char *frag_shader_lib; - char *vert_shader_str; - char *vert_shadow_shader_str; - char *vert_background_shader_str; - char *vert_volume_shader_str; - char *geom_volume_shader_str; - char *volume_shader_lib; + char *closure_lit_lib; + char *surface_lit_frag; + char *surface_prepass_frag; + char *surface_geom_barycentric; + + DRWShaderLibrary *lib; /* LookDev Materials */ Material *glossy_mat; @@ -88,6 +87,8 @@ static struct { Material *error_mat; + World *default_world; + /* Default Material */ struct { bNodeTree *ntree; @@ -103,16 +104,39 @@ static struct { } world; } e_data = {NULL}; /* Engine data */ -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; +extern char datatoc_common_hair_lib_glsl[]; +extern char datatoc_common_math_lib_glsl[]; +extern char datatoc_common_math_geom_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; +extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; extern char datatoc_ambient_occlusion_lib_glsl[]; extern char datatoc_background_vert_glsl[]; -extern char datatoc_common_hair_lib_glsl[]; +extern char datatoc_bsdf_common_lib_glsl[]; +extern char datatoc_bsdf_lut_frag_glsl[]; +extern char datatoc_bsdf_sampling_lib_glsl[]; +extern char datatoc_btdf_lut_frag_glsl[]; +extern char datatoc_closure_lib_glsl[]; +extern char datatoc_common_uniforms_lib_glsl[]; +extern char datatoc_common_utiltex_lib_glsl[]; extern char datatoc_cubemap_lib_glsl[]; -extern char datatoc_default_world_frag_glsl[]; +extern char datatoc_default_frag_glsl[]; +extern char datatoc_lookdev_world_frag_glsl[]; +extern char datatoc_effect_bloom_frag_glsl[]; +extern char datatoc_effect_dof_frag_glsl[]; +extern char datatoc_effect_dof_vert_glsl[]; +extern char datatoc_effect_downsample_cube_frag_glsl[]; +extern char datatoc_effect_downsample_frag_glsl[]; +extern char datatoc_effect_gtao_frag_glsl[]; +extern char datatoc_effect_minmaxz_frag_glsl[]; +extern char datatoc_effect_mist_frag_glsl[]; +extern char datatoc_effect_motion_blur_frag_glsl[]; +extern char datatoc_effect_ssr_frag_glsl[]; +extern char datatoc_effect_subsurface_frag_glsl[]; +extern char datatoc_effect_temporal_aa_glsl[]; +extern char datatoc_effect_translucency_frag_glsl[]; +extern char datatoc_effect_velocity_resolve_frag_glsl[]; +extern char datatoc_effect_velocity_tile_frag_glsl[]; extern char datatoc_irradiance_lib_glsl[]; extern char datatoc_lightprobe_cube_display_frag_glsl[]; extern char datatoc_lightprobe_cube_display_vert_glsl[]; @@ -131,72 +155,111 @@ extern char datatoc_lightprobe_planar_downsample_geom_glsl[]; extern char datatoc_lightprobe_planar_downsample_vert_glsl[]; extern char datatoc_lightprobe_vert_glsl[]; extern char datatoc_lights_lib_glsl[]; -extern char datatoc_lit_surface_frag_glsl[]; -extern char datatoc_lit_surface_vert_glsl[]; +extern char datatoc_closure_lit_lib_glsl[]; extern char datatoc_ltc_lib_glsl[]; +extern char datatoc_object_motion_frag_glsl[]; +extern char datatoc_object_motion_vert_glsl[]; extern char datatoc_octahedron_lib_glsl[]; extern char datatoc_prepass_frag_glsl[]; +extern char datatoc_prepass_vert_glsl[]; extern char datatoc_raytrace_lib_glsl[]; +extern char datatoc_renderpass_lib_glsl[]; +extern char datatoc_renderpass_postprocess_frag_glsl[]; +extern char datatoc_shadow_accum_frag_glsl[]; +extern char datatoc_shadow_frag_glsl[]; extern char datatoc_shadow_vert_glsl[]; extern char datatoc_ssr_lib_glsl[]; +extern char datatoc_surface_frag_glsl[]; +extern char datatoc_surface_geom_glsl[]; +extern char datatoc_surface_lib_glsl[]; +extern char datatoc_surface_vert_glsl[]; extern char datatoc_update_noise_frag_glsl[]; +extern char datatoc_volumetric_accum_frag_glsl[]; extern char datatoc_volumetric_frag_glsl[]; extern char datatoc_volumetric_geom_glsl[]; +extern char datatoc_volumetric_integration_frag_glsl[]; extern char datatoc_volumetric_lib_glsl[]; +extern char datatoc_volumetric_resolve_frag_glsl[]; +extern char datatoc_volumetric_scatter_frag_glsl[]; extern char datatoc_volumetric_vert_glsl[]; -/* Velocity Resolve */ -extern char datatoc_effect_velocity_resolve_frag_glsl[]; - -/* Temporal Sampling */ -extern char datatoc_effect_temporal_aa_glsl[]; - /* *********** FUNCTIONS *********** */ +static void eevee_shader_library_ensure(void) +{ + if (e_data.lib == NULL) { + e_data.lib = DRW_shader_library_create(); + /* NOTE: Theses needs to be ordered by dependencies. */ + DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib); + DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib); + DRW_SHADER_LIB_ADD(e_data.lib, renderpass_lib); + DRW_SHADER_LIB_ADD(e_data.lib, bsdf_common_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_utiltex_lib); + DRW_SHADER_LIB_ADD(e_data.lib, bsdf_sampling_lib); + DRW_SHADER_LIB_ADD(e_data.lib, cubemap_lib); + DRW_SHADER_LIB_ADD(e_data.lib, raytrace_lib); + DRW_SHADER_LIB_ADD(e_data.lib, ambient_occlusion_lib); + DRW_SHADER_LIB_ADD(e_data.lib, octahedron_lib); + DRW_SHADER_LIB_ADD(e_data.lib, irradiance_lib); + DRW_SHADER_LIB_ADD(e_data.lib, lightprobe_lib); + DRW_SHADER_LIB_ADD(e_data.lib, ltc_lib); + DRW_SHADER_LIB_ADD(e_data.lib, lights_lib); + DRW_SHADER_LIB_ADD(e_data.lib, surface_lib); + DRW_SHADER_LIB_ADD(e_data.lib, volumetric_lib); + DRW_SHADER_LIB_ADD(e_data.lib, closure_lib); + DRW_SHADER_LIB_ADD(e_data.lib, ssr_lib); + + /* Add one for each Closure */ + e_data.closure_lit_lib = BLI_string_joinN(datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl); + + DRW_shader_library_add_file(e_data.lib, e_data.closure_lit_lib, "closure_lit_lib.glsl"); + + e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib, + datatoc_surface_frag_glsl); + + e_data.surface_prepass_frag = DRW_shader_library_create_shader_string( + e_data.lib, datatoc_prepass_frag_glsl); + + e_data.surface_geom_barycentric = DRW_shader_library_create_shader_string( + e_data.lib, datatoc_surface_geom_glsl); + } +} + void EEVEE_shaders_lightprobe_shaders_init(void) { BLI_assert(e_data.probe_filter_glossy_sh == NULL); - char *shader_str = NULL; - - shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_lightprobe_filter_glossy_frag_glsl); - - e_data.probe_filter_glossy_sh = DRW_shader_create( - datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, shader_str, filter_defines); - - e_data.probe_default_sh = DRW_shader_create_with_lib(datatoc_background_vert_glsl, - NULL, - datatoc_default_world_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); - MEM_freeN(shader_str); + eevee_shader_library_ensure(); - shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_lightprobe_filter_diffuse_frag_glsl); + e_data.probe_filter_glossy_sh = DRW_shader_create_with_shaderlib( + datatoc_lightprobe_vert_glsl, + datatoc_lightprobe_geom_glsl, + datatoc_lightprobe_filter_glossy_frag_glsl, + e_data.lib, + filter_defines); - e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen(shader_str, filter_defines); + e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_lightprobe_filter_diffuse_frag_glsl, e_data.lib, filter_defines); - MEM_freeN(shader_str); + e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_lightprobe_filter_visibility_frag_glsl, e_data.lib, filter_defines); - shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_lightprobe_filter_visibility_frag_glsl); - - e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen(shader_str, filter_defines); - - MEM_freeN(shader_str); - - e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen(datatoc_lightprobe_grid_fill_frag_glsl, - filter_defines); + e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_lightprobe_grid_fill_frag_glsl, e_data.lib, filter_defines); e_data.probe_planar_downsample_sh = DRW_shader_create( datatoc_lightprobe_planar_downsample_vert_glsl, @@ -207,70 +270,18 @@ void EEVEE_shaders_lightprobe_shaders_init(void) void EEVEE_shaders_material_shaders_init(void) { - e_data.frag_shader_lib = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_ambient_occlusion_lib_glsl, - datatoc_raytrace_lib_glsl, - datatoc_ssr_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_irradiance_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_ltc_lib_glsl, - datatoc_lights_lib_glsl, - /* Add one for each Closure */ - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_volumetric_lib_glsl); - - e_data.volume_shader_lib = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_ambient_occlusion_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_irradiance_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_ltc_lib_glsl, - datatoc_lights_lib_glsl, - datatoc_volumetric_lib_glsl, - datatoc_volumetric_frag_glsl); - - e_data.vert_shader_str = BLI_string_joinN( - datatoc_common_view_lib_glsl, datatoc_common_hair_lib_glsl, datatoc_lit_surface_vert_glsl); - - e_data.vert_shadow_shader_str = BLI_string_joinN( - datatoc_common_view_lib_glsl, datatoc_common_hair_lib_glsl, datatoc_shadow_vert_glsl); - - e_data.vert_background_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_background_vert_glsl); - - e_data.vert_volume_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_volumetric_vert_glsl); - - e_data.geom_volume_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_volumetric_geom_glsl); + eevee_shader_library_ensure(); } -GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void) +DRWShaderLibrary *EEVEE_shader_lib_get(void) { - return e_data.probe_filter_glossy_sh; + eevee_shader_library_ensure(); + return e_data.lib; } -GPUShader *EEVEE_shaders_probe_default_sh_get(void) +GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void) { - return e_data.probe_default_sh; + return e_data.probe_filter_glossy_sh; } GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void) @@ -293,59 +304,40 @@ GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void) return e_data.probe_planar_downsample_sh; } -GPUShader *EEVEE_shaders_default_studiolight_sh_get(void) +GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void) { - if (e_data.probe_default_studiolight_sh == NULL) { - e_data.probe_default_studiolight_sh = DRW_shader_create_with_lib( - datatoc_background_vert_glsl, - NULL, - datatoc_default_world_frag_glsl, - datatoc_common_view_lib_glsl, - "#define LOOKDEV\n"); - } - return e_data.probe_default_studiolight_sh; + if (e_data.studiolight_probe_sh == NULL) { + e_data.studiolight_probe_sh = DRW_shader_create_with_shaderlib(datatoc_background_vert_glsl, + NULL, + datatoc_lookdev_world_frag_glsl, + e_data.lib, + SHADER_DEFINES); + } + return e_data.studiolight_probe_sh; } -GPUShader *EEVEE_shaders_background_studiolight_sh_get(void) +GPUShader *EEVEE_shaders_studiolight_background_sh_get(void) { - if (e_data.probe_background_studiolight_sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_default_world_frag_glsl); - - e_data.probe_background_studiolight_sh = DRW_shader_create_with_lib( + if (e_data.studiolight_background_sh == NULL) { + e_data.studiolight_background_sh = DRW_shader_create_with_shaderlib( datatoc_background_vert_glsl, NULL, - frag_str, - datatoc_common_view_lib_glsl, + datatoc_lookdev_world_frag_glsl, + e_data.lib, "#define LOOKDEV_BG\n" SHADER_DEFINES); - - MEM_freeN(frag_str); } - return e_data.probe_background_studiolight_sh; + return e_data.studiolight_background_sh; } GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void) { if (e_data.probe_cube_display_sh == NULL) { - char *shader_str = BLI_string_joinN(datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_lightprobe_cube_display_frag_glsl); - - char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_lightprobe_cube_display_vert_glsl); - - e_data.probe_cube_display_sh = DRW_shader_create(vert_str, NULL, shader_str, SHADER_DEFINES); - - MEM_freeN(vert_str); - MEM_freeN(shader_str); + e_data.probe_cube_display_sh = DRW_shader_create_with_shaderlib( + datatoc_lightprobe_cube_display_vert_glsl, + NULL, + datatoc_lightprobe_cube_display_frag_glsl, + e_data.lib, + SHADER_DEFINES); } return e_data.probe_cube_display_sh; } @@ -353,22 +345,12 @@ GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void) GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void) { if (e_data.probe_grid_display_sh == NULL) { - char *shader_str = BLI_string_joinN(datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_irradiance_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_lightprobe_grid_display_frag_glsl); - - char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_lightprobe_grid_display_vert_glsl); - - e_data.probe_grid_display_sh = DRW_shader_create(vert_str, NULL, shader_str, filter_defines); - - MEM_freeN(vert_str); - MEM_freeN(shader_str); + e_data.probe_grid_display_sh = DRW_shader_create_with_shaderlib( + datatoc_lightprobe_grid_display_vert_glsl, + NULL, + datatoc_lightprobe_grid_display_frag_glsl, + e_data.lib, + filter_defines); } return e_data.probe_grid_display_sh; } @@ -376,16 +358,12 @@ GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void) GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void) { if (e_data.probe_planar_display_sh == NULL) { - char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_lightprobe_planar_display_vert_glsl); - - char *shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_lightprobe_planar_display_frag_glsl); - - e_data.probe_planar_display_sh = DRW_shader_create(vert_str, NULL, shader_str, NULL); - - MEM_freeN(vert_str); - MEM_freeN(shader_str); + e_data.probe_planar_display_sh = DRW_shader_create_with_shaderlib( + datatoc_lightprobe_planar_display_vert_glsl, + NULL, + datatoc_lightprobe_planar_display_frag_glsl, + e_data.lib, + NULL); } return e_data.probe_planar_display_sh; } @@ -393,34 +371,17 @@ GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void) GPUShader *EEVEE_shaders_velocity_resolve_sh_get(void) { if (e_data.velocity_resolve_sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_common_uniforms_lib_glsl, - datatoc_common_view_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_effect_velocity_resolve_frag_glsl); - - e_data.velocity_resolve_sh = DRW_shader_create_fullscreen(frag_str, NULL); - - MEM_freeN(frag_str); + e_data.velocity_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_velocity_resolve_frag_glsl, e_data.lib, NULL); } return e_data.velocity_resolve_sh; } -GPUShader *EEVEE_shaders_default_background_sh_get(void) -{ - if (e_data.default_background == NULL) { - e_data.default_background = DRW_shader_create_with_lib(datatoc_background_vert_glsl, - NULL, - datatoc_default_world_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); - } - return e_data.default_background; -} - GPUShader *EEVEE_shaders_update_noise_sh_get(void) { if (e_data.update_noise_sh == NULL) { - e_data.update_noise_sh = DRW_shader_create_fullscreen(datatoc_update_noise_frag_glsl, NULL); + e_data.update_noise_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_update_noise_frag_glsl, e_data.lib, NULL); } return e_data.update_noise_sh; } @@ -437,13 +398,8 @@ GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects) sh = &e_data.taa_resolve_sh; } if (*sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_common_uniforms_lib_glsl, - datatoc_common_view_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_effect_temporal_aa_glsl); - - *sh = DRW_shader_create_fullscreen(frag_str, define); - MEM_freeN(frag_str); + *sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_temporal_aa_glsl, e_data.lib, define); } return *sh; @@ -583,6 +539,18 @@ struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo) return e_data.world.ntree; } +World *EEVEE_world_default_get(void) +{ + if (e_data.default_world == NULL) { + e_data.default_world = BKE_id_new_nomain(ID_WO, "EEVEEE default world"); + copy_v3_fl(&e_data.default_world->horr, 0.0f); + e_data.default_world->use_nodes = 0; + e_data.default_world->nodetree = NULL; + BLI_listbase_clear(&e_data.default_world->gpumaterial); + } + return e_data.default_world; +} + static char *eevee_get_defines(int options) { char *str = NULL; @@ -605,7 +573,7 @@ static char *eevee_get_defines(int options) if ((options & VAR_MAT_HAIR) != 0) { BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); } - if ((options & (VAR_MAT_PROBE | VAR_WORLD_PROBE)) != 0) { + if ((options & VAR_WORLD_PROBE) != 0) { BLI_dynstr_append(ds, "#define PROBE_CAPTURE\n"); } if ((options & VAR_MAT_HASH) != 0) { @@ -635,13 +603,13 @@ static char *eevee_get_vert(int options) char *str = NULL; if ((options & VAR_MAT_VOLUME) != 0) { - str = BLI_strdup(e_data.vert_volume_shader_str); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_vert_glsl); } else if ((options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0) { - str = BLI_strdup(e_data.vert_background_shader_str); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_background_vert_glsl); } else { - str = BLI_strdup(e_data.vert_shader_str); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_vert_glsl); } return str; @@ -652,7 +620,7 @@ static char *eevee_get_geom(int options) char *str = NULL; if ((options & VAR_MAT_VOLUME) != 0) { - str = BLI_strdup(e_data.geom_volume_shader_str); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_geom_glsl); } return str; @@ -663,18 +631,36 @@ static char *eevee_get_frag(int options) char *str = NULL; if ((options & VAR_MAT_VOLUME) != 0) { - str = BLI_strdup(e_data.volume_shader_lib); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_frag_glsl); } else if ((options & VAR_MAT_DEPTH) != 0) { - str = BLI_string_joinN(e_data.frag_shader_lib, datatoc_prepass_frag_glsl); + str = BLI_strdup(e_data.surface_prepass_frag); } else { - str = BLI_strdup(e_data.frag_shader_lib); + str = BLI_strdup(e_data.surface_lit_frag); } return str; } +static void eevee_material_post_eval(GPUMaterial *mat, + int options, + const char **UNUSED(vert_code), + const char **geom_code, + const char **UNUSED(frag_lib), + const char **UNUSED(defines)) +{ + const bool is_hair = (options & VAR_MAT_HAIR) != 0; + const bool is_mesh = (options & VAR_MAT_MESH) != 0; + + /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used. + * Note: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */ + if (!is_hair && is_mesh && GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) && + *geom_code == NULL) { + *geom_code = e_data.surface_geom_barycentric; + } +} + static struct GPUMaterial *eevee_material_get_ex( struct Scene *scene, Material *ma, World *wo, int options, bool deferred) { @@ -702,14 +688,16 @@ static struct GPUMaterial *eevee_material_get_ex( char *frag = eevee_get_frag(options); if (ma) { + GPUMaterialEvalCallbackFn cbfn = &eevee_material_post_eval; + bNodeTree *ntree = !is_default ? ma->nodetree : EEVEE_shader_default_surface_nodetree(ma); mat = DRW_shader_create_from_material( - scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred); + scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, cbfn); } else { bNodeTree *ntree = !is_default ? wo->nodetree : EEVEE_shader_default_world_nodetree(wo); mat = DRW_shader_create_from_world( - scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred); + scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, NULL); } MEM_SAFE_FREE(defines); @@ -764,30 +752,31 @@ struct GPUMaterial *EEVEE_material_get( void EEVEE_shaders_free(void) { - MEM_SAFE_FREE(e_data.frag_shader_lib); - MEM_SAFE_FREE(e_data.vert_shader_str); - MEM_SAFE_FREE(e_data.vert_shadow_shader_str); - MEM_SAFE_FREE(e_data.vert_background_shader_str); - MEM_SAFE_FREE(e_data.vert_volume_shader_str); - MEM_SAFE_FREE(e_data.geom_volume_shader_str); - MEM_SAFE_FREE(e_data.volume_shader_lib); - DRW_SHADER_FREE_SAFE(e_data.default_background); + MEM_SAFE_FREE(e_data.closure_lit_lib); + MEM_SAFE_FREE(e_data.surface_prepass_frag); + MEM_SAFE_FREE(e_data.surface_lit_frag); + MEM_SAFE_FREE(e_data.surface_geom_barycentric); + DRW_SHADER_FREE_SAFE(e_data.lookdev_background); DRW_SHADER_FREE_SAFE(e_data.update_noise_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_default_sh); DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh); DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh); DRW_SHADER_FREE_SAFE(e_data.probe_filter_visibility_sh); DRW_SHADER_FREE_SAFE(e_data.probe_grid_fill_sh); DRW_SHADER_FREE_SAFE(e_data.probe_planar_downsample_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_default_studiolight_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_background_studiolight_sh); + DRW_SHADER_FREE_SAFE(e_data.studiolight_probe_sh); + DRW_SHADER_FREE_SAFE(e_data.studiolight_background_sh); DRW_SHADER_FREE_SAFE(e_data.probe_grid_display_sh); DRW_SHADER_FREE_SAFE(e_data.probe_cube_display_sh); DRW_SHADER_FREE_SAFE(e_data.probe_planar_display_sh); DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh); DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh); DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh); + DRW_SHADER_LIB_FREE_SAFE(e_data.lib); + if (e_data.default_world) { + BKE_id_free(NULL, e_data.default_world); + e_data.default_world = NULL; + } if (e_data.glossy_mat) { BKE_id_free(NULL, e_data.glossy_mat); e_data.glossy_mat = NULL; diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c index 8c50b26b45f..0da356b75ac 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows.c +++ b/source/blender/draw/engines/eevee/eevee_shadows.c @@ -42,11 +42,6 @@ static struct { extern char datatoc_shadow_vert_glsl[]; extern char datatoc_shadow_frag_glsl[]; extern char datatoc_shadow_accum_frag_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_lights_lib_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; void eevee_contact_shadow_setup(const Light *la, EEVEE_Shadow *evsh) { @@ -65,23 +60,13 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata) const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); if (!e_data.shadow_sh) { - e_data.shadow_sh = DRW_shader_create_with_lib(datatoc_shadow_vert_glsl, - NULL, - datatoc_shadow_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); - } + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); - if (!e_data.shadow_accum_sh) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_raytrace_lib_glsl, - datatoc_lights_lib_glsl, - datatoc_shadow_accum_frag_glsl); + e_data.shadow_sh = DRW_shader_create_with_shaderlib( + datatoc_shadow_vert_glsl, NULL, datatoc_shadow_frag_glsl, lib, NULL); - e_data.shadow_accum_sh = DRW_shader_create_fullscreen(frag_str, SHADER_DEFINES); - MEM_freeN(frag_str); + e_data.shadow_accum_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_shadow_accum_frag_glsl, lib, SHADER_DEFINES); } if (!sldata->lights) { diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c index ef4588f4aca..637c5201afc 100644 --- a/source/blender/draw/engines/eevee/eevee_subsurface.c +++ b/source/blender/draw/engines/eevee/eevee_subsurface.c @@ -38,41 +38,19 @@ static struct { struct GPUShader *sss_sh[3]; } e_data = {{NULL}}; /* Engine data */ -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_lights_lib_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; -extern char datatoc_octahedron_lib_glsl[]; -extern char datatoc_cubemap_lib_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; extern char datatoc_effect_subsurface_frag_glsl[]; extern char datatoc_effect_translucency_frag_glsl[]; static void eevee_create_shader_subsurface(void) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_effect_subsurface_frag_glsl); - - /* TODO(fclem) remove some of these dependencies. */ - char *frag_translucent_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_raytrace_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_lights_lib_glsl, - datatoc_effect_translucency_frag_glsl); - - e_data.sss_sh[0] = DRW_shader_create_fullscreen(frag_str, "#define FIRST_PASS\n"); - e_data.sss_sh[1] = DRW_shader_create_fullscreen(frag_str, "#define SECOND_PASS\n"); - e_data.sss_sh[2] = DRW_shader_create_fullscreen(frag_translucent_str, - "#define EEVEE_TRANSLUCENCY\n" SHADER_DEFINES); - - MEM_freeN(frag_translucent_str); - MEM_freeN(frag_str); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.sss_sh[0] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_subsurface_frag_glsl, lib, "#define FIRST_PASS\n"); + e_data.sss_sh[1] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_subsurface_frag_glsl, lib, "#define SECOND_PASS\n"); + e_data.sss_sh[2] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_translucency_frag_glsl, lib, "#define EEVEE_TRANSLUCENCY\n" SHADER_DEFINES); } void EEVEE_subsurface_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *UNUSED(vedata)) diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 300022e97a9..57d5e54290e 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -44,16 +44,12 @@ #include "DEG_depsgraph_query.h" -#include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_material.h" #include "GPU_texture.h" #include "eevee_private.h" static struct { - char *volumetric_common_lib; - char *volumetric_common_lights_lib; - struct GPUShader *volumetric_clear_sh; struct GPUShader *scatter_sh; struct GPUShader *scatter_with_lights_sh; @@ -97,57 +93,48 @@ extern char datatoc_common_fullscreen_vert_glsl[]; static void eevee_create_shader_volumes(void) { - e_data.volumetric_common_lib = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_volumetric_lib_glsl); - - e_data.volumetric_common_lights_lib = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_irradiance_lib_glsl, - datatoc_lights_lib_glsl, - datatoc_volumetric_lib_glsl); - - e_data.volumetric_clear_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_frag_glsl, - e_data.volumetric_common_lib, - "#define VOLUMETRICS\n" - "#define CLEAR\n"); - e_data.scatter_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_scatter_frag_glsl, - e_data.volumetric_common_lights_lib, - SHADER_DEFINES - "#define VOLUMETRICS\n" - "#define VOLUME_SHADOW\n"); - e_data.scatter_with_lights_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_scatter_frag_glsl, - e_data.volumetric_common_lights_lib, - SHADER_DEFINES - "#define VOLUMETRICS\n" - "#define VOLUME_LIGHTING\n" - "#define VOLUME_SHADOW\n"); - e_data.volumetric_integration_sh = DRW_shader_create_with_lib( + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.volumetric_clear_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl, + datatoc_volumetric_geom_glsl, + datatoc_volumetric_frag_glsl, + lib, + SHADER_DEFINES + "#define VOLUMETRICS\n" + "#define CLEAR\n"); + + e_data.scatter_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl, + datatoc_volumetric_geom_glsl, + datatoc_volumetric_scatter_frag_glsl, + lib, + SHADER_DEFINES + "#define VOLUMETRICS\n" + "#define VOLUME_SHADOW\n"); + + e_data.scatter_with_lights_sh = DRW_shader_create_with_shaderlib( + datatoc_volumetric_vert_glsl, + datatoc_volumetric_geom_glsl, + datatoc_volumetric_scatter_frag_glsl, + lib, + SHADER_DEFINES + "#define VOLUMETRICS\n" + "#define VOLUME_LIGHTING\n" + "#define VOLUME_SHADOW\n"); + + e_data.volumetric_integration_sh = DRW_shader_create_with_shaderlib( datatoc_volumetric_vert_glsl, datatoc_volumetric_geom_glsl, datatoc_volumetric_integration_frag_glsl, - e_data.volumetric_common_lib, + lib, USE_VOLUME_OPTI ? "#extension GL_ARB_shader_image_load_store: enable\n" "#extension GL_ARB_shading_language_420pack: enable\n" - "#define USE_VOLUME_OPTI\n" : - NULL); - e_data.volumetric_resolve_sh = DRW_shader_create_with_lib(datatoc_common_fullscreen_vert_glsl, - NULL, - datatoc_volumetric_resolve_frag_glsl, - e_data.volumetric_common_lib, - NULL); - e_data.volumetric_accum_sh = DRW_shader_create_fullscreen(datatoc_volumetric_accum_frag_glsl, - NULL); + "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : + SHADER_DEFINES); + + e_data.volumetric_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_volumetric_resolve_frag_glsl, lib, SHADER_DEFINES); + e_data.volumetric_accum_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_volumetric_accum_frag_glsl, lib, SHADER_DEFINES); const float density[4] = {1.0f, 1.0f, 1.0f, 1.0f}; e_data.dummy_density = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, density); @@ -259,17 +246,11 @@ void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) common_data->vol_shadow_steps = 0; } - /* Update view_vecs */ - float invproj[4][4], winmat[4][4]; - DRW_view_winmat_get(NULL, winmat, false); - DRW_view_winmat_get(NULL, invproj, true); - EEVEE_update_viewvecs(invproj, winmat, sldata->common_data.view_vecs); - if (DRW_view_is_persp_get(NULL)) { float sample_distribution = scene_eval->eevee.volumetric_sample_distribution; sample_distribution = 4.0f * (max_ff(1.0f - sample_distribution, 1e-2f)); - const float clip_start = common_data->view_vecs[0][2]; + const float clip_start = DRW_view_near_distance_get(NULL); /* Negate */ float near = integration_start = min_ff(-integration_start, clip_start - 1e-4f); float far = integration_end = min_ff(-integration_end, near - 1e-4f); @@ -280,8 +261,8 @@ void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) common_data->vol_depth_param[2] = sample_distribution; } else { - const float clip_start = common_data->view_vecs[0][2]; - const float clip_end = clip_start + common_data->view_vecs[1][2]; + const float clip_start = DRW_view_near_distance_get(NULL); + const float clip_end = DRW_view_far_distance_get(NULL); integration_start = min_ff(integration_end, clip_start); integration_end = max_ff(-integration_end, clip_end); @@ -504,12 +485,7 @@ static bool eevee_volume_object_mesh_init(Scene *scene, #endif if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS) /* && show_smoke */) { - if (!(fds->flags & FLUID_DOMAIN_USE_NOISE)) { - GPU_create_smoke(fmd, 0); - } - else if (fds->flags & FLUID_DOMAIN_USE_NOISE) { - GPU_create_smoke(fmd, 1); - } + DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE); BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(fmd)); } @@ -841,16 +817,13 @@ void EEVEE_volumes_free_smoke_textures(void) /* Free Smoke Textures after rendering */ LISTBASE_FOREACH (LinkData *, link, &e_data.smoke_domains) { FluidModifierData *fmd = (FluidModifierData *)link->data; - GPU_free_smoke(fmd); + DRW_smoke_free(fmd); } BLI_freelistN(&e_data.smoke_domains); } void EEVEE_volumes_free(void) { - MEM_SAFE_FREE(e_data.volumetric_common_lib); - MEM_SAFE_FREE(e_data.volumetric_common_lights_lib); - DRW_TEXTURE_FREE_SAFE(e_data.dummy_scatter); DRW_TEXTURE_FREE_SAFE(e_data.dummy_transmit); diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl index 57b16418696..2f6f8327f58 100644 --- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(raytrace_lib.glsl) + /* Based on Practical Realtime Strategies for Accurate Indirect Occlusion * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pptx @@ -24,12 +27,6 @@ #define MAX_SEARCH_ITER 32 #define MAX_LOD 6.0 -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - uniform sampler2D horizonBuffer; /* aoSettings flags */ @@ -243,6 +240,11 @@ float gtao_multibounce(float visibility, vec3 albedo) return max(x, ((x * a + b) * x + c) * x); } +float specular_occlusion(float NV, float AO, float roughness) +{ + return saturate(pow(NV + AO, roughness) - 1.0 + AO); +} + /* Use the right occlusion */ float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out vec3 bent_normal) { diff --git a/source/blender/draw/engines/eevee/shaders/background_vert.glsl b/source/blender/draw/engines/eevee/shaders/background_vert.glsl index aff8e0857f6..ab5d9a7ebe4 100644 --- a/source/blender/draw/engines/eevee/shaders/background_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/background_vert.glsl @@ -1,17 +1,17 @@ -in vec2 pos; +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) -out vec3 viewPosition; +in vec2 pos; -#ifndef VOLUMETRICS -/* necessary for compilation*/ -out vec3 worldPosition; -out vec3 worldNormal; -out vec3 viewNormal; -#endif +RESOURCE_ID_VARYING void main() { + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + PASS_RESOURCE_ID + gl_Position = vec4(pos, 1.0, 1.0); viewPosition = vec3(pos, -1.0); @@ -22,6 +22,6 @@ void main() #endif #ifdef USE_ATTR - pass_attr(viewPosition); + pass_attr(viewPosition, NormalMatrix, ModelMatrixInverse); #endif } 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 a8b8566edec..deedde64194 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -1,487 +1,7 @@ -#define M_PI 3.14159265358979323846 /* pi */ -#define M_2PI 6.28318530717958647692 /* 2*pi */ -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#define M_1_PI 0.318309886183790671538 /* 1/pi */ -#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */ -#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */ -#define FLT_MAX 3.402823e+38 +#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#define LUT_SIZE 64 - -/* Buffers */ -uniform sampler2D colorBuffer; -uniform sampler2D depthBuffer; -uniform sampler2D maxzBuffer; -uniform sampler2D minzBuffer; -uniform sampler2DArray planarDepth; - -#define cameraForward ViewMatrixInverse[2].xyz -#define cameraPos ViewMatrixInverse[3].xyz -#define cameraVec \ - ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - worldPosition) : cameraForward) -#define viewCameraVec \ - ((ProjectionMatrix[3][3] == 0.0) ? normalize(-viewPosition) : vec3(0.0, 0.0, 1.0)) - -/* ------- Structures -------- */ - -/* ------ Lights ----- */ -struct LightData { - vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */ - vec4 color_spec; /* w : Spec Intensity */ - vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */ - vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */ - vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */ - vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */ -}; - -/* convenience aliases */ -#define l_color color_spec.rgb -#define l_spec color_spec.a -#define l_position position_influence.xyz -#define l_influence position_influence.w -#define l_sizex rightvec_sizex.w -#define l_sizey upvec_sizey.w -#define l_right rightvec_sizex.xyz -#define l_up upvec_sizey.xyz -#define l_forward forwardvec_type.xyz -#define l_type forwardvec_type.w -#define l_spot_size spotdata_radius_shadow.x -#define l_spot_blend spotdata_radius_shadow.y -#define l_radius spotdata_radius_shadow.z -#define l_shadowid spotdata_radius_shadow.w - -/* ------ Shadows ----- */ -#ifndef MAX_CASCADE_NUM -# define MAX_CASCADE_NUM 4 -#endif - -struct ShadowData { - vec4 near_far_bias_id; - vec4 contact_shadow_data; -}; - -struct ShadowCubeData { - mat4 shadowmat; - vec4 position; -}; - -struct ShadowCascadeData { - mat4 shadowmat[MAX_CASCADE_NUM]; - vec4 split_start_distances; - vec4 split_end_distances; - vec4 shadow_vec_id; -}; - -/* convenience aliases */ -#define sh_near near_far_bias_id.x -#define sh_far near_far_bias_id.y -#define sh_bias near_far_bias_id.z -#define sh_data_index near_far_bias_id.w -#define sh_contact_dist contact_shadow_data.x -#define sh_contact_offset contact_shadow_data.y -#define sh_contact_spread contact_shadow_data.z -#define sh_contact_thickness contact_shadow_data.w -#define sh_shadow_vec shadow_vec_id.xyz -#define sh_tex_index shadow_vec_id.w - -/* ------ Render Passes ----- */ -layout(std140) uniform renderpass_block -{ - bool renderPassDiffuse; - bool renderPassDiffuseLight; - bool renderPassGlossy; - bool renderPassGlossyLight; - bool renderPassEmit; - bool renderPassSSSColor; - bool renderPassEnvironment; -}; - -vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light) -{ - return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0); -} - -vec3 render_pass_sss_mask(vec3 sss_color) -{ - return renderPassSSSColor ? sss_color : vec3(0.0); -} - -vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light) -{ - return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0); -} - -vec3 render_pass_emission_mask(vec3 emission_light) -{ - return renderPassEmit ? emission_light : vec3(0.0); -} - -/* ------- Convenience functions --------- */ - -vec3 mul(mat3 m, vec3 v) -{ - return m * v; -} -mat3 mul(mat3 m1, mat3 m2) -{ - return m1 * m2; -} -vec3 transform_direction(mat4 m, vec3 v) -{ - return mat3(m) * v; -} -vec3 transform_point(mat4 m, vec3 v) -{ - return (m * vec4(v, 1.0)).xyz; -} -vec3 project_point(mat4 m, vec3 v) -{ - vec4 tmp = m * vec4(v, 1.0); - return tmp.xyz / tmp.w; -} - -#define min3(a, b, c) min(a, min(b, c)) -#define min4(a, b, c, d) min(a, min3(b, c, d)) -#define min5(a, b, c, d, e) min(a, min4(b, c, d, e)) -#define min6(a, b, c, d, e, f) min(a, min5(b, c, d, e, f)) -#define min7(a, b, c, d, e, f, g) min(a, min6(b, c, d, e, f, g)) -#define min8(a, b, c, d, e, f, g, h) min(a, min7(b, c, d, e, f, g, h)) -#define min9(a, b, c, d, e, f, g, h, i) min(a, min8(b, c, d, e, f, g, h, i)) - -#define max3(a, b, c) max(a, max(b, c)) -#define max4(a, b, c, d) max(a, max3(b, c, d)) -#define max5(a, b, c, d, e) max(a, max4(b, c, d, e)) -#define max6(a, b, c, d, e, f) max(a, max5(b, c, d, e, f)) -#define max7(a, b, c, d, e, f, g) max(a, max6(b, c, d, e, f, g)) -#define max8(a, b, c, d, e, f, g, h) max(a, max7(b, c, d, e, f, g, h)) -#define max9(a, b, c, d, e, f, g, h, i) max(a, max8(b, c, d, e, f, g, h, i)) - -#define avg3(a, b, c) (a + b + c) * (1.0 / 3.0) -#define avg4(a, b, c, d) (a + b + c + d) * (1.0 / 4.0) -#define avg5(a, b, c, d, e) (a + b + c + d + e) * (1.0 / 5.0) -#define avg6(a, b, c, d, e, f) (a + b + c + d + e + f) * (1.0 / 6.0) -#define avg7(a, b, c, d, e, f, g) (a + b + c + d + e + f + g) * (1.0 / 7.0) -#define avg8(a, b, c, d, e, f, g, h) (a + b + c + d + e + f + g + h) * (1.0 / 8.0) -#define avg9(a, b, c, d, e, f, g, h, i) (a + b + c + d + e + f + g + h + i) * (1.0 / 9.0) - -float min_v2(vec2 v) -{ - return min(v.x, v.y); -} -float min_v3(vec3 v) -{ - return min(v.x, min(v.y, v.z)); -} -float min_v4(vec4 v) -{ - return min(min(v.x, v.y), min(v.z, v.w)); -} -float max_v2(vec2 v) -{ - return max(v.x, v.y); -} -float max_v3(vec3 v) -{ - return max(v.x, max(v.y, v.z)); -} -float max_v4(vec4 v) -{ - return max(max(v.x, v.y), max(v.z, v.w)); -} - -float sum(vec2 v) -{ - return dot(vec2(1.0), v); -} -float sum(vec3 v) -{ - return dot(vec3(1.0), v); -} -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); -} -vec2 saturate(vec2 a) -{ - return clamp(a, 0.0, 1.0); -} -vec3 saturate(vec3 a) -{ - return clamp(a, 0.0, 1.0); -} -vec4 saturate(vec4 a) -{ - return clamp(a, 0.0, 1.0); -} - -float distance_squared(vec2 a, vec2 b) -{ - a -= b; - return dot(a, a); -} -float distance_squared(vec3 a, vec3 b) -{ - a -= b; - return dot(a, a); -} -float len_squared(vec3 a) -{ - return dot(a, a); -} - -float inverse_distance(vec3 V) -{ - return max(1 / length(V), 1e-8); -} - -vec2 mip_ratio_interp(float mip) -{ - float low_mip = floor(mip); - return mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip); -} - -/* ------- RNG ------- */ - -float wang_hash_noise(uint s) -{ - s = (s ^ 61u) ^ (s >> 16u); - s *= 9u; - s = s ^ (s >> 4u); - s *= 0x27d4eb2du; - s = s ^ (s >> 15u); - - return fract(float(s) / 4294967296.0); -} - -/* ------- Fast Math ------- */ - -/* [Drobot2014a] Low Level Optimizations for GCN */ -float fast_sqrt(float v) -{ - return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1)); -} - -vec2 fast_sqrt(vec2 v) -{ - return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1)); -} - -/* [Eberly2014] GPGPU Programming for Games and Science */ -float fast_acos(float v) -{ - float res = -0.156583 * abs(v) + M_PI_2; - res *= fast_sqrt(1.0 - abs(v)); - return (v >= 0) ? res : M_PI - res; -} - -vec2 fast_acos(vec2 v) -{ - vec2 res = -0.156583 * abs(v) + M_PI_2; - res *= fast_sqrt(1.0 - abs(v)); - v.x = (v.x >= 0) ? res.x : M_PI - res.x; - v.y = (v.y >= 0) ? res.y : M_PI - res.y; - return v; -} - -float point_plane_projection_dist(vec3 lineorigin, vec3 planeorigin, vec3 planenormal) -{ - return dot(planenormal, planeorigin - lineorigin); -} - -float line_plane_intersect_dist(vec3 lineorigin, - vec3 linedirection, - vec3 planeorigin, - vec3 planenormal) -{ - return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection); -} - -float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec4 plane) -{ - vec3 plane_co = plane.xyz * (-plane.w / len_squared(plane.xyz)); - vec3 h = lineorigin - plane_co; - return -dot(plane.xyz, h) / dot(plane.xyz, linedirection); -} - -vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal) -{ - float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal); - return lineorigin + linedirection * dist; -} - -vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec4 plane) -{ - float dist = line_plane_intersect_dist(lineorigin, linedirection, plane); - return lineorigin + linedirection * dist; -} - -float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin) -{ - /* aligned plane normal */ - vec3 L = planeorigin - lineorigin; - float diskdist = length(L); - vec3 planenormal = -normalize(L); - return -diskdist / dot(planenormal, linedirection); -} - -vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin) -{ - float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin); - if (dist < 0) { - /* if intersection is behind we fake the intersection to be - * really far and (hopefully) not inside the radius of interest */ - dist = 1e16; - } - return lineorigin + linedirection * dist; -} - -float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection) -{ - float a = dot(linedirection, linedirection); - float b = dot(linedirection, lineorigin); - float c = dot(lineorigin, lineorigin) - 1; - - float dist = 1e15; - float determinant = b * b - a * c; - if (determinant >= 0) { - dist = (sqrt(determinant) - b) / a; - } - - return dist; -} - -float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection) -{ - /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ - */ - vec3 firstplane = (vec3(1.0) - lineorigin) / linedirection; - vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection; - vec3 furthestplane = max(firstplane, secondplane); - - return min_v3(furthestplane); -} - -/* Return texture coordinates to sample Surface LUT */ -vec2 lut_coords(float cosTheta, float roughness) -{ - float theta = acos(cosTheta); - vec2 coords = vec2(roughness, theta / M_PI_2); - - /* scale and bias coordinates, for correct filtered lookup */ - return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; -} - -vec2 lut_coords_ltc(float cosTheta, float roughness) -{ - vec2 coords = vec2(roughness, sqrt(1.0 - cosTheta)); - - /* scale and bias coordinates, for correct filtered lookup */ - return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; -} - -/* -- Tangent Space conversion -- */ -vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B) -{ - return T * vector.x + B * vector.y + N * vector.z; -} - -vec3 world_to_tangent(vec3 vector, vec3 N, vec3 T, vec3 B) -{ - return vec3(dot(T, vector), dot(B, vector), dot(N, vector)); -} - -void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B) -{ - vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - T = normalize(cross(UpVector, N)); - B = cross(N, T); -} - -/* ---- Opengl Depth conversion ---- */ - -float linear_depth(bool is_persp, float z, float zf, float zn) -{ - if (is_persp) { - return (zn * zf) / (z * (zn - zf) + zf); - } - else { - return (z * 2.0 - 1.0) * zf; - } -} - -float buffer_depth(bool is_persp, float z, float zf, float zn) -{ - if (is_persp) { - return (zf * (zn - z)) / (z * (zn - zf)); - } - else { - return (z / (zf * 2.0)) + 0.5; - } -} - -float get_view_z_from_depth(float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - float d = 2.0 * depth - 1.0; - return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); - } - else { - return viewVecs[0].z + depth * viewVecs[1].z; - } -} - -float get_depth_from_view_z(float z) -{ - if (ProjectionMatrix[3][3] == 0.0) { - float d = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2]; - return d * 0.5 + 0.5; - } - else { - return (z - viewVecs[0].z) / viewVecs[1].z; - } -} - -vec2 get_uvs_from_view(vec3 view) -{ - vec3 ndc = project_point(ProjectionMatrix, view); - return ndc.xy * 0.5 + 0.5; -} - -vec3 get_view_space_from_depth(vec2 uvcoords, float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - return vec3(viewVecs[0].xy + uvcoords * viewVecs[1].xy, 1.0) * get_view_z_from_depth(depth); - } - else { - return viewVecs[0].xyz + vec3(uvcoords, depth) * viewVecs[1].xyz; - } -} - -vec3 get_world_space_from_depth(vec2 uvcoords, float depth) -{ - return (ViewMatrixInverse * vec4(get_view_space_from_depth(uvcoords, depth), 1.0)).xyz; -} - -vec3 get_specular_reflection_dominant_dir(vec3 N, vec3 V, float roughness) +vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness) { vec3 R = -reflect(V, N); float smoothness = 1.0 - roughness; @@ -489,13 +9,6 @@ vec3 get_specular_reflection_dominant_dir(vec3 N, vec3 V, float roughness) return normalize(mix(N, R, fac)); } -float specular_occlusion(float NV, float AO, float roughness) -{ - return saturate(pow(NV + AO, roughness) - 1.0 + AO); -} - -/* --- Refraction utils --- */ - float ior_from_f0(float f0) { float f = sqrt(f0); @@ -508,7 +21,7 @@ float f0_from_ior(float eta) return A * A; } -vec3 get_specular_refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior) +vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior) { /* TODO: This a bad approximation. Better approximation should fit * the refracted vector and roughness into the best prefiltered reflection @@ -527,128 +40,6 @@ vec3 get_specular_refraction_dominant_dir(vec3 N, vec3 V, float roughness, float return R; } -float get_btdf_lut(sampler2DArray btdf_lut_tex, float NV, float roughness, float ior) -{ - const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE; - - vec3 coords; - /* Try to compensate for the low resolution and interpolation error. */ - coords.x = (ior > 1.0) ? (0.9 + lut_scale_bias_texel_size.z) + - (0.1 - lut_scale_bias_texel_size.z) * f0_from_ior(ior) : - (0.9 + lut_scale_bias_texel_size.z) * ior * ior; - coords.y = 1.0 - saturate(NV); - coords.xy *= lut_scale_bias_texel_size.x; - coords.xy += lut_scale_bias_texel_size.y; - - const float lut_lvl_ofs = 4.0; /* First texture lvl of roughness. */ - const float lut_lvl_scale = 16.0; /* How many lvl of roughness in the lut. */ - - float mip = roughness * lut_lvl_scale; - float mip_floor = floor(mip); - - coords.z = lut_lvl_ofs + mip_floor + 1.0; - float btdf_high = textureLod(btdf_lut_tex, coords, 0.0).r; - - coords.z -= 1.0; - float btdf_low = textureLod(btdf_lut_tex, coords, 0.0).r; - - float btdf = (ior == 1.0) ? 1.0 : mix(btdf_low, btdf_high, mip - coords.z); - - return btdf; -} - -/* ---- Encode / Decode Normal buffer data ---- */ -/* From http://aras-p.info/texts/CompactNormalStorage.html - * Using Method #4: Spheremap Transform */ -vec2 normal_encode(vec3 n, vec3 view) -{ - float p = sqrt(n.z * 8.0 + 8.0); - return n.xy / p + 0.5; -} - -vec3 normal_decode(vec2 enc, vec3 view) -{ - vec2 fenc = enc * 4.0 - 2.0; - float f = dot(fenc, fenc); - float g = sqrt(1.0 - f / 4.0); - vec3 n; - n.xy = fenc * g; - n.z = 1 - f / 2; - return n; -} - -/* ---- RGBM (shared multiplier) encoding ---- */ -/* From http://iwasbeingirony.blogspot.fr/2010/06/difference-between-rgbm-and-rgbd.html */ - -/* Higher RGBM_MAX_RANGE gives imprecision issues in low intensity. */ -#define RGBM_MAX_RANGE 512.0 - -vec4 rgbm_encode(vec3 rgb) -{ - float maxRGB = max_v3(rgb); - float M = maxRGB / RGBM_MAX_RANGE; - M = ceil(M * 255.0) / 255.0; - return vec4(rgb / (M * RGBM_MAX_RANGE), M); -} - -vec3 rgbm_decode(vec4 data) -{ - return data.rgb * (data.a * RGBM_MAX_RANGE); -} - -/* ---- RGBE (shared exponent) encoding ---- */ -vec4 rgbe_encode(vec3 rgb) -{ - float maxRGB = max_v3(rgb); - float fexp = ceil(log2(maxRGB)); - return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0); -} - -vec3 rgbe_decode(vec4 data) -{ - float fexp = data.a * 255.0 - 128.0; - return data.rgb * exp2(fexp); -} - -#if 1 -# define irradiance_encode rgbe_encode -# define irradiance_decode rgbe_decode -#else /* No ecoding (when using floating point format) */ -# define irradiance_encode(X) (X).rgbb -# define irradiance_decode(X) (X).rgb -#endif - -/* Irradiance Visibility Encoding */ -#if 1 -vec4 visibility_encode(vec2 accum, float range) -{ - accum /= range; - - vec4 data; - data.x = fract(accum.x); - data.y = floor(accum.x) / 255.0; - data.z = fract(accum.y); - data.w = floor(accum.y) / 255.0; - - return data; -} - -vec2 visibility_decode(vec4 data, float range) -{ - return (data.xz + data.yw * 255.0) * range; -} -#else /* No ecoding (when using floating point format) */ -vec4 visibility_encode(vec2 accum, float range) -{ - return accum.xyxy; -} - -vec2 visibility_decode(vec4 data, float range) -{ - return data.xy; -} -#endif - /* Fresnel monochromatic, perfect mirror */ float F_eta(float eta, float cos_theta) { @@ -766,265 +157,3 @@ float cone_cosine(float r) /* Jimenez 2016 in Practical Realtime Strategies for Accurate Indirect Occlusion*/ return exp2(-3.32193 * r * r); } - -/* --------- Closure ---------- */ - -#ifdef VOLUMETRICS - -struct Closure { - vec3 absorption; - vec3 scatter; - vec3 emission; - 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) -{ - Closure cl; - cl.absorption = mix(cl1.absorption, cl2.absorption, fac); - cl.scatter = mix(cl1.scatter, cl2.scatter, fac); - cl.emission = mix(cl1.emission, cl2.emission, fac); - cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac); - return cl; -} - -Closure closure_add(Closure cl1, Closure cl2) -{ - Closure cl; - cl.absorption = cl1.absorption + cl2.absorption; - cl.scatter = cl1.scatter + cl2.scatter; - cl.emission = cl1.emission + cl2.emission; - cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */ - return cl; -} - -Closure closure_emission(vec3 rgb) -{ - Closure cl = CLOSURE_DEFAULT; - cl.emission = rgb; - return cl; -} - -#else /* VOLUMETRICS */ - -struct Closure { - vec3 radiance; - vec3 transmittance; - float holdout; -# ifdef USE_SSS - vec3 sss_irradiance; - vec3 sss_albedo; - float sss_radius; -# endif - vec4 ssr_data; - vec2 ssr_normal; - int flag; -}; - -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 -# define CLOSURE_DEFAULT \ - Closure(vec3(0.0), vec3(0.0), 0.0, vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec2(0.0), 0) -# else -# define CLOSURE_DEFAULT Closure(vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec2(0.0), 0) -# endif - -uniform int outputSsrId = 1; -uniform int outputSssId = 1; - -void closure_load_ssr_data( - vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout 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; - } -} - -void closure_load_sss_data( - float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl) -{ -# ifdef USE_SSS - if (sss_id == outputSssId) { - cl.sss_irradiance = sss_irradiance; - cl.sss_radius = radius; - cl.sss_albedo = sss_albedo; - cl.flag |= CLOSURE_SSS_FLAG; - cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0)); - } - else -# endif - { - cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo); - } -} - -Closure closure_mix(Closure cl1, Closure cl2, float fac) -{ - Closure cl; - cl.holdout = mix(cl1.holdout, cl2.holdout, fac); - - if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) { - fac = 1.0; - } - else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) { - fac = 0.0; - } - - 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 - cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac); - bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); - /* It also does not make sense to mix SSS radius or irradiance. */ - cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; - cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; -# endif - return cl; -} - -Closure closure_add(Closure cl1, Closure 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_albedo = cl1.sss_albedo + cl2.sss_albedo; - bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); - /* It also does not make sense to mix SSS radius or irradiance. */ - cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; - cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; -# endif - return cl; -} - -Closure closure_emission(vec3 rgb) -{ - Closure cl = CLOSURE_DEFAULT; - cl.radiance = rgb; - return cl; -} - -/* Breaking this across multiple lines causes issues for some older GLSL compilers. */ -/* clang-format off */ -# if defined(MESH_SHADER) && !defined(DEPTH_SHADER) -/* clang-format on */ -# 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 -layout(location = 3) out vec3 sssIrradiance; -layout(location = 4) out float sssRadius; -layout(location = 5) out vec3 sssAlbedo; -# 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 */ -void volumetric_resolve(vec2 frag_uvs, - float frag_depth, - out vec3 transmittance, - out vec3 scattering); -# endif - -# define NODETREE_EXEC -void main() -{ - Closure cl = nodetree_exec(); - - float holdout = saturate(1.0 - cl.holdout); - float transmit = saturate(avg(cl.transmittance)); - float alpha = 1.0 - transmit; - -# ifdef USE_ALPHA_BLEND - vec2 uvs = gl_FragCoord.xy * volCoordScale.zw; - vec3 vol_transmit, vol_scatter; - volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter); - - /* Removes part of the volume scattering that have - * already been added to the destination pixels. - * Since we do that using the blending pipeline we need to account for material transmittance. */ - vol_scatter -= vol_scatter * cl.transmittance; - - cl.radiance = cl.radiance * holdout * vol_transmit + vol_scatter; - outRadiance = vec4(cl.radiance, alpha * holdout); - outTransmittance = vec4(cl.transmittance, transmit) * holdout; -# else - outRadiance = vec4(cl.radiance, holdout); - ssrNormals = cl.ssr_normal; - ssrData = cl.ssr_data; -# ifdef USE_SSS - sssIrradiance = cl.sss_irradiance; - sssRadius = cl.sss_radius; - sssAlbedo = cl.sss_albedo; -# endif -# endif - - /* For Probe capture */ -# ifdef USE_SSS - float fac = float(!sssToggle); - - /* TODO(fclem) we shouldn't need this. - * Just disable USE_SSS when USE_REFRACTION is enabled. */ -# ifdef USE_REFRACTION - /* SSRefraction pass is done after the SSS pass. - * In order to not loose the diffuse light totally we - * need to merge the SSS radiance to the main radiance. */ - fac = 1.0; -# endif - - outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac; -# endif - -# ifdef LOOKDEV - gl_FragDepth = 0.0; -# endif - -# ifndef USE_ALPHA_BLEND - float alpha_div = 1.0 / max(1e-8, alpha); - outRadiance.rgb *= alpha_div; - ssrData.rgb *= alpha_div; -# ifdef USE_SSS - sssAlbedo.rgb *= alpha_div; -# endif -# endif -} - -# endif /* MESH_SHADER */ - -#endif /* VOLUMETRICS */ diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl index f05b3396428..2b2da884fde 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl @@ -1,3 +1,4 @@ +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) out vec4 FragColor; @@ -5,8 +6,8 @@ void main() { vec3 N, T, B, V; - float NV = (1.0 - (clamp(gl_FragCoord.y / BRDF_LUT_SIZE, 1e-4, 0.9999))); - float sqrtRoughness = clamp(gl_FragCoord.x / BRDF_LUT_SIZE, 1e-4, 0.9999); + float NV = (1.0 - (clamp(gl_FragCoord.y / b, 1e-4, 0.9999))); + float sqrtRoughness = clamp(gl_FragCoord.x / LUT_SIZE, 1e-4, 0.9999); float a = sqrtRoughness * sqrtRoughness; float a2 = a * a; diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl index 5f2b719095e..066ea58e2bf 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl @@ -1,6 +1,7 @@ +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + uniform sampler1D texHammersley; -uniform sampler2D texJitter; uniform float sampleCount; uniform float invSampleCount; @@ -8,8 +9,7 @@ vec2 jitternoise = vec2(0.0); #ifndef UTIL_TEX # define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) + #endif /* UTIL_TEX */ void setup_noise(void) @@ -17,6 +17,11 @@ void setup_noise(void) jitternoise = texelfetch_noise_tex(gl_FragCoord.xy).rg; /* Global variable */ } +vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B) +{ + return T * vector.x + B * vector.y + N * vector.z; +} + #ifdef HAMMERSLEY_SIZE vec3 hammersley_3d(float i, float invsamplenbr) { diff --git a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl index 1389a9763c0..d815d9d4e6b 100644 --- a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl @@ -1,3 +1,4 @@ +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) uniform float a2; @@ -7,8 +8,8 @@ void main() { vec3 N, T, B, V; - float x = gl_FragCoord.x / BRDF_LUT_SIZE; - float y = gl_FragCoord.y / BRDF_LUT_SIZE; + float x = gl_FragCoord.x / LUT_SIZE; + float y = gl_FragCoord.y / LUT_SIZE; /* There is little variation if ior > 1.0 so we * maximize LUT precision for ior < 1.0 */ x = x * 1.1; diff --git a/source/blender/draw/engines/eevee/shaders/closure_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_lib.glsl new file mode 100644 index 00000000000..e572245ace9 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/closure_lib.glsl @@ -0,0 +1,181 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(renderpass_lib.glsl) + +#ifndef VOLUMETRICS + +uniform int outputSsrId = 1; +uniform int outputSssId = 1; + +#endif + +struct Closure { +#ifdef VOLUMETRICS + vec3 absorption; + vec3 scatter; + vec3 emission; + float anisotropy; + +#else /* SURFACE */ + vec3 radiance; + vec3 transmittance; + float holdout; + vec4 ssr_data; + vec2 ssr_normal; + int flag; +# ifdef USE_SSS + vec3 sss_irradiance; + vec3 sss_albedo; + float sss_radius; +# endif + +#endif +}; + +/* Prototype */ +Closure nodetree_exec(void); + +/* clang-format off */ +/* Avoid multiline defines. */ +#ifdef VOLUMETRICS +# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), vec3(0), 0.0) +#elif !defined(USE_SSS) +# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0) +#else +# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0, vec3(0), vec3(0), 0.0) +#endif +/* clang-format on */ + +#define FLAG_TEST(flag, val) (((flag) & (val)) != 0) + +#define CLOSURE_SSR_FLAG 1 +#define CLOSURE_SSS_FLAG 2 +#define CLOSURE_HOLDOUT_FLAG 4 + +#ifdef VOLUMETRICS +Closure closure_mix(Closure cl1, Closure cl2, float fac) +{ + Closure cl; + cl.absorption = mix(cl1.absorption, cl2.absorption, fac); + cl.scatter = mix(cl1.scatter, cl2.scatter, fac); + cl.emission = mix(cl1.emission, cl2.emission, fac); + cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac); + return cl; +} + +Closure closure_add(Closure cl1, Closure cl2) +{ + Closure cl; + cl.absorption = cl1.absorption + cl2.absorption; + cl.scatter = cl1.scatter + cl2.scatter; + cl.emission = cl1.emission + cl2.emission; + cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */ + return cl; +} + +Closure closure_emission(vec3 rgb) +{ + Closure cl = CLOSURE_DEFAULT; + cl.emission = rgb; + return cl; +} + +#else /* SURFACE */ + +Closure closure_mix(Closure cl1, Closure cl2, float fac) +{ + Closure cl; + cl.holdout = mix(cl1.holdout, cl2.holdout, fac); + + if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) { + fac = 1.0; + } + else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) { + fac = 0.0; + } + + 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 + cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac); + bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); + /* It also does not make sense to mix SSS radius or irradiance. */ + cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; + cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; +# endif + return cl; +} + +Closure closure_add(Closure cl1, Closure 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_albedo = cl1.sss_albedo + cl2.sss_albedo; + bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); + /* It also does not make sense to mix SSS radius or irradiance. */ + cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; + cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; +# endif + return cl; +} + +Closure closure_emission(vec3 rgb) +{ + Closure cl = CLOSURE_DEFAULT; + cl.radiance = rgb; + return cl; +} + +#endif + +#ifndef VOLUMETRICS + +void closure_load_ssr_data( + vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout 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; + } +} + +void closure_load_sss_data( + float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl) +{ +# ifdef USE_SSS + if (sss_id == outputSssId) { + cl.sss_irradiance = sss_irradiance; + cl.sss_radius = radius; + cl.sss_albedo = sss_albedo; + cl.flag |= CLOSURE_SSS_FLAG; + cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0)); + } + else +# endif + { + cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo); + } +} + +#endif diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl index bc7879763c3..bf33caf9854 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl @@ -1,32 +1,8 @@ -#ifndef LIT_SURFACE_UNIFORM -# define LIT_SURFACE_UNIFORM - -uniform float refractionDepth; - -# ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -# endif /* UTIL_TEX */ - -in vec3 worldPosition; -in vec3 viewPosition; - -in vec3 worldNormal; -in vec3 viewNormal; - -# ifdef HAIR_SHADER -in vec3 hairTangent; /* world space */ -in float hairThickTime; -in float hairThickness; -in float hairTime; -flat in int hairStrandID; - -uniform int hairThicknessRes = 1; -# endif - -#endif /* LIT_SURFACE_UNIFORM */ +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) +#pragma BLENDER_REQUIRE(ssr_lib.glsl) /** * AUTO CONFIG @@ -209,7 +185,7 @@ void CLOSURE_NAME(vec3 N vec3 V = cameraVec; - vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0); + vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); /* ---------------------------------------------------------------- */ /* -------------------- SCENE LIGHTS LIGHTING --------------------- */ @@ -328,11 +304,11 @@ void CLOSURE_NAME(vec3 N # endif # ifdef CLOSURE_GLOSSY - vec3 spec_dir = get_specular_reflection_dominant_dir(N, V, roughnessSquared); + vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared); # endif # ifdef CLOSURE_CLEARCOAT - vec3 C_spec_dir = get_specular_reflection_dominant_dir(C_N, V, C_roughnessSquared); + vec3 C_spec_dir = specular_dominant_dir(C_N, V, C_roughnessSquared); # endif # ifdef CLOSURE_REFRACTION @@ -345,7 +321,7 @@ void CLOSURE_NAME(vec3 N line_plane_intersect( worldPosition, refr_V, worldPosition - N * refractionDepth, N) : worldPosition; - vec3 refr_dir = get_specular_refraction_dominant_dir(N, refr_V, roughness, final_ior); + vec3 refr_dir = refraction_dominant_dir(N, refr_V, roughness, final_ior); # endif # ifdef CLOSURE_REFRACTION @@ -485,7 +461,7 @@ void CLOSURE_NAME(vec3 N # endif # ifdef CLOSURE_REFRACTION - float btdf = get_btdf_lut(utilTex, NV, roughness, ior); + float btdf = get_btdf_lut(NV, roughness, ior); out_refr += refr_accum.rgb * btdf; # endif diff --git a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl index 759b4098b37..a6c9eebaff2 100644 --- a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl @@ -2,7 +2,6 @@ layout(std140) uniform common_block { mat4 pastViewProjectionMatrix; - vec4 viewVecs[2]; vec2 mipRatio[10]; /* To correct mip level texel misalignment */ /* Ambient Occlusion */ vec4 aoParameters[2]; @@ -70,3 +69,9 @@ layout(std140) uniform common_block #define ssrQuality ssrParameters.x #define ssrThickness ssrParameters.y #define ssrPixelSize ssrParameters.zw + +vec2 mip_ratio_interp(float mip) +{ + float low_mip = floor(mip); + return mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip); +} diff --git a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl new file mode 100644 index 00000000000..95a585f0d9c --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl @@ -0,0 +1,65 @@ + +#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Utiltex + * + * Utiltex is a sampler2DArray that stores a number of useful small utilitary textures and lookup + * tables. + * \{ */ + +uniform sampler2DArray utilTex; + +#define LUT_SIZE 64 + +#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) + +/* Return texture coordinates to sample Surface LUT */ +vec2 lut_coords(float cosTheta, float roughness) +{ + float theta = acos(cosTheta); + vec2 coords = vec2(roughness, theta / M_PI_2); + + /* scale and bias coordinates, for correct filtered lookup */ + return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; +} + +vec2 lut_coords_ltc(float cosTheta, float roughness) +{ + vec2 coords = vec2(roughness, sqrt(1.0 - cosTheta)); + + /* scale and bias coordinates, for correct filtered lookup */ + return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; +} + +float get_btdf_lut(float NV, float roughness, float ior) +{ + const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE; + + vec3 coords; + /* Try to compensate for the low resolution and interpolation error. */ + coords.x = (ior > 1.0) ? (0.9 + lut_scale_bias_texel_size.z) + + (0.1 - lut_scale_bias_texel_size.z) * f0_from_ior(ior) : + (0.9 + lut_scale_bias_texel_size.z) * ior * ior; + coords.y = 1.0 - saturate(NV); + coords.xy *= lut_scale_bias_texel_size.x; + coords.xy += lut_scale_bias_texel_size.y; + + const float lut_lvl_ofs = 4.0; /* First texture lvl of roughness. */ + const float lut_lvl_scale = 16.0; /* How many lvl of roughness in the lut. */ + + float mip = roughness * lut_lvl_scale; + float mip_floor = floor(mip); + + coords.z = lut_lvl_ofs + mip_floor + 1.0; + float btdf_high = textureLod(utilTex, coords, 0.0).r; + + coords.z -= 1.0; + float btdf_low = textureLod(utilTex, coords, 0.0).r; + + float btdf = (ior == 1.0) ? 1.0 : mix(btdf_low, btdf_high, mip - coords.z); + + return btdf; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/default_frag.glsl b/source/blender/draw/engines/eevee/shaders/default_frag.glsl deleted file mode 100644 index 1014b25033a..00000000000 --- a/source/blender/draw/engines/eevee/shaders/default_frag.glsl +++ /dev/null @@ -1,51 +0,0 @@ - -uniform vec3 basecol; -uniform float metallic; -uniform float specular; -uniform float roughness; - -Closure nodetree_exec(void) -{ -#ifdef HAIR_SHADER - vec3 B = normalize(cross(worldNormal, hairTangent)); - float cos_theta; - if (hairThicknessRes == 1) { - vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0); - /* Random cosine normal distribution on the hair surface. */ - cos_theta = rand.x * 2.0 - 1.0; - } - else { - /* Shade as a cylinder. */ - cos_theta = hairThickTime / hairThickness; - } - float sin_theta = sqrt(max(0.0, 1.0f - cos_theta * cos_theta)); - vec3 N = normalize(worldNormal * sin_theta + B * cos_theta); - vec3 vN = mat3(ViewMatrix) * N; -#else - vec3 N = normalize(gl_FrontFacing ? worldNormal : -worldNormal); - vec3 vN = normalize(gl_FrontFacing ? viewNormal : -viewNormal); -#endif - - vec3 dielectric = vec3(0.034) * specular * 2.0; - vec3 albedo = mix(basecol, vec3(0.0), metallic); - vec3 f0 = mix(dielectric, basecol, metallic); - vec3 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic); - vec3 out_diff, out_spec, ssr_spec; - eevee_closure_default(N, albedo, f0, f90, 1, roughness, 1.0, true, out_diff, out_spec, ssr_spec); - - Closure cl = CLOSURE_DEFAULT; - cl.radiance = render_pass_glossy_mask(vec3(1.0), out_spec) + - render_pass_diffuse_mask(albedo, out_diff * albedo); - closure_load_ssr_data(ssr_spec, roughness, N, viewCameraVec, 1, cl); - -#ifdef LOOKDEV - gl_FragDepth = 0.0; -#endif - -#ifdef HOLDOUT - cl = CLOSURE_DEFAULT; - cl.holdout = 1.0; -#endif - - return cl; -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl index d56890769a7..9c1ca17f87c 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl @@ -1,4 +1,8 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + uniform sampler2D colorBuffer; uniform sampler2D depthBuffer; @@ -18,9 +22,6 @@ uniform vec4 bokehParams[2]; uniform vec2 nearFar; /* Near & far view depths values */ -#define M_PI 3.1415926535897932384626433832795 -#define M_2PI 6.2831853071795864769252868 - /* -------------- Utils ------------- */ /* divide by sensor size to get the normalized size */ @@ -34,11 +35,6 @@ uniform vec2 nearFar; /* Near & far view depths values */ #define weighted_sum(a, b, c, d, e) \ (a * e.x + b * e.y + c * e.z + d * e.w) / max(1e-6, dot(e, vec4(1.0))); -float max_v4(vec4 v) -{ - return max(max(v.x, v.y), max(v.z, v.w)); -} - vec4 safe_color(vec4 c) { /* Clamp to avoid black square artifacts if a pixel goes NaN. */ diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl index d83b410125a..6e35d4a54ae 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + uniform vec4 bokehParams[2]; #define bokeh_rotation bokehParams[0].x @@ -15,8 +17,6 @@ flat out float smoothFac; flat out ivec2 edge; out vec2 particlecoord; -#define M_PI 3.1415926535897932384626433832795 - /* Scatter pass, calculate a triangle covering the CoC. */ void main() { diff --git a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl index eea0ce0aae2..47fe21928b3 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl @@ -1,14 +1,34 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) + /** * This shader only compute maximum horizon angles for each directions. * The final integration is done at the resolve stage with the shading normal. */ -uniform float rotationOffset; - out vec4 FragColor; -#ifdef DEBUG_AO uniform sampler2D normalBuffer; +#ifdef LAYERED_DEPTH +uniform sampler2DArray depthBufferLayered; +uniform int layer; +# define gtao_depthBuffer depthBufferLayered +# define gtao_textureLod(a, b, c) textureLod(a, vec3(b, layer), c) + +#else +uniform sampler2D depthBuffer; +# define gtao_depthBuffer depthBuffer +# define gtao_textureLod(a, b, c) textureLod(a, b, c) + +#endif + +uniform float rotationOffset; + +#ifdef DEBUG_AO void main() { @@ -34,18 +54,6 @@ void main() #else -# ifdef LAYERED_DEPTH -uniform sampler2DArray depthBufferLayered; -uniform int layer; -# define gtao_depthBuffer depthBufferLayered -# define gtao_textureLod(a, b, c) textureLod(a, vec3(b, layer), c) - -# else -# define gtao_depthBuffer depthBuffer -# define gtao_textureLod(a, b, c) textureLod(a, b, c) - -# endif - void main() { vec2 uvs = saturate(gl_FragCoord.xy / vec2(textureSize(gtao_depthBuffer, 0).xy)); diff --git a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl index edee55a07e0..7331f92ba6d 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl @@ -1,5 +1,10 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + /* Convert depth to Mist factor */ uniform vec3 mistSettings; +uniform sampler2D depthBuffer; #define mistStart mistSettings.x #define mistInvDistance mistSettings.y diff --git a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl index fbf507a2e40..3501a4448c5 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + /* * Based on: * A Fast and Stable Feature-Aware Motion Blur Filter @@ -15,11 +17,6 @@ uniform sampler2D tileMaxBuffer; #define KERNEL 8 -/* TODO(fclem) deduplicate this code. */ -uniform sampler2DArray utilTex; -#define LUT_SIZE 64 -#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) - uniform float depthScale; uniform ivec2 tileBufferSize; uniform vec2 viewportSize; diff --git a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl index 598cc3e5183..f8dccb7511a 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl @@ -1,4 +1,11 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(raytrace_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(ssr_lib.glsl) + /* Based on Stochastic Screen Space Reflections * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections */ @@ -131,7 +138,7 @@ void main() return; } - vec4 rand = texelFetch(utilTex, ivec3(halfres_texel % LUT_SIZE, 2), 0); + vec4 rand = texelfetch_noise_tex(halfres_texel); /* Gives *perfect* reflection for very small roughness */ if (roughness < 0.04) { diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl index e9da49c9eb9..2a53a4f119f 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl @@ -1,4 +1,9 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) + /* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ #define MAX_SSS_SAMPLES 65 @@ -14,36 +19,16 @@ uniform sampler2D sssIrradiance; uniform sampler2D sssRadius; uniform sampler2D sssAlbedo; -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - layout(location = 0) out vec4 sssRadiance; -float get_view_z_from_depth(float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - float d = 2.0 * depth - 1.0; - return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); - } - else { - return viewVecs[0].z + depth * viewVecs[1].z; - } -} - -#define LUT_SIZE 64 -#define M_PI_2 1.5707963267948966 /* pi/2 */ -#define M_2PI 6.2831853071795865 /* 2*pi */ - void main(void) { vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO precompute */ vec2 uvs = gl_FragCoord.xy * pixel_size; vec3 sss_irradiance = texture(sssIrradiance, uvs).rgb; float sss_radius = texture(sssRadius, uvs).r; - float depth_view = get_view_z_from_depth(texture(depthBuffer, uvs).r); + float depth = texture(depthBuffer, uvs).r; + float depth_view = get_view_z_from_depth(depth); float rand = texelfetch_noise_tex(gl_FragCoord.xy).r; #ifdef FIRST_PASS diff --git a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl index b44645174bd..28947e971d2 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl @@ -1,5 +1,11 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +uniform sampler2D colorBuffer; +uniform sampler2D depthBuffer; uniform sampler2D colorHistoryBuffer; + uniform mat4 prevViewProjectionMatrix; out vec4 FragColor; diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl index 6531ceb8dbe..c85eff92e37 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl @@ -1,11 +1,16 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(lights_lib.glsl) + in vec4 uvcoordsvar; out vec4 FragColor; +uniform sampler2D depthBuffer; uniform sampler1D sssTexProfile; uniform sampler2D sssRadius; - uniform sampler2DArray sssShadowCubes; uniform sampler2DArray sssShadowCascades; @@ -27,12 +32,6 @@ vec3 sss_profile(float s) return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb; } -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - float light_translucent_power_with_falloff(LightData ld, vec3 N, vec4 l_vector) { float power, falloff; diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl index d927fd78d30..145939cefb2 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl @@ -1,4 +1,8 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +uniform sampler2D depthBuffer; + uniform mat4 prevViewProjMatrix; uniform mat4 currViewProjMatrixInv; uniform mat4 nextViewProjMatrix; diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl index 0eb598521af..f52acaf6f7f 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl @@ -148,4 +148,4 @@ void main() tileMaxVelocity = encode_velocity(max_motion); } -#endif
\ No newline at end of file +#endif diff --git a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl b/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl index 64ea87001f4..2274bf8b950 100644 --- a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl @@ -1,18 +1,71 @@ -uniform sampler2DArray irradianceGrid; +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) +#pragma BLENDER_REQUIRE(octahedron_lib.glsl) #define IRRADIANCE_LIB +/* ---------------------------------------------------------------------- */ +/** \name Structure + * \{ */ + #if defined(IRRADIANCE_SH_L2) struct IrradianceData { vec3 shcoefs[9]; }; + #else /* defined(IRRADIANCE_HL2) */ struct IrradianceData { vec3 cubesides[3]; }; + #endif +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Resources + * \{ */ + +uniform sampler2DArray irradianceGrid; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Functions + * \{ */ + +vec4 irradiance_encode(vec3 rgb) +{ + float maxRGB = max_v3(rgb); + float fexp = ceil(log2(maxRGB)); + return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0); +} + +vec3 irradiance_decode(vec4 data) +{ + float fexp = data.a * 255.0 - 128.0; + return data.rgb * exp2(fexp); +} + +vec4 visibility_encode(vec2 accum, float range) +{ + accum /= range; + + vec4 data; + data.x = fract(accum.x); + data.y = floor(accum.x) / 255.0; + data.z = fract(accum.y); + data.w = floor(accum.y) / 255.0; + + return data; +} + +vec2 visibility_decode(vec4 data, float range) +{ + return (data.xz + data.yw * 255.0) * range; +} + IrradianceData load_irradiance_cell(int cell, vec3 N) { /* Keep in sync with diffuse_filter_probe() */ @@ -155,3 +208,5 @@ vec3 irradiance_from_cell_get(int cell, vec3 ir_dir) IrradianceData ir_data = load_irradiance_cell(cell, ir_dir); return compute_irradiance(ir_dir, ir_data); } + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl index 96fe94fc41e..b0da4274a13 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) + flat in int pid; in vec2 quadCoord; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl index db780714091..d06ad553ca4 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + /* XXX TODO fix code duplication */ struct CubeData { vec4 position_type; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl index b485511318b..bf45169ebaa 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl @@ -1,4 +1,8 @@ +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) + uniform samplerCube probeHdr; uniform int probeSize; uniform float lodFactor; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl index 00eb3c7e200..ccb77427ed2 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) + uniform samplerCube probeHdr; uniform float roughnessSquared; uniform float texelSize; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl index 5d8af21032a..8d7c58a93d5 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl @@ -1,4 +1,8 @@ +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) + uniform samplerCube probeDepth; uniform int outputSize; uniform float lodFactor; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl index f8bc1703c66..009ccf6535e 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl @@ -40,9 +40,6 @@ void main() for (int v = 0; v < 3; v++) { gl_Position = vPos[v]; worldPosition = x_axis[fFace] * vPos[v].x + y_axis[fFace] * vPos[v].y + maj_axes[fFace]; -#ifdef USE_ATTR - pass_attr(v); -#endif EmitVertex(); } diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl index f9bcc718a1e..dc5ec1e40f5 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) flat in int cellOffset; in vec2 quadCoord; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl index 4e500db827e..6fefe1319bd 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + uniform float sphere_size; uniform int offset; uniform ivec3 grid_resolution; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl index 6c6db88139b..a2e25b83532 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl @@ -1,3 +1,12 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) +#pragma BLENDER_REQUIRE(cubemap_lib.glsl) +#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) + /* ----------- Uniforms --------- */ uniform sampler2DArray probePlanars; @@ -73,12 +82,6 @@ struct GridData { # define MAX_PLANAR 1 #endif -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - layout(std140) uniform probe_block { CubeData probes_data[MAX_PROBE]; @@ -218,7 +221,7 @@ void fallback_cubemap(vec3 N, inout vec4 spec_accum) { /* Specular probes */ - vec3 spec_dir = get_specular_reflection_dominant_dir(N, V, roughnessSquared); + vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared); #ifdef SSR_AO vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); @@ -246,7 +249,6 @@ void fallback_cubemap(vec3 N, } } -#ifdef IRRADIANCE_LIB vec3 probe_evaluate_grid(GridData gd, vec3 W, vec3 N, vec3 localpos) { localpos = localpos * 0.5 + 0.5; @@ -308,5 +310,3 @@ vec3 probe_evaluate_world_diff(vec3 N) { return irradiance_from_cell_get(0, N); } - -#endif /* IRRADIANCE_LIB */ diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl index 2807488db6c..0a53abcb04a 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + uniform sampler2DArray probePlanars; in vec3 worldPosition; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl index 65506e5c7b1..6759c060259 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + in vec3 pos; in int probe_id; diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl index 3b9d0a8f2bc..949e4d8f04f 100644 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl @@ -1,8 +1,76 @@ -uniform sampler2DArrayShadow shadowCubeTexture; -uniform sampler2DArrayShadow shadowCascadeTexture; +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(raytrace_lib.glsl) +#pragma BLENDER_REQUIRE(ltc_lib.glsl) + +#ifndef MAX_CASCADE_NUM +# define MAX_CASCADE_NUM 4 +#endif + +/* ---------------------------------------------------------------------- */ +/** \name Structure + * \{ */ + +struct LightData { + vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */ + vec4 color_spec; /* w : Spec Intensity */ + vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */ + vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */ + vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */ + vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */ +}; + +/* convenience aliases */ +#define l_color color_spec.rgb +#define l_spec color_spec.a +#define l_position position_influence.xyz +#define l_influence position_influence.w +#define l_sizex rightvec_sizex.w +#define l_sizey upvec_sizey.w +#define l_right rightvec_sizex.xyz +#define l_up upvec_sizey.xyz +#define l_forward forwardvec_type.xyz +#define l_type forwardvec_type.w +#define l_spot_size spotdata_radius_shadow.x +#define l_spot_blend spotdata_radius_shadow.y +#define l_radius spotdata_radius_shadow.z +#define l_shadowid spotdata_radius_shadow.w + +struct ShadowData { + vec4 near_far_bias_id; + vec4 contact_shadow_data; +}; + +struct ShadowCubeData { + mat4 shadowmat; + vec4 position; +}; + +struct ShadowCascadeData { + mat4 shadowmat[MAX_CASCADE_NUM]; + vec4 split_start_distances; + vec4 split_end_distances; + vec4 shadow_vec_id; +}; + +/* convenience aliases */ +#define sh_near near_far_bias_id.x +#define sh_far near_far_bias_id.y +#define sh_bias near_far_bias_id.z +#define sh_data_index near_far_bias_id.w +#define sh_contact_dist contact_shadow_data.x +#define sh_contact_offset contact_shadow_data.y +#define sh_contact_spread contact_shadow_data.z +#define sh_contact_thickness contact_shadow_data.w +#define sh_shadow_vec shadow_vec_id.xyz +#define sh_tex_index shadow_vec_id.w + +/** \} */ -#define LAMPS_LIB +/* ---------------------------------------------------------------------- */ +/** \name Resources + * \{ */ layout(std140) uniform shadow_block { @@ -16,6 +84,15 @@ layout(std140) uniform light_block LightData lights_data[MAX_LIGHT]; }; +uniform sampler2DArrayShadow shadowCubeTexture; +uniform sampler2DArrayShadow shadowCascadeTexture; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Shadow Functions + * \{ */ + /* type */ #define POINT 0.0 #define SUN 1.0 @@ -133,9 +210,11 @@ float sample_cascade_shadow(int shadow_id, vec3 W) #undef scube #undef scsmd -/* ----------------------------------------------------------- */ -/* --------------------- Light Functions --------------------- */ -/* ----------------------------------------------------------- */ +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Light Functions + * \{ */ /* From Frostbite PBR Course * Distance based attenuation @@ -258,7 +337,6 @@ float light_visibility(LightData ld, l_atten); } -#ifdef USE_LTC float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) { if (ld.l_type == AREA_RECT) { @@ -321,4 +399,5 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) return ltc_evaluate_disk(N, V, ltc_matrix(ltc_mat), points); } } -#endif + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/default_world_frag.glsl b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl index 8c876cf582c..9077b414026 100644 --- a/source/blender/draw/engines/eevee/shaders/default_world_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl @@ -1,20 +1,17 @@ -uniform float backgroundAlpha; -uniform vec3 color; - -out vec4 FragColor; +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) -#if defined(LOOKDEV_BG) || defined(LOOKDEV) +uniform sampler2D studioLight; +uniform float backgroundAlpha; uniform mat3 StudioLightMatrix; -uniform sampler2D image; uniform float studioLightIntensity = 1.0; uniform float studioLightBlur = 0.0; -in vec3 viewPosition; -# ifndef M_PI -# define M_PI 3.14159265358979323846 -# endif +out vec4 FragColor; vec3 background_transform_to_world(vec3 viewvec) { @@ -35,36 +32,20 @@ vec4 node_tex_environment_equirectangular(vec3 co, sampler2D ima) vec3 nco = normalize(co); float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5; float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5; - - /* Fix pole bleeding */ - float width = float(textureSize(ima, 0).x); - float texel_width = 1.0 / width; - v = clamp(v, texel_width, 1.0 - texel_width); - - /* Fix u = 0 seam */ - /* This is caused by texture filtering, since uv don't have smooth derivatives - * at u = 0 or 2PI, hardware filtering is using the smallest mipmap for certain - * texels. So we force the highest mipmap and don't do anisotropic filtering. */ return textureLod(ima, vec2(u, v), 0.0); } -#endif void main() { - vec3 background_color; + vec3 worldvec = background_transform_to_world(viewPosition); + vec3 background_color; #if defined(LOOKDEV_BG) - vec3 worldvec = background_transform_to_world(viewPosition); background_color = probe_evaluate_world_spec(worldvec, studioLightBlur).rgb; - background_color *= studioLightIntensity; - -#elif defined(LOOKDEV) - vec3 worldvec = background_transform_to_world(viewPosition); - background_color = node_tex_environment_equirectangular(StudioLightMatrix * worldvec, image).rgb; - background_color *= studioLightIntensity; - #else - background_color = color; + worldvec = StudioLightMatrix * worldvec; + background_color = node_tex_environment_equirectangular(worldvec, studioLight).rgb; + background_color *= studioLightIntensity; #endif FragColor = vec4(clamp(background_color, vec3(0.0), vec3(1e10)), 1.0) * backgroundAlpha; diff --git a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl index dbfc7ad5a71..2750d42a74a 100644 --- a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl @@ -8,12 +8,6 @@ #define USE_LTC -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - /* Diffuse *clipped* sphere integral. */ float diffuse_sphere_integral(float avg_dir_z, float form_factor) { diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl index 95cd69ba310..ef96bcbaedb 100644 --- a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) + uniform mat4 currModelMatrix; uniform mat4 prevModelMatrix; uniform mat4 nextModelMatrix; @@ -19,6 +22,8 @@ out vec3 nextWorldPos; void main() { + GPU_INTEL_VERTEX_SHADER_WORKAROUND + #ifdef HAIR bool is_persp = (ProjectionMatrix[3][3] == 0.0); float time, thick_time, thickness; diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl index 9acd8f998f6..34999076f9c 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl @@ -1,4 +1,14 @@ +/* Required by some nodes. */ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lit_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + #ifdef USE_ALPHA_HASH /* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire */ @@ -56,8 +66,6 @@ float hashed_alpha_threshold(vec3 co) #endif -#define NODETREE_EXEC - void main() { #if defined(USE_ALPHA_HASH) @@ -66,11 +74,9 @@ void main() float opacity = saturate(1.0 - avg(cl.transmittance)); -# if defined(USE_ALPHA_HASH) /* Hashed Alpha Testing */ if (opacity < hashed_alpha_threshold(worldPosition)) { discard; } -# endif #endif } diff --git a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl index 2c7e0aca3fb..f650e2eeb8c 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl @@ -1,16 +1,14 @@ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + #ifndef HAIR_SHADER in vec3 pos; #endif void main() { -#ifdef GPU_INTEL - /* Due to some shader compiler bug, we somewhat - * need to access gl_VertexID to make it work. even - * if it's actually dead code. */ - gl_Position.x = float(gl_VertexID); -#endif + GPU_INTEL_VERTEX_SHADER_WORKAROUND #ifdef HAIR_SHADER float time, thick_time, thickness; @@ -34,5 +32,4 @@ void main() #ifdef CLIP_PLANES gl_ClipDistance[0] = dot(vec4(worldPosition.xyz, 1.0), clipPlanes[0]); #endif - /* TODO motion vectors */ } diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl index f88cfdf3787..39db39f8756 100644 --- a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl @@ -1,3 +1,11 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) + +uniform sampler2D maxzBuffer; +uniform sampler2DArray planarDepth; + #define MAX_STEP 256 float sample_depth(vec2 uv, int index, float lod) diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl new file mode 100644 index 00000000000..36cf3cecf40 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl @@ -0,0 +1,43 @@ + +/* ---------------------------------------------------------------------- */ +/** \name Resources + * \{ */ + +layout(std140) uniform renderpass_block +{ + bool renderPassDiffuse; + bool renderPassDiffuseLight; + bool renderPassGlossy; + bool renderPassGlossyLight; + bool renderPassEmit; + bool renderPassSSSColor; + bool renderPassEnvironment; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Functions + * \{ */ + +vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light) +{ + return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0); +} + +vec3 render_pass_sss_mask(vec3 sss_color) +{ + return renderPassSSSColor ? sss_color : vec3(0.0); +} + +vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light) +{ + return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0); +} + +vec3 render_pass_emission_mask(vec3 emission_light) +{ + return renderPassEmit ? emission_light : vec3(0.0); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl index 361963d5ac3..89a411bc7cb 100644 --- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl @@ -1,3 +1,7 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) + #define PASS_POST_UNDEFINED 0 #define PASS_POST_ACCUMULATED_COLOR 1 #define PASS_POST_ACCUMULATED_LIGHT 2 @@ -9,6 +13,8 @@ uniform int postProcessType; uniform int currentSample; + +uniform sampler2D depthBuffer; uniform sampler2D inputBuffer; uniform sampler2D inputSecondLightBuffer; uniform sampler2D inputColorBuffer; diff --git a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl index 860ec9e1727..e0b9d4a60db 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl @@ -1,11 +1,11 @@ -out vec4 fragColor; +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(lights_lib.glsl) + +uniform sampler2D depthBuffer; -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ +out vec4 fragColor; void main() { diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index c42f905cf7e..0e342938396 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -1,39 +1,23 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + in vec3 pos; in vec3 nor; -#ifdef MESH_SHADER -out vec3 worldPosition; -out vec3 viewPosition; -out vec3 worldNormal; -out vec3 viewNormal; -#endif - -#ifdef HAIR_SHADER -out vec3 hairTangent; -out float hairThickTime; -out float hairThickness; -out float hairTime; -flat out int hairStrandID; -#endif - void main() { -#ifdef GPU_INTEL - /* Due to some shader compiler bug, we somewhat - * need to access gl_VertexID to make it work. even - * if it's actually dead code. */ - gl_Position.x = float(gl_VertexID); -#endif + GPU_INTEL_VERTEX_SHADER_WORKAROUND #ifdef HAIR_SHADER hairStrandID = hair_get_strand_id(); - vec3 world_pos, binor; + vec3 pos, binor; hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), ModelMatrixInverse, ViewMatrixInverse[3].xyz, ViewMatrixInverse[2].xyz, - world_pos, + pos, hairTangent, binor, hairTime, @@ -41,6 +25,7 @@ void main() hairThickTime); worldNormal = cross(hairTangent, binor); + vec3 world_pos = pos; #else vec3 world_pos = point_object_to_world(pos); #endif @@ -57,7 +42,10 @@ void main() /* No need to normalize since this is just a rotation. */ viewNormal = normal_world_to_view(worldNormal); # ifdef USE_ATTR - pass_attr(pos); +# ifdef HAIR_SHADER + pos = hair_get_strand_pos(); +# endif + pass_attr(pos, NormalMatrix, ModelMatrixInverse); # endif #endif } diff --git a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl index 0591b247541..29495e98355 100644 --- a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl @@ -1,3 +1,10 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(raytrace_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + /* ------------ Refraction ------------ */ #define BTDF_BIAS 0.85 diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl new file mode 100644 index 00000000000..e746acfdfa3 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl @@ -0,0 +1,89 @@ + +/* Required by some nodes. */ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + +#pragma BLENDER_REQUIRE(closure_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lit_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + +#ifdef 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; + +#else /* OPAQUE */ +layout(location = 0) out vec4 outRadiance; +layout(location = 1) out vec2 ssrNormals; +layout(location = 2) out vec4 ssrData; +# ifdef USE_SSS +layout(location = 3) out vec3 sssIrradiance; +layout(location = 4) out float sssRadius; +layout(location = 5) out vec3 sssAlbedo; +# endif + +#endif + +void main() +{ + Closure cl = nodetree_exec(); + + float holdout = saturate(1.0 - cl.holdout); + float transmit = saturate(avg(cl.transmittance)); + float alpha = 1.0 - transmit; + +#ifdef USE_ALPHA_BLEND + vec2 uvs = gl_FragCoord.xy * volCoordScale.zw; + vec3 vol_transmit, vol_scatter; + volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter); + + /* Removes part of the volume scattering that have + * already been added to the destination pixels. + * Since we do that using the blending pipeline we need to account for material transmittance. */ + vol_scatter -= vol_scatter * cl.transmittance; + + cl.radiance = cl.radiance * holdout * vol_transmit + vol_scatter; + outRadiance = vec4(cl.radiance, alpha * holdout); + outTransmittance = vec4(cl.transmittance, transmit) * holdout; +#else + outRadiance = vec4(cl.radiance, holdout); + ssrNormals = cl.ssr_normal; + ssrData = cl.ssr_data; +# ifdef USE_SSS + sssIrradiance = cl.sss_irradiance; + sssRadius = cl.sss_radius; + sssAlbedo = cl.sss_albedo; +# endif +#endif + + /* For Probe capture */ +#ifdef USE_SSS + float fac = float(!sssToggle); + + /* TODO(fclem) we shouldn't need this. + * Just disable USE_SSS when USE_REFRACTION is enabled. */ +# ifdef USE_REFRACTION + /* SSRefraction pass is done after the SSS pass. + * In order to not loose the diffuse light totally we + * need to merge the SSS radiance to the main radiance. */ + fac = 1.0; +# endif + + outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac; +#endif + +#ifndef USE_ALPHA_BLEND + float alpha_div = 1.0 / max(1e-8, alpha); + outRadiance.rgb *= alpha_div; + ssrData.rgb *= alpha_div; +# ifdef USE_SSS + sssAlbedo.rgb *= alpha_div; +# endif +#endif + +#ifdef LOOKDEV + /* Lookdev spheres are rendered in front. */ + gl_FragDepth = 0.0; +#endif +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_geom.glsl b/source/blender/draw/engines/eevee/shaders/surface_geom.glsl new file mode 100644 index 00000000000..ad437dca79a --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/surface_geom.glsl @@ -0,0 +1,46 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +RESOURCE_ID_VARYING + +/* Only used to compute barycentric coordinates. */ + +void main() +{ +#ifdef DO_BARYCENTRIC_DISTANCES + dataAttrOut.barycentricDist = calc_barycentric_distances( + dataIn[0].worldPosition, dataIn[1].worldPosition, dataIn[2].worldPosition); +#endif + + PASS_RESOURCE_ID + +#ifdef USE_ATTR + pass_attr(0); +#endif + PASS_SURFACE_INTERFACE(0); + gl_Position = gl_in[0].gl_Position; + gl_ClipDistance[0] = gl_in[0].gl_ClipDistance[0]; + EmitVertex(); + +#ifdef USE_ATTR + pass_attr(1); +#endif + PASS_SURFACE_INTERFACE(1); + gl_Position = gl_in[1].gl_Position; + gl_ClipDistance[0] = gl_in[1].gl_ClipDistance[0]; + EmitVertex(); + +#ifdef USE_ATTR + pass_attr(2); +#endif + PASS_SURFACE_INTERFACE(2); + gl_Position = gl_in[2].gl_Position; + gl_ClipDistance[0] = gl_in[2].gl_ClipDistance[0]; + EmitVertex(); + + EndPrimitive(); +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl new file mode 100644 index 00000000000..b93a3a23eff --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -0,0 +1,43 @@ +/** This describe the entire interface of the shader. */ + +/* Samplers */ +uniform sampler2D colorBuffer; +uniform sampler2D depthBuffer; + +/* Uniforms */ +uniform float refractionDepth; + +#define SURFACE_INTERFACE \ + vec3 worldPosition; \ + vec3 viewPosition; \ + vec3 worldNormal; \ + vec3 viewNormal; + +#ifdef GPU_GEOMETRY_SHADER +in ShaderStageInterface{SURFACE_INTERFACE} dataIn[]; + +out ShaderStageInterface{SURFACE_INTERFACE} dataOut; + +# define PASS_SURFACE_INTERFACE(vert) \ + dataOut.worldPosition = dataIn[vert].worldPosition; \ + dataOut.viewPosition = dataIn[vert].viewPosition; \ + dataOut.worldNormal = dataIn[vert].worldNormal; \ + dataOut.viewNormal = dataIn[vert].viewNormal; + +#else + +IN_OUT ShaderStageInterface{SURFACE_INTERFACE}; + +#endif + +#ifdef HAIR_SHADER +IN_OUT ShaderHairInterface +{ + /* world space */ + vec3 hairTangent; + float hairThickTime; + float hairThickness; + float hairTime; + flat int hairStrandID; +}; +#endif diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 1b94fc2bee1..0ad1393dd70 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -1,32 +1,20 @@ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + #ifndef HAIR_SHADER in vec3 pos; in vec3 nor; #endif -#ifdef MESH_SHADER -out vec3 worldPosition; -out vec3 viewPosition; -out vec3 worldNormal; -out vec3 viewNormal; -#endif - -#ifdef HAIR_SHADER -out vec3 hairTangent; -out float hairThickTime; -out float hairThickness; -out float hairTime; -flat out int hairStrandID; -#endif +RESOURCE_ID_VARYING void main() { -#ifdef GPU_INTEL - /* Due to some shader compiler bug, we somewhat - * need to access gl_VertexID to make it work. even - * if it's actually dead code. */ - gl_Position.x = float(gl_VertexID); -#endif + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + PASS_RESOURCE_ID #ifdef HAIR_SHADER hairStrandID = hair_get_strand_id(); @@ -63,7 +51,10 @@ void main() /* No need to normalize since this is just a rotation. */ viewNormal = normal_world_to_view(worldNormal); # ifdef USE_ATTR - pass_attr(pos); +# ifdef HAIR_SHADER + pos = hair_get_strand_pos(); +# endif + pass_attr(pos, NormalMatrix, ModelMatrixInverse); # endif #endif } diff --git a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl b/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl index 02ad2170f06..0c01c46c2ba 100644 --- a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl @@ -1,11 +1,11 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + uniform sampler2D blueNoise; uniform vec3 offsets; out vec4 FragColor; -#define M_2PI 6.28318530717958647692 - void main(void) { vec3 blue_noise = texelFetch(blueNoise, ivec2(gl_FragCoord.xy), 0).xyz; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index 312fc07054a..bac69ab0355 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -1,9 +1,10 @@ +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ -#define NODETREE_EXEC - #ifdef MESH_SHADER uniform vec3 volumeOrcoLoc; uniform vec3 volumeOrcoSize; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl index 96b891c929f..30cda401284 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + #ifdef MESH_SHADER /* TODO tight slices */ layout(triangles) in; @@ -12,12 +14,16 @@ in vec4 vPos[]; flat out int slice; +RESOURCE_ID_VARYING + #ifdef MESH_SHADER /* TODO tight slices */ void main() { gl_Layer = slice = int(vPos[0].z); + PASS_RESOURCE_ID + # ifdef USE_ATTR pass_attr(0); # endif @@ -48,6 +54,8 @@ void main() { gl_Layer = slice = int(vPos[0].z); + PASS_RESOURCE_ID + # ifdef USE_ATTR pass_attr(0); # endif diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl index c3c442e7b69..f4276bd61bd 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ @@ -11,9 +13,11 @@ uniform sampler3D volumeExtinction; #ifdef USE_VOLUME_OPTI uniform layout(binding = 0, r11f_g11f_b10f) writeonly restrict image3D finalScattering_img; uniform layout(binding = 1, r11f_g11f_b10f) writeonly restrict image3D finalTransmittance_img; + vec3 finalScattering; vec3 finalTransmittance; #else + flat in int slice; layout(location = 0) out vec3 finalScattering; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl index 40eb3da42d1..9b852a57ec4 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl @@ -1,4 +1,8 @@ +#pragma BLENDER_REQUIRE(lights_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ @@ -58,7 +62,6 @@ float phase_function(vec3 v, vec3 l, float g) return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0)); } -#ifdef LAMPS_LIB vec3 light_volume(LightData ld, vec4 l_vector) { float power; @@ -95,7 +98,7 @@ vec3 light_volume(LightData ld, vec4 l_vector) return tint * lum; } -# define VOLUMETRIC_SHADOW_MAX_STEP 32.0 +#define VOLUMETRIC_SHADOW_MAX_STEP 32.0 vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction) { @@ -109,7 +112,7 @@ vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction) vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D volume_extinction) { -# if defined(VOLUME_SHADOW) +#if defined(VOLUME_SHADOW) /* Heterogeneous volume shadows */ float dd = l_vector.w / volShadowSteps; vec3 L = l_vector.xyz * l_vector.w; @@ -120,27 +123,24 @@ vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D v shadow *= exp(-s_extinction * dd); } return shadow; -# else +#else return vec3(1.0); -# endif /* VOLUME_SHADOW */ +#endif /* VOLUME_SHADOW */ } -#endif -#ifdef IRRADIANCE_LIB vec3 irradiance_volumetric(vec3 wpos) { -# ifdef IRRADIANCE_HL2 +#ifdef IRRADIANCE_HL2 IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0)); vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; ir_data = load_irradiance_cell(0, vec3(-1.0)); irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; irradiance *= 0.16666666; /* 1/6 */ return irradiance; -# else +#else return vec3(0.0); -# endif -} #endif +} uniform sampler3D inScattering; uniform sampler3D inTransmittance; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl index 1ff7e848c40..6ab21587c9a 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl index 9621fa1cc0d..806f1b5b205 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl index b96360febb0..b70747ecec3 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -1,6 +1,10 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + out vec4 vPos; +RESOURCE_ID_VARYING + void main() { /* Generate Triangle : less memory fetches from a VBO */ @@ -25,7 +29,9 @@ void main() vPos.z = float(t_id); vPos.w = 1.0; + PASS_RESOURCE_ID + #ifdef USE_ATTR - pass_attr(vec3(0.0)); + pass_attr(vec3(0.0), mat3(1), mat4(1)); #endif } diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 6e6b35e19ca..ed443b2c73f 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -63,7 +63,7 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_ ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); if (ibuf != NULL && ibuf->rect != NULL) { - gpu_tex = GPU_texture_from_blender(image, &iuser, ibuf, GL_TEXTURE_2D); + gpu_tex = BKE_image_get_gpu_texture(image, &iuser, ibuf); *r_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL); } BKE_image_release_ibuf(image, ibuf, lock); diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 4748858a6a8..c3294f88acf 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -143,9 +143,11 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, int w = BLI_rcti_size_x(rect); int h = BLI_rcti_size_y(rect); if (pix_col) { + GPU_texture_bind(txl->render_color_tx, 0); GPU_texture_update_sub(txl->render_color_tx, GPU_DATA_FLOAT, pix_col, x, y, 0, w, h, 0); } if (pix_z) { + GPU_texture_bind(txl->render_depth_tx, 0); GPU_texture_update_sub(txl->render_depth_tx, GPU_DATA_FLOAT, pix_z, x, y, 0, w, h, 0); } } diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 0b4d0fcdc11..60e622ca706 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -53,8 +53,6 @@ #include "ED_view3d.h" -#include "GPU_draw.h" - #include "overlay_private.h" #include "draw_common.h" @@ -1339,7 +1337,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, else { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); - if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag && (1 << 0))) { + if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag & (1 << 0))) { ListBase targets = {NULL, NULL}; bConstraintTarget *ct; @@ -1422,7 +1420,7 @@ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb, line_count /= fds->res[axis]; } - GPU_create_smoke_velocity(fmd); + DRW_smoke_ensure_velocity(fmd); GPUShader *sh = OVERLAY_shader_volume_velocity(use_needle); DRWShadingGroup *grp = DRW_shgroup_create(sh, data->psl->extra_ps[0]); @@ -1452,7 +1450,7 @@ static void OVERLAY_volume_free_smoke_textures(OVERLAY_Data *data) LinkData *link; while ((link = BLI_pophead(&data->stl->pd->smoke_domains))) { FluidModifierData *fmd = (FluidModifierData *)link->data; - GPU_free_smoke_velocity(fmd); + DRW_smoke_free_velocity(fmd); MEM_freeN(link); } } diff --git a/source/blender/draw/engines/overlay/overlay_image.c b/source/blender/draw/engines/overlay/overlay_image.c index 67132a9e0ed..d456e147c15 100644 --- a/source/blender/draw/engines/overlay/overlay_image.c +++ b/source/blender/draw/engines/overlay/overlay_image.c @@ -175,7 +175,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp } width = ibuf->x; height = ibuf->y; - tex = GPU_texture_from_blender(image, iuser, ibuf, GL_TEXTURE_2D); + tex = BKE_image_get_gpu_texture(image, iuser, ibuf); BKE_image_release_ibuf(image, ibuf, lock); iuser->scene = NULL; @@ -203,7 +203,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp } BKE_movieclip_user_set_frame(&bgpic->cuser, ctime); - tex = GPU_texture_from_movieclip(clip, &bgpic->cuser, GL_TEXTURE_2D); + tex = BKE_movieclip_get_gpu_texture(clip, &bgpic->cuser); if (tex == NULL) { return NULL; } @@ -232,7 +232,7 @@ static void OVERLAY_image_free_movieclips_textures(OVERLAY_Data *data) LinkData *link; while ((link = BLI_pophead(&data->stl->pd->bg_movie_clips))) { MovieClip *clip = (MovieClip *)link->data; - GPU_free_texture_movieclip(clip); + BKE_movieclip_free_gputexture(clip); MEM_freeN(link); } } @@ -383,7 +383,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) if (ima != NULL) { ImageUser iuser = *ob->iuser; camera_background_images_stereo_setup(draw_ctx->scene, draw_ctx->v3d, ima, &iuser); - tex = GPU_texture_from_blender(ima, &iuser, NULL, GL_TEXTURE_2D); + tex = BKE_image_get_gpu_texture(ima, &iuser, NULL); if (tex) { size[0] = GPU_texture_orig_width(tex); size[1] = GPU_texture_orig_height(tex); diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c index e94cc820568..25b8a953ba7 100644 --- a/source/blender/draw/engines/overlay/overlay_paint.c +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -22,6 +22,8 @@ #include "DRW_render.h" +#include "BKE_image.h" + #include "DNA_mesh_types.h" #include "DEG_depsgraph_query.h" @@ -136,7 +138,7 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata) state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); - GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, NULL, GL_TEXTURE_2D); + GPUTexture *tex = BKE_image_get_gpu_texture(imapaint->stencil, NULL, NULL); const bool mask_premult = (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL); const bool mask_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0; diff --git a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl index 0d01f67c6ea..2989e07691f 100644 --- a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + uniform sampler2D colorTex; uniform sampler2D depthTex; uniform sampler2D lineTex; diff --git a/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl index 380708795e9..0925901a9c9 100644 --- a/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl @@ -14,19 +14,6 @@ layout(depth_greater) out float gl_FragDepth; layout(location = 0) out vec4 fragColor; layout(location = 1) out vec4 lineOutput; -#define cameraPos ViewMatrixInverse[3].xyz - -float get_depth_from_view_z(float z) -{ - if (ProjectionMatrix[3][3] == 0.0) { - z = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2]; - } - else { - z = z * ProjectionMatrix[2][2] / (1.0 - ProjectionMatrix[3][2]); - } - return z * 0.5 + 0.5; -} - void main() { const float sphere_radius = 0.05; diff --git a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl index 317e9fe0447..d0b68df0625 100644 --- a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl @@ -14,8 +14,6 @@ uniform float meshSize; uniform float lineKernel = 0.0; uniform sampler2D depthBuffer; -#define cameraPos (ViewMatrixInverse[3].xyz) - uniform int gridFlag; #define STEPS_LEN 8 diff --git a/source/blender/draw/engines/overlay/shaders/grid_vert.glsl b/source/blender/draw/engines/overlay/shaders/grid_vert.glsl index 496bb011c74..dd0e771ad93 100644 --- a/source/blender/draw/engines/overlay/shaders/grid_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/grid_vert.glsl @@ -7,8 +7,6 @@ uniform float meshSize; uniform int gridFlag; -#define cameraPos (ViewMatrixInverse[3].xyz) - #define PLANE_XY (1 << 4) #define PLANE_XZ (1 << 5) #define PLANE_YZ (1 << 6) diff --git a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl index 722dbdd0b5e..d0d52c8485b 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl @@ -28,7 +28,7 @@ void cavity_compute(vec2 screenco, return; } - vec3 position = view_position_from_depth(screenco, depth, ViewVecs, ProjectionMatrix); + vec3 position = get_view_space_from_depth(screenco, depth); vec3 normal = workbench_normal_decode(texture(normalBuffer, screenco)); vec2 jitter_co = (screenco * world_data.viewport_size.xy) * world_data.cavity_jitter_scale; @@ -68,7 +68,7 @@ void cavity_compute(vec2 screenco, bool is_background = (s_depth == 1.0); /* This trick provide good edge effect even if no neighbor is found. */ s_depth = (is_background) ? depth : s_depth; - vec3 s_pos = view_position_from_depth(uvcoords, s_depth, ViewVecs, ProjectionMatrix); + vec3 s_pos = get_view_space_from_depth(uvcoords, s_depth); if (is_background) { s_pos.z -= world_data.cavity_distance; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl index c529f23265b..eb61edca6c7 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl @@ -67,31 +67,3 @@ void workbench_float_pair_decode(float data, out float v1, out float v2) v1 = float(idata & v1_mask) * (1.0 / float(v1_mask)); v2 = float(idata >> int(ROUGHNESS_BITS)) * (1.0 / float(v2_mask)); } - -vec3 view_vector_from_screen_uv(vec2 uv, vec4 viewvecs[2], mat4 proj_mat) -{ - if (proj_mat[3][3] == 0.0) { - return normalize(vec3(viewvecs[0].xy + uv * viewvecs[1].xy, 1.0)); - } - else { - return vec3(0.0, 0.0, 1.0); - } -} - -vec3 view_position_from_depth(vec2 uvcoords, float depth, vec4 viewvecs[2], mat4 proj_mat) -{ - if (proj_mat[3][3] == 0.0) { - /* Perspective */ - float d = 2.0 * depth - 1.0; - - float zview = -proj_mat[3][2] / (d + proj_mat[2][2]); - - return zview * vec3(viewvecs[0].xy + uvcoords * viewvecs[1].xy, 1.0); - } - else { - /* Orthographic */ - vec3 offset = vec3(uvcoords, depth); - - return viewvecs[0].xyz + offset * viewvecs[1].xyz; - } -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl index f3a238fd112..6e10a656fc1 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl @@ -14,7 +14,7 @@ out vec4 fragColor; void main() { /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ - vec3 I = view_vector_from_screen_uv(uvcoordsvar.st, ViewVecs, ProjectionMatrix); + vec3 I = get_view_vector_from_screen_uv(uvcoordsvar.st); vec3 N = workbench_normal_decode(texture(normalBuffer, uvcoordsvar.st)); vec4 mat_data = texture(materialBuffer, uvcoordsvar.st); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl index 51007a9f246..71816f6ff6e 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl @@ -1,3 +1,6 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + /** * Separable Hexagonal Bokeh Blur by Colin Barré-Brisebois * https://colinbarrebrisebois.com/2017/04/18/hexagonal-bokeh-blur-revisited-part-1-basic-3-pass-version/ @@ -21,13 +24,6 @@ uniform sampler2D noiseTex; #define dof_distance dofParams.y #define dof_invsensorsize dofParams.z -#define M_PI 3.1415926535897932 /* pi */ - -float max_v4(vec4 v) -{ - return max(max(v.x, v.y), max(v.z, v.w)); -} - #define weighted_sum(a, b, c, d, e, e_sum) \ ((a)*e.x + (b)*e.y + (c)*e.z + (d)*e.w) / max(1e-6, e_sum); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl index ba8eeff1001..fd4d00d96dd 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl @@ -57,7 +57,7 @@ void main() { /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ vec2 uv_viewport = gl_FragCoord.xy * world_data.viewport_size_inv; - vec3 I = view_vector_from_screen_uv(uv_viewport, ViewVecs, ProjectionMatrix); + vec3 I = get_view_vector_from_screen_uv(uv_viewport); vec3 N = normalize(normal_interp); vec3 color = color_interp; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 6ab652cbf36..aa938d80fa3 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -1,4 +1,5 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl) #pragma BLENDER_REQUIRE(workbench_data_lib.glsl) @@ -33,11 +34,6 @@ float phase_function_isotropic() return 1.0 / (4.0 * M_PI); } -float max_v3(vec3 v) -{ - return max(v.x, max(v.y, v.z)); -} - float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection) { /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ @@ -194,8 +190,8 @@ void main() float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; float depth_end = min(depth, gl_FragCoord.z); - vec3 vs_ray_end = view_position_from_depth(screen_uv, depth_end, ViewVecs, ProjectionMatrix); - vec3 vs_ray_ori = view_position_from_depth(screen_uv, 0.0, ViewVecs, ProjectionMatrix); + vec3 vs_ray_end = get_view_space_from_depth(screen_uv, depth_end); + vec3 vs_ray_ori = get_view_space_from_depth(screen_uv, 0.0); vec3 vs_ray_dir = (is_persp) ? (vs_ray_end - vs_ray_ori) : vec3(0.0, 0.0, -1.0); vs_ray_dir /= abs(vs_ray_dir.z); diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 4d5a5b163ed..2ed63bac853 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -261,11 +261,11 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd, if (ima) { if (ima->source == IMA_SRC_TILED) { - tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D_ARRAY); - tex_tile_data = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_1D_ARRAY); + tex = BKE_image_get_gpu_tiles(ima, iuser, NULL); + tex_tile_data = BKE_image_get_gpu_tilemap(ima, iuser, NULL); } else { - tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D); + tex = BKE_image_get_gpu_texture(ima, iuser, NULL); } } diff --git a/source/blender/draw/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c index 835f10598d4..aab3cef00e6 100644 --- a/source/blender/draw/engines/workbench/workbench_shader.c +++ b/source/blender/draw/engines/workbench/workbench_shader.c @@ -28,6 +28,8 @@ #include "workbench_engine.h" #include "workbench_private.h" +extern char datatoc_common_math_lib_glsl[]; +extern char datatoc_common_math_geom_lib_glsl[]; extern char datatoc_common_hair_lib_glsl[]; extern char datatoc_common_pointcloud_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; @@ -119,6 +121,8 @@ void workbench_shader_library_ensure(void) if (e_data.lib == NULL) { e_data.lib = DRW_shader_library_create(); /* NOTE: Theses needs to be ordered by dependencies. */ + DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_pointcloud_lib); diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 8e345f8275b..d3c4d51dbd4 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -38,8 +38,6 @@ #include "BKE_volume.h" #include "BKE_volume_render.h" -#include "GPU_draw.h" - void workbench_volume_engine_init(WORKBENCH_Data *vedata) { WORKBENCH_TextureList *txl = vedata->txl; @@ -79,13 +77,10 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, wpd->volumes_do = true; if (fds->use_coba) { - GPU_create_smoke_coba_field(fmd); - } - else if (!(fds->flags & FLUID_DOMAIN_USE_NOISE)) { - GPU_create_smoke(fmd, 0); + DRW_smoke_ensure_coba_field(fmd); } - else if (fds->flags & FLUID_DOMAIN_USE_NOISE) { - GPU_create_smoke(fmd, 1); + else { + DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE); } if ((!fds->use_coba && (fds->tex_density == NULL && fds->tex_color == NULL)) || @@ -293,7 +288,7 @@ void workbench_volume_draw_finish(WORKBENCH_Data *vedata) * all viewport in a redraw at least. */ LISTBASE_FOREACH (LinkData *, link, &wpd->smoke_domains) { FluidModifierData *fmd = (FluidModifierData *)link->data; - GPU_free_smoke(fmd); + DRW_smoke_free(fmd); } BLI_freelistN(&wpd->smoke_domains); } diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index ab6ea53261f..956bddfb357 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -197,6 +197,17 @@ void DRW_uniformbuffer_free(struct GPUUniformBuffer *ubo); } while (0) /* Shaders */ + +#ifndef __GPU_MATERIAL_H__ +/* FIXME: Meh avoid including all GPUMaterial. */ +typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat, + int options, + const char **vert_code, + const char **geom_code, + const char **frag_lib, + const char **defines); +#endif + struct GPUShader *DRW_shader_create(const char *vert, const char *geom, const char *frag, @@ -236,7 +247,8 @@ struct GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, const char *geom, const char *frag_lib, const char *defines, - bool deferred); + bool deferred, + GPUMaterialEvalCallbackFn callback); struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, struct Material *ma, struct bNodeTree *ntree, @@ -247,7 +259,8 @@ struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, const char *geom, const char *frag_lib, const char *defines, - bool deferred); + bool deferred, + GPUMaterialEvalCallbackFn callback); void DRW_shader_free(struct GPUShader *shader); #define DRW_SHADER_FREE_SAFE(shader) \ do { \ diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c index 17902f27513..06cedb9f72c 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c @@ -158,6 +158,7 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * const bool has_radius = pointcloud->radius != NULL; static GPUVertFormat format = {0}; + static GPUVertFormat format_no_radius = {0}; static uint pos; if (format.attr_len == 0) { /* initialize vertex format */ @@ -167,11 +168,11 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * * If the vertex shader has more components than the array provides, the extras are given * values from the vector (0, 0, 0, 1) for the missing XYZW components. */ - int comp_len = has_radius ? 4 : 3; - pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, comp_len, GPU_FETCH_FLOAT); + pos = GPU_vertformat_attr_add(&format_no_radius, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } - cache->pos = GPU_vertbuf_create_with_format(&format); + cache->pos = GPU_vertbuf_create_with_format(has_radius ? &format : &format_no_radius); GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint); if (has_radius) { diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index d7bb9b78e09..6060dce47ac 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -26,8 +26,10 @@ struct DRWPass; struct DRWShadingGroup; struct GPUMaterial; struct ModifierData; +struct FluidModifierData; struct Object; struct ParticleSystem; +struct RegionView3D; struct ViewLayer; #define UBO_FIRST_COLOR colorWire @@ -158,14 +160,14 @@ void DRW_globals_update(void); void DRW_globals_free(void); struct DRWView *DRW_view_create_with_zoffset(const struct DRWView *parent_view, - const RegionView3D *rv3d, + const struct RegionView3D *rv3d, float offset); int DRW_object_wire_theme_get(struct Object *ob, struct ViewLayer *view_layer, float **r_color); float *DRW_color_background_blend_get(int theme_id); -bool DRW_object_is_flat(Object *ob, int *r_axis); -bool DRW_object_axis_orthogonal_to_view(Object *ob, int axis); +bool DRW_object_is_flat(struct Object *ob, int *r_axis); +bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); /* draw_hair.c */ @@ -187,6 +189,16 @@ void DRW_hair_init(void); void DRW_hair_update(void); void DRW_hair_free(void); +/* draw_fluid.c */ + +/* Fluid simulation. */ +void DRW_smoke_ensure(struct FluidModifierData *fmd, int highres); +void DRW_smoke_ensure_coba_field(struct FluidModifierData *fmd); +void DRW_smoke_ensure_velocity(struct FluidModifierData *fmd); + +void DRW_smoke_free(struct FluidModifierData *fmd); +void DRW_smoke_free_velocity(struct FluidModifierData *fmd); + /* draw_common.c */ struct DRW_Global { /** If needed, contains all global/Theme colors diff --git a/source/blender/gpu/intern/gpu_draw_smoke.c b/source/blender/draw/intern/draw_fluid.c index e0b94e20574..33311dc698f 100644 --- a/source/blender/gpu/intern/gpu_draw_smoke.c +++ b/source/blender/draw/intern/draw_fluid.c @@ -35,10 +35,10 @@ #include "BKE_colorband.h" -#include "GPU_draw.h" -#include "GPU_glew.h" #include "GPU_texture.h" +#include "draw_common.h" /* Own include. */ + #ifdef WITH_FLUID # include "manta_fluid_API.h" #endif @@ -62,7 +62,8 @@ static void create_flame_spectrum_texture(float *data) # define MAX_FIRE_ALPHA 0.06f # define FULL_ON_FIRE 100 - float *spec_pixels = MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels"); + float *spec_pixels = (float *)MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), + "spec_pixels"); blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000); @@ -105,7 +106,7 @@ static void create_color_ramp(const struct ColorBand *coba, float *data) static GPUTexture *create_transfer_function(int type, const struct ColorBand *coba) { - float *data = MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__); + float *data = (float *)MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__); switch (type) { case TFUNC_FLAME_SPECTRUM: @@ -184,7 +185,7 @@ static GPUTexture *create_field_texture(FluidDomainSettings *fds) } GPUTexture *tex = GPU_texture_create_nD( - fds->res[0], fds->res[1], fds->res[2], 3, field, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); + UNPACK3(fds->res), 3, field, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); swizzle_texture_channel_single(tex); return tex; @@ -203,7 +204,7 @@ static GPUTexture *create_density_texture(FluidDomainSettings *fds, int highres) } GPUTexture *tex = GPU_texture_create_nD( - dim[0], dim[1], dim[2], 3, data, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); + UNPACK3(dim), 3, data, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); swizzle_texture_channel_single(tex); @@ -221,7 +222,7 @@ static GPUTexture *create_color_texture(FluidDomainSettings *fds, int highres) int cell_count = (highres) ? manta_noise_get_cells(fds->fluid) : fds->total_cells; int *dim = (highres) ? fds->res_noise : fds->res; - float *data = MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture"); + float *data = (float *)MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture"); if (data == NULL) { return NULL; @@ -276,7 +277,7 @@ static GPUTexture *create_flame_texture(FluidDomainSettings *fds, int highres) /** \name Public API * \{ */ -void GPU_free_smoke(FluidModifierData *fmd) +void DRW_smoke_free(FluidModifierData *fmd) { if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) { if (fmd->domain->tex_density) { @@ -316,7 +317,7 @@ void GPU_free_smoke(FluidModifierData *fmd) } } -void GPU_create_smoke_coba_field(FluidModifierData *fmd) +void DRW_smoke_ensure_coba_field(FluidModifierData *fmd) { #ifndef WITH_FLUID UNUSED_VARS(fmd); @@ -334,7 +335,7 @@ void GPU_create_smoke_coba_field(FluidModifierData *fmd) #endif } -void GPU_create_smoke(FluidModifierData *fmd, int highres) +void DRW_smoke_ensure(FluidModifierData *fmd, int highres) { #ifndef WITH_FLUID UNUSED_VARS(fmd, highres); @@ -355,9 +356,7 @@ void GPU_create_smoke(FluidModifierData *fmd, int highres) fds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL); } if (!fds->tex_shadow) { - fds->tex_shadow = GPU_texture_create_nD(fds->res[0], - fds->res[1], - fds->res[2], + fds->tex_shadow = GPU_texture_create_nD(UNPACK3(fds->res), 3, manta_smoke_get_shadow(fds->fluid), GPU_R8, @@ -370,7 +369,7 @@ void GPU_create_smoke(FluidModifierData *fmd, int highres) #endif /* WITH_FLUID */ } -void GPU_create_smoke_velocity(FluidModifierData *fmd) +void DRW_smoke_ensure_velocity(FluidModifierData *fmd) { #ifndef WITH_FLUID UNUSED_VARS(fmd); @@ -387,19 +386,16 @@ void GPU_create_smoke_velocity(FluidModifierData *fmd) } if (!fds->tex_velocity_x) { - fds->tex_velocity_x = GPU_texture_create_3d( - fds->res[0], fds->res[1], fds->res[2], GPU_R16F, vel_x, NULL); - fds->tex_velocity_y = GPU_texture_create_3d( - fds->res[0], fds->res[1], fds->res[2], GPU_R16F, vel_y, NULL); - fds->tex_velocity_z = GPU_texture_create_3d( - fds->res[0], fds->res[1], fds->res[2], GPU_R16F, vel_z, NULL); + fds->tex_velocity_x = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_x, NULL); + fds->tex_velocity_y = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_y, NULL); + fds->tex_velocity_z = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_z, NULL); } } #endif /* WITH_FLUID */ } -/* TODO Unify with the other GPU_free_smoke. */ -void GPU_free_smoke_velocity(FluidModifierData *fmd) +/* TODO Unify with the other DRW_smoke_free. */ +void DRW_smoke_free_velocity(FluidModifierData *fmd) { if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) { if (fmd->domain->tex_velocity_x) { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 2beab021cfb..656a62dffb5 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2723,7 +2723,7 @@ void DRW_engines_free(void) void DRW_render_context_enable(Render *render) { if (G.background && DST.gl_context == NULL) { - WM_init_opengl(G_MAIN); + WM_init_opengl(); } if (GPU_use_main_context_workaround()) { @@ -2854,7 +2854,7 @@ void DRW_opengl_context_disable_ex(bool restore) void DRW_opengl_context_enable(void) { if (G.background && DST.gl_context == NULL) { - WM_init_opengl(G_MAIN); + WM_init_opengl(); } DRW_opengl_context_enable_ex(true); } diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 9d8050504ab..3184d6155d2 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1292,13 +1292,10 @@ static DRWShadingGroup *drw_shgroup_material_create_ex(GPUPass *gpupass, DRWPass } static void drw_shgroup_material_texture(DRWShadingGroup *grp, - GPUMaterialTexture *tex, + GPUTexture *gputex, const char *name, - eGPUSamplerState state, - int textarget) + eGPUSamplerState state) { - GPUTexture *gputex = GPU_texture_from_blender(tex->ima, tex->iuser, NULL, textarget); - DRW_shgroup_uniform_texture_ex(grp, name, gputex, state); GPUTexture **gputex_ref = BLI_memblock_alloc(DST.vmempool->images); @@ -1314,15 +1311,16 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial LISTBASE_FOREACH (GPUMaterialTexture *, tex, &textures) { if (tex->ima) { /* Image */ + GPUTexture *gputex; if (tex->tiled_mapping_name[0]) { - drw_shgroup_material_texture( - grp, tex, tex->sampler_name, tex->sampler_state, GL_TEXTURE_2D_ARRAY); - drw_shgroup_material_texture( - grp, tex, tex->tiled_mapping_name, tex->sampler_state, GL_TEXTURE_1D_ARRAY); + gputex = BKE_image_get_gpu_tiles(tex->ima, tex->iuser, NULL); + drw_shgroup_material_texture(grp, gputex, tex->sampler_name, tex->sampler_state); + gputex = BKE_image_get_gpu_tilemap(tex->ima, tex->iuser, NULL); + drw_shgroup_material_texture(grp, gputex, tex->tiled_mapping_name, tex->sampler_state); } else { - drw_shgroup_material_texture( - grp, tex, tex->sampler_name, tex->sampler_state, GL_TEXTURE_2D); + gputex = BKE_image_get_gpu_texture(tex->ima, tex->iuser, NULL); + drw_shgroup_material_texture(grp, gputex, tex->sampler_name, tex->sampler_state); } } else if (tex->colorband) { diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index b6f51ada5a1..91cbc03e5a4 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -446,6 +446,7 @@ void DRW_state_reset(void) DRW_state_reset_ex(DRW_STATE_DEFAULT); GPU_texture_unbind_all(); + GPU_uniformbuffer_unbind_all(); /* Should stay constant during the whole rendering. */ GPU_point_size(5); @@ -669,8 +670,7 @@ BLI_INLINE void draw_geometry_bind(DRWShadingGroup *shgroup, GPUBatch *geom) DST.batch = geom; - GPU_batch_program_set_no_use( - geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader)); + GPU_batch_set_shader_no_bind(geom, shgroup->shader); geom->program_in_use = true; /* XXX hacking #GPUBatch */ @@ -773,10 +773,11 @@ static bool ubo_bindings_validate(DRWShadingGroup *shgroup) DRWPass *parent_pass = DRW_memblock_elem_from_handle(DST.vmempool->passes, &shgroup->pass_handle); - printf("Pass : %s, Shader : %s, Block : %s\n", + printf("Pass : %s, Shader : %s, Block : %s, Binding %d\n", parent_pass->name, shgroup->shader->name, - blockname); + blockname, + binding); } } # endif @@ -1106,6 +1107,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) /* Unbinding can be costly. Skip in normal condition. */ if (G.debug & G_DEBUG_GPU) { GPU_texture_unbind_all(); + GPU_uniformbuffer_unbind_all(); } } GPU_shader_bind(shgroup->shader); diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 34069438e47..1c260721efb 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -447,7 +447,8 @@ GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, const char *geom, const char *frag_lib, const char *defines, - bool deferred) + bool deferred, + GPUMaterialEvalCallbackFn callback) { GPUMaterial *mat = NULL; if (DRW_state_is_image_render() || !deferred) { @@ -467,7 +468,8 @@ GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, geom, frag_lib, defines, - wo->id.name); + wo->id.name, + callback); } if (GPU_material_status(mat) == GPU_MAT_QUEUED) { @@ -487,7 +489,8 @@ GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, const char *geom, const char *frag_lib, const char *defines, - bool deferred) + bool deferred, + GPUMaterialEvalCallbackFn callback) { GPUMaterial *mat = NULL; if (DRW_state_is_image_render() || !deferred) { @@ -507,7 +510,8 @@ GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, geom, frag_lib, defines, - ma->id.name); + ma->id.name, + callback); } if (GPU_material_status(mat) == GPU_MAT_QUEUED) { diff --git a/source/blender/draw/intern/draw_select_buffer.c b/source/blender/draw/intern/draw_select_buffer.c index 84c8d0f861f..ee5561e1e38 100644 --- a/source/blender/draw/intern/draw_select_buffer.c +++ b/source/blender/draw/intern/draw_select_buffer.c @@ -395,7 +395,7 @@ uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph, int center_x = width / 2; int center_y = height / 2; - /* Manhatten distance in keeping with other screen-based selection. */ + /* Manhattan distance in keeping with other screen-based selection. */ *dist = (uint)(abs(hit_x - center_x) + abs(hit_y - center_y)); /* Indices start at 1 here. */ diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index 3c470f802ec..b42700b2c7e 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -184,8 +184,7 @@ void DRW_draw_cursor(void) GPUBatch *cursor_batch = DRW_cache_cursor_get(is_aligned); GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR); - GPU_batch_program_set( - cursor_batch, GPU_shader_get_program(shader), GPU_shader_get_interface(shader)); + GPU_batch_set_shader(cursor_batch, shader); GPU_batch_draw(cursor_batch); diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index ffff631e34b..8684d82f228 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -95,7 +95,7 @@ void hair_get_interp_attrs( * For final drawing, the vertex index and the number of vertex per segment */ -#ifndef HAIR_PHASE_SUBDIV +#if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER) int hair_get_strand_id(void) { return gl_VertexID / (hairStrandsRes * hairThicknessRes); @@ -206,4 +206,24 @@ vec3 hair_get_strand_pos(void) return texelFetch(hairPointBuffer, id).point_position; } +vec2 hair_get_barycentric(void) +{ + /* To match cycles without breaking into individual segment we encode if we need to invert + * the first component into the second component. We invert if the barycentricTexCo.y + * is NOT 0.0 or 1.0. */ + int id = hair_get_base_id(); + return vec2(float((id % 2) == 1), float(((id % 4) % 3) > 0)); +} + #endif + +/* To be fed the result of hair_get_barycentric from vertex shader. */ +vec2 hair_resolve_barycentric(vec2 vert_barycentric) +{ + if (fract(vert_barycentric.y) != 0.0) { + return vec2(vert_barycentric.x, 0.0); + } + else { + return vec2(1.0 - vert_barycentric.x, 0.0); + } +} diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl index e337376d7c4..643d7e7d942 100644 --- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl @@ -116,4 +116,4 @@ vec3 normal_decode(vec2 enc, vec3 view) return n; } -/** \} */
\ No newline at end of file +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl index 36b67f2bd60..625e8bb1ff8 100644 --- a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl +++ b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl @@ -36,4 +36,4 @@ vec3 pointcloud_get_pos(void) vec3 outpos, outnor; pointcloud_get_pos_and_nor(outpos, outnor); return outpos; -}
\ No newline at end of file +} diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 095bc64a19e..a55d2cd8c1a 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -22,6 +22,16 @@ layout(std140) uniform viewBlock vec4 CameraTexCoFactors; }; +#define ViewNear (ViewVecs[0].w) +#define ViewFar (ViewVecs[1].w) + +#define cameraForward ViewMatrixInverse[2].xyz +#define cameraPos ViewMatrixInverse[3].xyz +#define cameraVec \ + ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - worldPosition) : cameraForward) +#define viewCameraVec \ + ((ProjectionMatrix[3][3] == 0.0) ? normalize(-viewPosition) : vec3(0.0, 0.0, 1.0)) + #ifdef world_clip_planes_calc_clip_distance # undef world_clip_planes_calc_clip_distance # define world_clip_planes_calc_clip_distance(p) \ @@ -104,10 +114,13 @@ uniform int resourceId; /* Use this to declare and pass the value if * the fragment shader uses the resource_id. */ -# define RESOURCE_ID_VARYING flat out int resourceIDFrag; -# define RESOURCE_ID_VARYING_GEOM flat out int resourceIDGeom; -# define PASS_RESOURCE_ID resourceIDFrag = resource_id; -# define PASS_RESOURCE_ID_GEOM resourceIDGeom = resource_id; +# ifdef USE_GEOMETRY_SHADER +# define RESOURCE_ID_VARYING flat out int resourceIDGeom; +# define PASS_RESOURCE_ID resourceIDGeom = resource_id; +# else +# define RESOURCE_ID_VARYING flat out int resourceIDFrag; +# define PASS_RESOURCE_ID resourceIDFrag = resource_id; +# endif #endif /* If used in a fragment / geometry shader, we pass @@ -118,7 +131,7 @@ uniform int resourceId; flat in int resourceIDGeom[]; # define resource_id resourceIDGeom -# define PASS_RESOURCE_ID(i) resourceIDFrag = resource_id[i]; +# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0]; #endif #ifdef GPU_FRAGMENT_SHADER @@ -171,9 +184,12 @@ uniform mat4 ModelMatrixInverse; * Note: This is only valid because we are only using the mat3 of the ViewMatrixInverse. * ViewMatrix * transpose(ModelMatrixInverse) **/ -#define normal_object_to_view(n) (mat3(ViewMatrix) * (transpose(mat3(ModelMatrixInverse)) * n)) -#define normal_object_to_world(n) (transpose(mat3(ModelMatrixInverse)) * n) -#define normal_world_to_object(n) (transpose(mat3(ModelMatrix)) * n) +#define NormalMatrix transpose(mat3(ModelMatrixInverse)) +#define NormalMatrixInverse transpose(mat3(ModelMatrix)) + +#define normal_object_to_view(n) (mat3(ViewMatrix) * (NormalMatrix * n)) +#define normal_object_to_world(n) (NormalMatrix * n) +#define normal_world_to_object(n) (NormalMatrixInverse * n) #define normal_world_to_view(n) (mat3(ViewMatrix) * n) #define normal_view_to_world(n) (mat3(ViewMatrixInverse) * n) @@ -199,3 +215,78 @@ uniform mat4 ModelMatrixInverse; #define DRW_BASE_FROM_DUPLI (1 << 2) #define DRW_BASE_FROM_SET (1 << 3) #define DRW_BASE_ACTIVE (1 << 4) + +/* ---- Opengl Depth conversion ---- */ + +float linear_depth(bool is_persp, float z, float zf, float zn) +{ + if (is_persp) { + return (zn * zf) / (z * (zn - zf) + zf); + } + else { + return (z * 2.0 - 1.0) * zf; + } +} + +float buffer_depth(bool is_persp, float z, float zf, float zn) +{ + if (is_persp) { + return (zf * (zn - z)) / (z * (zn - zf)); + } + else { + return (z / (zf * 2.0)) + 0.5; + } +} + +float get_view_z_from_depth(float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = 2.0 * depth - 1.0; + return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); + } + else { + return ViewVecs[0].z + depth * ViewVecs[1].z; + } +} + +float get_depth_from_view_z(float z) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2]; + return d * 0.5 + 0.5; + } + else { + return (z - ViewVecs[0].z) / ViewVecs[1].z; + } +} + +vec2 get_uvs_from_view(vec3 view) +{ + vec4 ndc = ProjectionMatrix * vec4(view, 1.0); + return (ndc.xy / ndc.w) * 0.5 + 0.5; +} + +vec3 get_view_space_from_depth(vec2 uvcoords, float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + return vec3(ViewVecs[0].xy + uvcoords * ViewVecs[1].xy, 1.0) * get_view_z_from_depth(depth); + } + else { + return ViewVecs[0].xyz + vec3(uvcoords, depth) * ViewVecs[1].xyz; + } +} + +vec3 get_world_space_from_depth(vec2 uvcoords, float depth) +{ + return (ViewMatrixInverse * vec4(get_view_space_from_depth(uvcoords, depth), 1.0)).xyz; +} + +vec3 get_view_vector_from_screen_uv(vec2 uv) +{ + if (ProjectionMatrix[3][3] == 0.0) { + return normalize(vec3(ViewVecs[0].xy + uv * ViewVecs[1].xy, 1.0)); + } + else { + return vec3(0.0, 0.0, 1.0); + } +} diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 50733afe6fb..8280b58c21a 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -427,7 +427,7 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac) ac->spacetype = (area) ? area->spacetype : 0; ac->regiontype = (region) ? region->regiontype : 0; - /* initialise default y-scale factor */ + /* Initialize default y-scale factor. */ animedit_get_yscale_factor(ac); /* get data context info */ diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 8df9c99896e..e60270bc3f0 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -251,7 +251,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) */ BLI_dlrbTree_init(&pso->keys); - /* initialise numeric input */ + /* Initialize numeric input. */ initNumInput(&pso->num); pso->num.idx_max = 0; /* one axis */ pso->num.val_flag[0] |= NUM_NO_NEGATIVE; @@ -1310,7 +1310,7 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ @@ -1370,7 +1370,7 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ @@ -1429,7 +1429,7 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ @@ -1489,7 +1489,7 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ @@ -1549,7 +1549,7 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 91a8ea0fa3a..72ea52314b9 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1137,7 +1137,7 @@ int ED_curve_updateAnimPaths(Main *bmain, Curve *cu) /** \name Edit Mode Conversion (Make & Load) * \{ */ -static int *initialize_index_map(Object *obedit, int *r_old_totvert) +static int *init_index_map(Object *obedit, int *r_old_totvert) { Curve *curve = (Curve *)obedit->data; EditNurb *editnurb = curve->editnurb; @@ -1225,7 +1225,7 @@ static void remap_hooks_and_vertex_parents(Main *bmain, Object *obedit) if ((object->parent) && (object->parent->data == curve) && ELEM(object->partype, PARVERT1, PARVERT3)) { if (old_to_new_map == NULL) { - old_to_new_map = initialize_index_map(obedit, &old_totvert); + old_to_new_map = init_index_map(obedit, &old_totvert); } if (object->par1 < old_totvert) { @@ -1254,7 +1254,7 @@ static void remap_hooks_and_vertex_parents(Main *bmain, Object *obedit) int i, j; if (old_to_new_map == NULL) { - old_to_new_map = initialize_index_map(obedit, &old_totvert); + old_to_new_map = init_index_map(obedit, &old_totvert); } for (i = j = 0; i < hmd->totindex; i++) { diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index b759277572c..d78c543f94b 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -76,9 +76,9 @@ static int kill_selection(Object *obedit, int ins); /** \name Internal Utilities * \{ */ -static wchar_t findaccent(wchar_t char1, uint code) +static char32_t findaccent(char32_t char1, uint code) { - wchar_t new = 0; + char32_t new = 0; if (char1 == 'a') { if (code == '`') { @@ -682,7 +682,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const cu->strinfo = MEM_callocN((nchars + 4) * sizeof(CharInfo), "strinfo"); cu->len = 0; - cu->len_wchar = nchars - 1; + cu->len_char32 = nchars - 1; cu->pos = 0; s = cu->str; @@ -703,7 +703,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const } } - cu->pos = cu->len_wchar; + cu->pos = cu->len_char32; *s = '\0'; WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, obedit); @@ -1661,7 +1661,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) uintptr_t ascii = event->ascii; int alt = event->alt, shift = event->shift, ctrl = event->ctrl; int event_type = event->type, event_val = event->val; - wchar_t inserted_text[2] = {0}; + char32_t inserted_text[2] = {0}; if (RNA_struct_property_is_set(op->ptr, "text")) { return insert_text_exec(C, op); @@ -1733,7 +1733,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* store as utf8 in RNA string */ char inserted_utf8[8] = {0}; - BLI_strncpy_wchar_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8)); + BLI_str_utf32_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8)); RNA_string_set(op->ptr, "text", inserted_utf8); } @@ -1867,7 +1867,7 @@ void ED_curve_editfont_make(Object *obedit) { Curve *cu = obedit->data; EditFont *ef = cu->editfont; - int len_wchar; + int len_char32; if (ef == NULL) { ef = cu->editfont = MEM_callocN(sizeof(EditFont), "editfont"); @@ -1876,10 +1876,10 @@ void ED_curve_editfont_make(Object *obedit) ef->textbufinfo = MEM_callocN((MAXTEXT + 4) * sizeof(CharInfo), "texteditbufinfo"); } - /* Convert the original text to wchar_t */ - len_wchar = BLI_str_utf8_as_utf32(ef->textbuf, cu->str, MAXTEXT + 4); - BLI_assert(len_wchar == cu->len_wchar); - ef->len = len_wchar; + /* Convert the original text to chat32_t. */ + len_char32 = BLI_str_utf8_as_utf32(ef->textbuf, cu->str, MAXTEXT + 4); + BLI_assert(len_char32 == cu->len_char32); + ef->len = len_char32; BLI_assert(ef->len >= 0); memcpy(ef->textbufinfo, cu->strinfo, ef->len * sizeof(CharInfo)); @@ -1908,7 +1908,7 @@ void ED_curve_editfont_load(Object *obedit) MEM_freeN(cu->str); /* Calculate the actual string length in UTF-8 variable characters */ - cu->len_wchar = ef->len; + cu->len_char32 = ef->len; cu->len = BLI_str_utf32_as_utf8_len(ef->textbuf); /* Alloc memory for UTF-8 variable char length string */ @@ -1920,8 +1920,8 @@ void ED_curve_editfont_load(Object *obedit) if (cu->strinfo) { MEM_freeN(cu->strinfo); } - cu->strinfo = MEM_callocN((cu->len_wchar + 4) * sizeof(CharInfo), "texteditinfo"); - memcpy(cu->strinfo, ef->textbufinfo, cu->len_wchar * sizeof(CharInfo)); + cu->strinfo = MEM_callocN((cu->len_char32 + 4) * sizeof(CharInfo), "texteditinfo"); + memcpy(cu->strinfo, ef->textbufinfo, cu->len_char32 * sizeof(CharInfo)); /* Other vars */ cu->pos = ef->pos; diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index 71a364c60d7..033673a99a8 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -29,7 +29,6 @@ #include "ED_view3d.h" #include "GPU_batch.h" -#include "GPU_glew.h" #include "GPU_immediate.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index 85f84af5f14..406d66dfd8f 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -999,7 +999,7 @@ static int gizmo_cage2d_modal(bContext *C, if (data->dial == NULL) { MUL_V2_V3_M4_FINAL(test_co, data->orig_matrix_offset[3]); - data->dial = BLI_dial_initialize(test_co, FLT_EPSILON); + data->dial = BLI_dial_init(test_co, FLT_EPSILON); MUL_V2_V3_M4_FINAL(test_co, data->orig_mouse); BLI_dial_angle(data->dial, test_co); diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 6f700f6c4b8..ab83abb6b37 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -200,7 +200,7 @@ typedef struct tGPsdata { /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX (U.gp_manhattendist) +#define MIN_MANHATTAN_PX (U.gp_manhattandist) /* minimum length of new segment before new point can be added */ #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) @@ -297,7 +297,7 @@ static bool annotation_stroke_filtermval(tGPsdata *p, const float mval[2], float return false; } - if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) { + if ((dx > MIN_MANHATTAN_PX) && (dy > MIN_MANHATTAN_PX)) { return true; } @@ -603,7 +603,7 @@ static short annotation_stroke_addpoint(tGPsdata *p, /* store settings */ copy_v2_v2(&pt->x, mval); pt->pressure = pressure; - /* unused for annotations, but initialise for easier conversions to GP Object */ + /* Unused for annotations, but initialize for easier conversions to GP Object. */ pt->strength = 1.0f; /* point time */ diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 96b0296a7de..348fb614977 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -861,6 +861,154 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot) INT_MAX); } +/* ********************* Clean Duplicated Frames ************************** */ +static bool gpencil_frame_is_equal(bGPDframe *gpf_a, bGPDframe *gpf_b) +{ + if ((gpf_a == NULL) || (gpf_b == NULL)) { + return false; + } + /* If the number of strokes is different, cannot be equal. */ + int totstrokes_a = BLI_listbase_count(&gpf_a->strokes); + int totstrokes_b = BLI_listbase_count(&gpf_b->strokes); + if ((totstrokes_a == 0) || (totstrokes_b == 0) || (totstrokes_a != totstrokes_b)) { + return false; + } + /* Loop all strokes and check. */ + bGPDstroke *gps_a = gpf_a->strokes.first; + bGPDstroke *gps_b = gpf_b->strokes.first; + for (int i = 0; i < totstrokes_a; i++) { + /* If the number of points is different, cannot be equal. */ + if (gps_a->totpoints != gps_b->totpoints) { + return false; + } + /* Check other variables. */ + if (!equals_v4v4(gps_a->vert_color_fill, gps_b->vert_color_fill)) { + return false; + } + if (gps_a->thickness != gps_b->thickness) { + return false; + } + if (gps_a->mat_nr != gps_b->mat_nr) { + return false; + } + if (gps_a->caps[0] != gps_b->caps[0]) { + return false; + } + if (gps_a->caps[1] != gps_b->caps[1]) { + return false; + } + if (gps_a->hardeness != gps_b->hardeness) { + return false; + } + if (!equals_v2v2(gps_a->aspect_ratio, gps_b->aspect_ratio)) { + return false; + } + if (gps_a->uv_rotation != gps_b->uv_rotation) { + return false; + } + if (!equals_v2v2(gps_a->uv_translation, gps_b->uv_translation)) { + return false; + } + if (gps_a->uv_scale != gps_b->uv_scale) { + return false; + } + + /* Loop points and check if equals or not. */ + for (int p = 0; p < gps_a->totpoints; p++) { + bGPDspoint *pt_a = &gps_a->points[p]; + bGPDspoint *pt_b = &gps_b->points[p]; + if (!equals_v3v3(&pt_a->x, &pt_b->x)) { + return false; + } + if (pt_a->pressure != pt_b->pressure) { + return false; + } + if (pt_a->strength != pt_b->strength) { + return false; + } + if (pt_a->uv_fac != pt_b->uv_fac) { + return false; + } + if (pt_a->uv_rot != pt_b->uv_rot) { + return false; + } + if (!equals_v4v4(pt_a->vert_color, pt_b->vert_color)) { + return false; + } + } + + /* Look at next pair of strokes. */ + gps_a = gps_a->next; + gps_b = gps_b->next; + } + + return true; +} + +static int gpencil_frame_clean_duplicate_exec(bContext *C, wmOperator *op) +{ +#define SELECTED 1 + + bool changed = false; + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + const int type = RNA_enum_get(op->ptr, "type"); + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Only editable and visible layers are considered. */ + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->frames.first != NULL)) { + bGPDframe *gpf = gpl->frames.first; + + if ((type == SELECTED) && ((gpf->flag & GP_FRAME_SELECT) == 0)) { + continue; + } + + while (gpf != NULL) { + if (gpencil_frame_is_equal(gpf, gpf->next)) { + /* Remove frame. */ + BKE_gpencil_layer_frame_delete(gpl, gpf->next); + /* Tag for recalc. */ + changed = true; + } + else { + gpf = gpf->next; + } + } + } + } + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_frame_clean_duplicate(wmOperatorType *ot) +{ + static const EnumPropertyItem clean_type[] = { + {0, "ALL", 0, "All Frames", ""}, + {1, "SELECTED", 0, "Selected Frames", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Clean Duplicated Frames"; + ot->idname = "GPENCIL_OT_frame_clean_duplicate"; + ot->description = "Remove any duplicated frame"; + + /* callbacks */ + ot->exec = gpencil_frame_clean_duplicate_exec; + ot->poll = gpencil_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", clean_type, 0, "Type", ""); +} + /* *********************** Hide Layers ******************************** */ static int gpencil_hide_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 286efeeff01..9658dc04b52 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -94,6 +94,8 @@ /** \name Stroke Edit Mode Management * \{ */ +static void gpencil_flip_stroke(bGPDstroke *gps); + /* poll callback for all stroke editing operators */ static bool gpencil_stroke_edit_poll(bContext *C) { @@ -1126,6 +1128,11 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps) pt->flag |= GP_SPOINT_SELECT; } + /* Flip stroke if it was only one point to consider extrude point as last point. */ + if (gps->totpoints == 2) { + gpencil_flip_stroke(gps); + } + /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gps); @@ -1764,7 +1771,7 @@ static int gpencil_blank_frame_add_exec(bContext *C, wmOperator *op) const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); - /* Initialise datablock and an active layer if nothing exists yet */ + /* Initialize data-block and an active layer if nothing exists yet. */ if (ELEM(NULL, gpd, active_gpl)) { /* Let's just be lazy, and call the "Add New Layer" operator, * which sets everything up as required. */ @@ -4283,14 +4290,14 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_prev, dupflag); ob_dst = base_new->object; ob_dst->mode = OB_MODE_OBJECT; - /* Duplication will increment bGPdata usercount, but since we create a new greasepencil datablock - * for ob_dst (which gets its own user automatically), we have to decrement the usercount again. - */ + /* Duplication will increment #bGPdata user-count, but since we create a new grease-pencil + * data-block for ob_dst (which gets its own user automatically), + * we have to decrement the user-count again. */ gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2); id_us_min(ob_dst->data); ob_dst->data = (bGPdata *)gpd_dst; - /* loop old datablock and separate parts */ + /* Loop old data-block and separate parts. */ if ((mode == GP_SEPARATE_POINT) || (mode == GP_SEPARATE_STROKE)) { CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { gpl_dst = NULL; diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 517225e5a81..7db25ef817b 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -104,6 +104,8 @@ typedef struct tGPDfill { struct bGPdata *gpd; /** current material */ struct Material *mat; + /** current brush */ + struct Brush *brush; /** layer */ struct bGPDlayer *gpl; /** frame */ @@ -233,6 +235,8 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) Object *ob = tgpf->ob; bGPdata *gpd = tgpf->gpd; + Brush *brush = tgpf->brush; + BrushGpencilSettings *brush_settings = brush->gpencil_settings; tGPDdraw tgpw; tgpw.rv3d = tgpf->rv3d; @@ -249,6 +253,12 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) GPU_blend(true); + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); + BLI_assert(gpl_active != NULL); + + const int gpl_active_index = BLI_findindex(&gpd->layers, gpl_active); + BLI_assert(gpl_active_index >= 0); + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* calculate parent position */ BKE_gpencil_parent_matrix_get(tgpw.depsgraph, ob, gpl, tgpw.diff_mat); @@ -258,6 +268,44 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) continue; } + /* Decide if layer is included or not depending of the layer mode. */ + const int gpl_index = BLI_findindex(&gpd->layers, gpl); + switch (brush_settings->fill_layer_mode) { + case GP_FILL_GPLMODE_ACTIVE: { + if (gpl_index != gpl_active_index) { + continue; + } + break; + } + case GP_FILL_GPLMODE_ABOVE: { + if (gpl_index != gpl_active_index + 1) { + continue; + } + break; + } + case GP_FILL_GPLMODE_BELOW: { + if (gpl_index != gpl_active_index - 1) { + continue; + } + break; + } + case GP_FILL_GPLMODE_ALL_ABOVE: { + if (gpl_index <= gpl_active_index) { + continue; + } + break; + } + case GP_FILL_GPLMODE_ALL_BELOW: { + if (gpl_index >= gpl_active_index) { + continue; + } + break; + } + case GP_FILL_GPLMODE_VISIBLE: + default: + break; + } + /* if active layer and no keyframe, create a new one */ if (gpl == tgpf->gpl) { if ((gpl->actframe == NULL) || (gpl->actframe->framenum != tgpf->active_cfra)) { @@ -416,10 +464,10 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) /* create a image to see result of template */ if (ibuf->rect_float) { - GPU_offscreen_read_pixels(offscreen, GL_FLOAT, ibuf->rect_float); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, ibuf->rect_float); } else if (ibuf->rect) { - GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, ibuf->rect); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_UNSIGNED_BYTE, ibuf->rect); } if (ibuf->rect_float && ibuf->rect) { IMB_rect_from_float(ibuf); @@ -1247,6 +1295,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *UNUSED(op)) /* save filling parameters */ Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); + tgpf->brush = brush; tgpf->flag = brush->gpencil_settings->flag; tgpf->fill_leak = brush->gpencil_settings->fill_leak; tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 66ac7948596..fa75c2a7cee 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -484,6 +484,7 @@ void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); void GPENCIL_OT_frame_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot); void GPENCIL_OT_frame_clean_loose(struct wmOperatorType *ot); +void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index efee05f7da3..3892f451e18 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -600,6 +600,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_frame_duplicate); WM_operatortype_append(GPENCIL_OT_frame_clean_fill); WM_operatortype_append(GPENCIL_OT_frame_clean_loose); + WM_operatortype_append(GPENCIL_OT_frame_clean_duplicate); WM_operatortype_append(GPENCIL_OT_convert); WM_operatortype_append(GPENCIL_OT_bake_mesh_animation); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 1880045e238..9ec04bbb553 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -268,7 +268,7 @@ typedef struct tGPsdata { /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX (U.gp_manhattendist) +#define MIN_MANHATTEN_PX (U.gp_manhattandist) /* minimum length of new segment before new point can be added */ #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) @@ -1817,15 +1817,15 @@ static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p) changed = true; } /* Be sure curves are initializated. */ - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_sensitivity); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_strength); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_jitter); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_pressure); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_strength); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_uv); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_hue); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_saturation); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_value); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_sensitivity); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_strength); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_jitter); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_pressure); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_strength); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_uv); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_hue); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_saturation); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_value); /* assign to temp tGPsdata */ p->brush = paint->brush; diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 20f959e2e2c..f44dbd1a752 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -696,7 +696,6 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) && (tgpi->rv3d->persp == RV3D_CAMOB) && (!is_depth); - const bool is_vertex_stroke = GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush); if (tgpi->type == GP_STROKE_BOX) { gps->totpoints = (tgpi->tot_edges * 4 + tgpi->tot_stored_edges); @@ -742,13 +741,13 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) gpencil_session_validatebuffer(tgpi); gpencil_init_colors(tgpi); if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_primitive); + BKE_curvemapping_init(ts->gp_sculpt.cur_primitive); } if (brush_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { - BKE_curvemapping_initialize(brush_settings->curve_jitter); + BKE_curvemapping_init(brush_settings->curve_jitter); } if (brush_settings->flag & GP_BRUSH_USE_STRENGTH_PRESSURE) { - BKE_curvemapping_initialize(brush_settings->curve_strength); + BKE_curvemapping_init(brush_settings->curve_strength); } /* get an array of depths, far depths are blended */ @@ -1021,12 +1020,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) pt->time = 0.0f; pt->flag = 0; pt->uv_fac = tpt->uv_fac; - if (is_vertex_stroke) { - copy_v4_v4(pt->vert_color, tpt->vert_color); - } - else { - zero_v4(pt->vert_color); - } + ED_gpencil_point_vertex_color_set(ts, brush, pt, tpt); if (gps->dvert != NULL) { MDeformVert *dvert = &gps->dvert[i]; @@ -1092,7 +1086,7 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive gpencil_primitive_update_strokes(C, tgpi); } -/* Initialise mouse points */ +/* Initialize mouse points. */ static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event) { copy_v2fl_v2i(tgpi->mval, event->mval); diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 20eeab65623..43c8b766c52 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -444,7 +444,9 @@ typedef struct tGPSB_Grab_StrokeData { int size; } tGPSB_Grab_StrokeData; -/* initialise custom data for handling this stroke */ +/** + * Initialize custom data for handling this stroke. + */ static void gpencil_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) { tGPSB_Grab_StrokeData *data = NULL; @@ -910,13 +912,13 @@ typedef struct tGPSB_CloneBrushData { GHash *new_colors; } tGPSB_CloneBrushData; -/* Initialise "clone" brush data */ +/* Initialize "clone" brush data. */ static void gpencil_brush_clone_init(bContext *C, tGP_BrushEditData *gso) { tGPSB_CloneBrushData *data; bGPDstroke *gps; - /* init custom data */ + /* Initialize custom-data. */ gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData"); /* compute midpoint of strokes on clipboard */ @@ -1188,7 +1190,7 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) Paint *paint = &ts->gp_sculptpaint->paint; gso->brush = paint->brush; - BKE_curvemapping_initialize(gso->brush->curve); + BKE_curvemapping_init(gso->brush->curve); /* save mask */ gso->mask = ts->gpencil_selectmode_sculpt; @@ -1200,10 +1202,10 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) /* Init multi-edit falloff curve data before doing anything, * so we won't have to do it again later. */ if (gso->is_multiframe) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); } - /* initialise custom data for brushes */ + /* Initialize custom data for brushes. */ char tool = gso->brush->gpencil_sculpt_tool; switch (tool) { case GPSCULPT_TOOL_CLONE: { @@ -1229,13 +1231,13 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) op->customdata = NULL; return false; } - /* initialise customdata */ + /* Initialize custom-data. */ gpencil_brush_clone_init(C, gso); break; } case GPSCULPT_TOOL_GRAB: { - /* initialise the cache needed for this brush */ + /* Initialize the cache needed for this brush. */ gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash"); break; } @@ -1936,7 +1938,7 @@ static int gpencil_sculpt_brush_invoke(bContext *C, wmOperator *op, const wmEven gso = op->customdata; - /* initialise type-specific data (used for the entire session) */ + /* Initialize type-specific data (used for the entire session). */ char tool = gso->brush->gpencil_sculpt_tool; switch (tool) { /* Brushes requiring timer... */ diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index f77bb394567..f2ccbd6d2cf 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -1500,7 +1500,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) if (ts->gp_sculpt.cur_falloff == NULL) { ts->gp_sculpt.cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_falloff_curve = ts->gp_sculpt.cur_falloff; - BKE_curvemapping_initialize(gp_falloff_curve); + BKE_curvemapping_init(gp_falloff_curve); BKE_curvemap_reset(gp_falloff_curve->cm, &gp_falloff_curve->clipr, CURVE_PRESET_GAUSS, @@ -2688,7 +2688,11 @@ void ED_gpencil_tag_scene_gpencil(Scene *scene) void ED_gpencil_fill_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDstroke *gps) { - if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) { + const bool is_vertex = (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) && + (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) || + (!GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) && + (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR)); + if (is_vertex) { copy_v3_v3(gps->vert_color_fill, brush->rgb); gps->vert_color_fill[3] = brush->gpencil_settings->vertex_factor; srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill); @@ -2703,7 +2707,12 @@ void ED_gpencil_point_vertex_color_set(ToolSettings *ts, bGPDspoint *pt, tGPspoint *tpt) { - if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) { + const bool is_vertex = (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) && + (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) || + (!GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) && + (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR)); + + if (is_vertex) { if (tpt == NULL) { copy_v3_v3(pt->vert_color, brush->rgb); pt->vert_color[3] = brush->gpencil_settings->vertex_factor; @@ -2859,6 +2868,18 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph, bGPdata *gpd_eval = (bGPdata *)ob_eval->data; MaterialGPencilStyle *gp_style = material->gp_style; + const bool is_vertex_fill = + (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) && + (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) || + (!GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) && + (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR)); + + const bool is_vertex_stroke = + (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) && + (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) || + (!GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) && + (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR)); + int idx = gpd->runtime.sbuffer_used; tGPspoint *tpt = (tGPspoint *)gpd->runtime.sbuffer + idx; @@ -2868,14 +2889,14 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph, srgb_to_linearrgb_v4(vertex_color, vertex_color); /* Copy fill vertex color. */ - if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) { + if (is_vertex_fill) { copy_v4_v4(gpd->runtime.vert_color_fill, vertex_color); } else { copy_v4_v4(gpd->runtime.vert_color_fill, gp_style->fill_rgba); } /* Copy stroke vertex color. */ - if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) { + if (is_vertex_stroke) { copy_v4_v4(tpt->vert_color, vertex_color); } else { diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index c36bc4388d7..b0dff6589da 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -727,7 +727,7 @@ static bool gpencil_vertexpaint_brush_init(bContext *C, wmOperator *op) gso->brush = paint->brush; srgb_to_linearrgb_v3_v3(gso->linear_color, gso->brush->rgb); - BKE_curvemapping_initialize(gso->brush->curve); + BKE_curvemapping_init(gso->brush->curve); gso->is_painting = false; gso->first = true; @@ -759,7 +759,7 @@ static bool gpencil_vertexpaint_brush_init(bContext *C, wmOperator *op) /* Init multi-edit falloff curve data before doing anything, * so we won't have to do it again later. */ if (gso->is_multiframe) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); } /* Setup space conversions. */ diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index 4392ec92824..9d3e9c6ae45 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -295,7 +295,7 @@ static bool gpencil_weightpaint_brush_init(bContext *C, wmOperator *op) gso->bmain = CTX_data_main(C); gso->brush = paint->brush; - BKE_curvemapping_initialize(gso->brush->curve); + BKE_curvemapping_init(gso->brush->curve); gso->is_painting = false; gso->first = true; @@ -326,7 +326,7 @@ static bool gpencil_weightpaint_brush_init(bContext *C, wmOperator *op) /* Init multi-edit falloff curve data before doing anything, * so we won't have to do it again later. */ if (gso->is_multiframe) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); } /* Setup space conversions. */ diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index e0ce72e5c3c..3b7eb01f92c 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -23,6 +23,8 @@ #pragma once +#include "GPU_texture.h" + #ifdef __cplusplus extern "C" { #endif @@ -65,9 +67,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float xzoom, float yzoom, @@ -77,9 +78,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float clip_min_x, float clip_min_y, @@ -93,9 +93,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -107,9 +106,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -132,7 +130,7 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, void ED_draw_imbuf(struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, struct ColorManagedViewSettings *view_settings, struct ColorManagedDisplaySettings *display_settings, float zoom_x, @@ -140,7 +138,7 @@ void ED_draw_imbuf(struct ImBuf *ibuf, void ED_draw_imbuf_clipping(struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, struct ColorManagedViewSettings *view_settings, struct ColorManagedDisplaySettings *display_settings, float clip_min_x, @@ -154,14 +152,14 @@ void ED_draw_imbuf_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float zoom_x, float zoom_y); void ED_draw_imbuf_ctx_clipping(const struct bContext *C, struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float clip_min_x, float clip_min_y, float clip_max_x, diff --git a/source/blender/editors/include/ED_buttons.h b/source/blender/editors/include/ED_buttons.h index 8206f5a8619..ccef62eb8d2 100644 --- a/source/blender/editors/include/ED_buttons.h +++ b/source/blender/editors/include/ED_buttons.h @@ -26,6 +26,10 @@ extern "C" { #endif +struct SpaceProperties; + +int ED_buttons_tabs_list(struct SpaceProperties *sbuts, int *context_tabs_array); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h index 447b7b76c72..6c5aacafc7a 100644 --- a/source/blender/editors/include/ED_numinput.h +++ b/source/blender/editors/include/ED_numinput.h @@ -102,8 +102,12 @@ bool handleNumInput(struct bContext *C, NumInput *n, const struct wmEvent *event #define NUM_MODAL_INCREMENT_UP 18 #define NUM_MODAL_INCREMENT_DOWN 19 -bool user_string_to_number( - bContext *C, const char *str, const struct UnitSettings *unit, int type, double *r_value); +bool user_string_to_number(bContext *C, + const char *str, + const struct UnitSettings *unit, + int type, + const char *error_prefix, + double *r_value); /** \} */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 59567803f35..29fe766b2d0 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -72,7 +72,7 @@ void ED_region_exit(struct bContext *C, struct ARegion *region); void ED_region_remove(struct bContext *C, struct ScrArea *area, struct ARegion *region); void ED_region_pixelspace(struct ARegion *region); void ED_region_update_rect(struct ARegion *region); -void ED_region_floating_initialize(struct ARegion *region); +void ED_region_floating_init(struct ARegion *region); void ED_region_tag_redraw(struct ARegion *region); void ED_region_tag_redraw_partial(struct ARegion *region, const struct rcti *rct, bool rebuild); void ED_region_tag_redraw_cursor(struct ARegion *region); @@ -170,7 +170,7 @@ void ED_spacetypes_keymap(struct wmKeyConfig *keyconf); int ED_area_header_switchbutton(const struct bContext *C, struct uiBlock *block, int yco); /* areas */ -void ED_area_initialize(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); +void ED_area_init(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); void ED_area_exit(struct bContext *C, struct ScrArea *area); int ED_screen_area_active(const struct bContext *C); void ED_screen_global_areas_refresh(struct wmWindow *win); @@ -220,7 +220,7 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); vert_name->next) /* screens */ -void ED_screens_initialize(struct Main *bmain, struct wmWindowManager *wm); +void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm); void ED_screen_draw_edges(struct wmWindow *win); void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2); void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac); @@ -286,6 +286,12 @@ bool ED_workspace_delete(struct WorkSpace *workspace, struct bContext *C, struct wmWindowManager *wm) ATTR_NONNULL(); void ED_workspace_scene_data_sync(struct WorkSpaceInstanceHook *hook, Scene *scene) ATTR_NONNULL(); +struct WorkSpaceLayout *ED_workspace_screen_change_ensure_unused_layout( + struct Main *bmain, + struct WorkSpace *workspace, + struct WorkSpaceLayout *layout_new, + const struct WorkSpaceLayout *layout_fallback_base, + struct wmWindow *win) ATTR_NONNULL(); struct WorkSpaceLayout *ED_workspace_layout_add(struct Main *bmain, struct WorkSpace *workspace, struct wmWindow *win, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 739a2184fb5..ddbea592238 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -698,17 +698,6 @@ float ED_view3d_grid_view_scale(struct Scene *scene, void ED_scene_draw_fps(const struct Scene *scene, int xoffset, int *yoffset); -/* view matrix properties utilities */ -/* unused */ -#if 0 -void ED_view3d_operator_properties_viewmat(struct wmOperatorType *ot); -void ED_view3d_operator_properties_viewmat_set(struct bContext *C, struct wmOperator *op); -void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, - int *winx, - int *winy, - float persmat[4][4]); -#endif - /* render */ void ED_view3d_stop_render_preview(struct wmWindowManager *wm, struct ARegion *region); void ED_view3d_shade_update(struct Main *bmain, struct View3D *v3d, struct ScrArea *area); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index d682597da8e..184032df3c6 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -100,7 +100,7 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle; /* use for clamping popups within the screen */ #define UI_SCREEN_MARGIN 10 -/* uiBlock->dt and uiBut->dt */ +/** #uiBlock.emboss and #uiBut.emboss */ enum { UI_EMBOSS = 0, /* use widget style for drawing */ UI_EMBOSS_NONE = 1, /* Nothing, only icon and/or text */ @@ -656,7 +656,7 @@ bool UI_popup_block_name_exists(const struct bScreen *screen, const char *name); uiBlock *UI_block_begin(const struct bContext *C, struct ARegion *region, const char *name, - short dt); + char emboss); void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], int r_xy[2]); void UI_block_end(const struct bContext *C, uiBlock *block); void UI_block_draw(const struct bContext *C, struct uiBlock *block); @@ -670,7 +670,7 @@ enum { }; void UI_block_theme_style_set(uiBlock *block, char theme_style); char UI_block_emboss_get(uiBlock *block); -void UI_block_emboss_set(uiBlock *block, char dt); +void UI_block_emboss_set(uiBlock *block, char emboss); void UI_block_free(const struct bContext *C, uiBlock *block); void UI_blocklist_free(const struct bContext *C, struct ListBase *lb); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 286cb1571bd..c5d5fbb90c0 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -52,7 +52,6 @@ #include "BKE_screen.h" #include "BKE_unit.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_state.h" @@ -1493,7 +1492,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) continue; } } - else if (but->dt != UI_EMBOSS_PULLDOWN) { + else if (but->emboss != UI_EMBOSS_PULLDOWN) { continue; } @@ -2807,25 +2806,39 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size) return str; } -static bool ui_set_but_string_eval_num_unit(bContext *C, - uiBut *but, - const char *str, - double *r_value) +/** + * Report a generic error prefix when evaluating a string with #BPY_execute_string_as_number + * as the Python error on it's own doesn't provide enough context. + */ +#define UI_NUMBER_EVAL_ERROR_PREFIX IFACE_("Error evaluating number, see Info editor for details") + +static bool ui_number_from_string_units( + bContext *C, const char *str, const int unit_type, const UnitSettings *unit, double *r_value) { + return user_string_to_number(C, str, unit, unit_type, UI_NUMBER_EVAL_ERROR_PREFIX, r_value); +} + +static bool ui_number_from_string_units_with_but(bContext *C, + const char *str, + const uiBut *but, + double *r_value) +{ + const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); const UnitSettings *unit = but->block->unit; - int type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); - return user_string_to_number(C, str, unit, type, r_value); + return ui_number_from_string_units(C, str, unit_type, unit, r_value); } static bool ui_number_from_string(bContext *C, const char *str, double *r_value) { + bool ok; #ifdef WITH_PYTHON - return BPY_execute_string_as_number(C, NULL, str, true, r_value); + ok = BPY_execute_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value); #else UNUSED_VARS(C); *r_value = atof(str); - return true; + ok = true; #endif + return ok; } static bool ui_number_from_string_factor(bContext *C, const char *str, double *r_value) @@ -2859,7 +2872,7 @@ static bool ui_number_from_string_percentage(bContext *C, const char *str, doubl return ui_number_from_string(C, str, r_value); } -bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double *r_value) +bool ui_but_string_eval_number(bContext *C, const uiBut *but, const char *str, double *r_value) { if (str[0] == '\0') { *r_value = 0.0; @@ -2873,7 +2886,7 @@ bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double if (ui_but_is_float(but)) { if (ui_but_is_unit(but)) { - return ui_set_but_string_eval_num_unit(C, but, str, r_value); + return ui_number_from_string_units_with_but(C, str, but, r_value); } if (subtype == PROP_FACTOR) { return ui_number_from_string_factor(C, str, r_value); @@ -3013,7 +3026,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) /* number editing */ double value; - if (ui_but_string_set_eval_num(C, but, str, &value) == false) { + if (ui_but_string_eval_number(C, but, str, &value) == false) { WM_report_banner_show(); return false; } @@ -3377,7 +3390,7 @@ void UI_block_region_set(uiBlock *block, ARegion *region) block->oldblock = oldblock; } -uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, short dt) +uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, char emboss) { uiBlock *block; wmWindow *window; @@ -3388,7 +3401,7 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh block = MEM_callocN(sizeof(uiBlock), "uiBlock"); block->active = 1; - block->dt = dt; + block->emboss = emboss; block->evil_C = (void *)C; /* XXX */ if (scn) { @@ -3427,12 +3440,12 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh char UI_block_emboss_get(uiBlock *block) { - return block->dt; + return block->emboss; } -void UI_block_emboss_set(uiBlock *block, char dt) +void UI_block_emboss_set(uiBlock *block, char emboss) { - block->dt = dt; + block->emboss = emboss; } void UI_block_theme_style_set(uiBlock *block, char theme_style) @@ -3812,7 +3825,7 @@ static uiBut *ui_def_but(uiBlock *block, but->tip = tip; but->disabled_info = block->lockstr; - but->dt = block->dt; + but->emboss = block->emboss; but->pie_dir = UI_RADIAL_NONE; but->block = block; /* pointer back, used for frontbuffer status, and picker */ @@ -4345,7 +4358,7 @@ static uiBut *ui_def_but_rna(uiBlock *block, } if (type == UI_BTYPE_MENU) { - if (but->dt == UI_EMBOSS_PULLDOWN) { + if (but->emboss == UI_EMBOSS_PULLDOWN) { ui_but_submenu_enable(block, but); } } diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index a08c5c45b6f..aaa5e1c0cf1 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -407,7 +407,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um) "'%s').label", idname); char *expr_result = NULL; - if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) { + if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { STRNCPY(drawstr, expr_result); MEM_freeN(expr_result); } diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index cc5d21c3df3..df11a78e657 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -773,9 +773,8 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region), (float)rect->ymin, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, ibuf->rect, 1.0f, 1.0f, @@ -2257,12 +2256,12 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Also add the last points on the right and bottom edges to close off the fill polygon. */ bool add_left_tri = profile->view_rect.xmin < 0.0f; bool add_bottom_tri = profile->view_rect.ymin < 0.0f; - uint tot_points = (uint)PROF_N_TABLE(profile->path_len) + 1 + add_left_tri + add_bottom_tri; + uint tot_points = (uint)PROF_TABLE_LEN(profile->path_len) + 1 + add_left_tri + add_bottom_tri; uint tot_triangles = tot_points - 2; /* Create array of the positions of the table's points. */ float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords"); - for (i = 0; i < (uint)PROF_N_TABLE(profile->path_len); + for (i = 0; i < (uint)PROF_TABLE_LEN(profile->path_len); i++) { /* Only add the points from the table here. */ table_coords[i][0] = pts[i].x; table_coords[i][1] = pts[i].y; @@ -2545,9 +2544,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), rect.ymin + 1, drawibuf->x, drawibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_LINEAR, + GPU_RGBA8, + true, drawibuf->rect, 1.0f, 1.0f, diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 93b052b3b69..5da82b5be9c 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -39,8 +39,6 @@ #include "RNA_access.h" -#include "GPU_glew.h" - #include "UI_interface.h" #include "IMB_colormanagement.h" diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index bcb4f7c672f..efbfcf8d19d 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2367,7 +2367,7 @@ static void ui_but_paste_numeric_value(bContext *C, { double value; - if (ui_but_string_set_eval_num(C, but, buf_paste, &value)) { + if (ui_but_string_eval_number(C, but, buf_paste, &value)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); data->value = value; ui_but_string_set(C, but, buf_paste); @@ -4390,7 +4390,7 @@ static int ui_do_but_TEX( if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && (!UI_but_is_utf8(but))) { /* pass - allow filesel, enter to execute */ } - else if (but->dt == UI_EMBOSS_NONE && !event->ctrl) { + else if (but->emboss == UI_EMBOSS_NONE && !event->ctrl) { /* pass */ } else { @@ -7129,7 +7129,7 @@ static int ui_do_but_CURVEPROFILE( dist_min_sq = square_f(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */ /* Loop through the path's high resolution table and find what's near the click. */ - for (int i = 1; i <= PROF_N_TABLE(profile->path_len); i++) { + for (int i = 1; i <= PROF_TABLE_LEN(profile->path_len); i++) { copy_v2_v2(f_xy_prev, f_xy); BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[i].x); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 586f5e07997..a7b7bad2fe6 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1517,18 +1517,8 @@ static void icon_draw_rect(float x, immUniform1f("factor", desaturate); } - immDrawPixelsTex(&state, - draw_x, - draw_y, - draw_w, - draw_h, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - rect, - 1.0f, - 1.0f, - col); + immDrawPixelsTex( + &state, draw_x, draw_y, draw_w, draw_h, GPU_RGBA8, false, rect, 1.0f, 1.0f, col); if (ima) { IMB_freeImBuf(ima); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index ab5d8806837..bf2971f93df 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -232,8 +232,8 @@ struct uiBut { const char *disabled_info; BIFIconID icon; - /** drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied from the block */ - char dt; + /** emboss: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied from the #uiBlock.emboss */ + char emboss; /** direction in a pie menu, used for collision detection (RadialDirection) */ signed char pie_dir; /** could be made into a single flag */ @@ -404,8 +404,8 @@ struct uiBlock { char direction; /** UI_BLOCK_THEME_STYLE_* */ char theme_style; - /** drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied to buttons */ - char dt; + /** UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied to #uiBut.emboss */ + char emboss; bool auto_open; char _pad[5]; double auto_open_last; @@ -516,10 +516,10 @@ extern void ui_but_string_get(uiBut *but, char *str, const size_t maxlen) ATTR_N extern char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size); extern void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen) ATTR_NONNULL(); extern bool ui_but_string_set(struct bContext *C, uiBut *but, const char *str) ATTR_NONNULL(); -extern bool ui_but_string_set_eval_num(struct bContext *C, - uiBut *but, - const char *str, - double *value) ATTR_NONNULL(); +extern bool ui_but_string_eval_number(struct bContext *C, + const uiBut *but, + const char *str, + double *value) ATTR_NONNULL(); extern int ui_but_string_get_max_length(uiBut *but); /* Clear & exit the active button's string. */ extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL(); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index f1d1ef589a5..98408156f4b 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -674,7 +674,7 @@ static void ui_item_array(uiLayout *layout, /* show checkboxes for rna on a non-emboss block (menu for eg) */ if (type == PROP_BOOLEAN && - ELEM(layout->root->block->dt, UI_EMBOSS_NONE, UI_EMBOSS_PULLDOWN)) { + ELEM(layout->root->block->emboss, UI_EMBOSS_NONE, UI_EMBOSS_PULLDOWN)) { boolarr = MEM_callocN(sizeof(bool) * len, __func__); RNA_property_boolean_get_array(ptr, prop, boolarr); } @@ -2333,7 +2333,7 @@ void uiItemFullR(uiLayout *layout, /* Mark non-embossed textfields inside a listbox. */ if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == UI_BTYPE_TEXT) && - (but->dt & UI_EMBOSS_NONE)) { + (but->emboss & UI_EMBOSS_NONE)) { UI_but_flag_enable(but, UI_BUT_LIST_ITEM); } @@ -3831,7 +3831,7 @@ static void ui_litem_layout_radial(uiLayout *litem) bitem->but->rect.xmax += 1.5f * UI_UNIT_X; /* enable drawing as pie item if supported by widget */ if (ui_item_is_radial_drawable(bitem)) { - bitem->but->dt = UI_EMBOSS_RADIAL; + bitem->but->emboss = UI_EMBOSS_RADIAL; bitem->but->drawflag |= UI_BUT_ICON_LEFT; } } @@ -5039,7 +5039,7 @@ float uiLayoutGetUnitsY(uiLayout *layout) int uiLayoutGetEmboss(uiLayout *layout) { if (layout->emboss == UI_EMBOSS_UNDEFINED) { - return layout->root->block->dt; + return layout->root->block->emboss; } return layout->emboss; } @@ -5414,7 +5414,7 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but) } if (layout->emboss != UI_EMBOSS_UNDEFINED) { - but->dt = layout->emboss; + but->emboss = layout->emboss; } } diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 39c1b8bb909..7ac3c90dcd2 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1807,7 +1807,7 @@ static void UI_OT_drop_color(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; RNA_def_float_color(ot->srna, "color", 3, NULL, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0); - RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected "); + RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected"); } /** \} */ diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 4634f4b95c9..10b219202e5 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -90,7 +90,7 @@ bool ui_but_is_interactive(const uiBut *but, const bool labeledit) if (but->flag & UI_SCROLLED) { return false; } - if ((but->type == UI_BTYPE_TEXT) && (but->dt == UI_EMBOSS_NONE) && !labeledit) { + if ((but->type == UI_BTYPE_TEXT) && (but->emboss == UI_EMBOSS_NONE) && !labeledit) { return false; } if ((but->type == UI_BTYPE_LISTROW) && labeledit) { diff --git a/source/blender/editors/interface/interface_region_hud.c b/source/blender/editors/interface/interface_region_hud.c index 1f8af7b9e6e..1773a7b3057 100644 --- a/source/blender/editors/interface/interface_region_hud.c +++ b/source/blender/editors/interface/interface_region_hud.c @@ -367,7 +367,7 @@ void ED_area_type_hud_ensure(bContext *C, ScrArea *area) ED_area_update_region_sizes(wm, win, area); } - ED_region_floating_initialize(region); + ED_region_floating_init(region); ED_region_tag_redraw(region); /* Reset zoom level (not well supported). */ diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 2ad7e517c60..13c85952f52 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -759,7 +759,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, ui_popup_block_scrolltest(block); /* adds subwindow */ - ED_region_floating_initialize(region); + ED_region_floating_init(region); /* get winmat now that we actually have the subwindow */ wmGetProjectionMatrix(block->winmat, ®ion->winrct); diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index a9e87f4cc07..80155e3e871 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -819,7 +819,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but } /* adds subwindow */ - ED_region_floating_initialize(region); + ED_region_floating_init(region); /* notify change and redraw */ ED_region_tag_redraw(region); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 46814e11b9e..41b41cb3d75 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -433,7 +433,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { expr_result = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) { + else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { if (STREQ(expr_result, "")) { MEM_freeN(expr_result); expr_result = NULL; @@ -490,7 +490,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { expr_result = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) { + else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { if (STREQ(expr_result, ".")) { MEM_freeN(expr_result); expr_result = NULL; @@ -594,7 +594,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { shortcut = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_intptr(C, expr_imports, expr, true, &expr_result)) { + else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { if (expr_result != 0) { wmKeyMap *keymap = (wmKeyMap *)expr_result; LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { @@ -659,7 +659,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* pass */ } else if (BPY_execute_string_as_string_and_size( - C, expr_imports, expr, true, &expr_result, &expr_result_len)) { + C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) { /* pass. */ } } @@ -736,7 +736,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { /* pass */ } - else if (BPY_execute_string_as_intptr(C, expr_imports, expr, true, &expr_result)) { + else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { if (expr_result != 0) { { uiTooltipField *field = text_field_add(data, @@ -1386,7 +1386,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, } /* adds subwindow */ - ED_region_floating_initialize(region); + ED_region_floating_init(region); /* notify change and redraw */ ED_region_tag_redraw(region); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index d3487b635ce..fcafa88a806 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -7348,6 +7348,9 @@ void uiTemplateCacheFile(uiLayout *layout, uiItemR(row, &fileptr, "scale", 0, IFACE_("Manual Scale"), ICON_NONE); } + uiItemR(layout, &fileptr, "velocity_name", 0, NULL, ICON_NONE); + uiItemR(layout, &fileptr, "velocity_unit", 0, NULL, ICON_NONE); + /* TODO: unused for now, so no need to expose. */ #if 0 row = uiLayoutRow(layout, false); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 9ba1a2c73f4..52835b5474e 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1356,7 +1356,7 @@ static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect) static int ui_but_draw_menu_icon(const uiBut *but) { - return (but->flag & UI_BUT_ICON_SUBMENU) && (but->dt == UI_EMBOSS_PULLDOWN); + return (but->flag & UI_BUT_ICON_SUBMENU) && (but->emboss == UI_EMBOSS_PULLDOWN); } /* icons have been standardized... and this call draws in untransformed coordinates */ @@ -1417,7 +1417,7 @@ static void widget_draw_icon( but->str && but->str[0] == '\0') { xs = rect->xmin + 2.0f * ofs; } - else if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) { + else if (but->emboss == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) { xs = rect->xmin + 2.0f * ofs; } else { @@ -2375,7 +2375,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, /* pass (even if its a menu toolbar) */ } else if (ui_block_is_pie_menu(but->block)) { - if (but->dt == UI_EMBOSS_RADIAL) { + if (but->emboss == UI_EMBOSS_RADIAL) { rect->xmin += 0.3f * U.widget_unit; } } @@ -4502,7 +4502,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu uiWidgetType *wt = NULL; /* handle menus separately */ - if (but->dt == UI_EMBOSS_PULLDOWN) { + if (but->emboss == UI_EMBOSS_PULLDOWN) { switch (but->type) { case UI_BTYPE_LABEL: widget_draw_text_icon(&style->widgetlabel, &tui->wcol_menu_back, but, rect); @@ -4515,7 +4515,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu break; } } - else if (but->dt == UI_EMBOSS_NONE) { + else if (but->emboss == UI_EMBOSS_NONE) { /* "nothing" */ switch (but->type) { case UI_BTYPE_LABEL: @@ -4526,11 +4526,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu break; } } - else if (but->dt == UI_EMBOSS_RADIAL) { + else if (but->emboss == UI_EMBOSS_RADIAL) { wt = widget_type(UI_WTYPE_MENU_ITEM_RADIAL); } else { - BLI_assert(but->dt == UI_EMBOSS); + BLI_assert(but->emboss == UI_EMBOSS); switch (but->type) { case UI_BTYPE_LABEL: @@ -4779,7 +4779,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu } if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { - if (but->dt != UI_EMBOSS_PULLDOWN) { + if (but->emboss != UI_EMBOSS_PULLDOWN) { disabled = true; } } diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 0cbf73280a3..3efed43e08c 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -242,7 +242,7 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) bool tot_changed = false, do_init; const uiStyle *style = UI_style_get(); - do_init = (v2d->flag & V2D_IS_INITIALISED) == 0; + do_init = (v2d->flag & V2D_IS_INIT) == 0; /* see eView2D_CommonViewTypes in UI_view2d.h for available view presets */ switch (type) { @@ -374,8 +374,8 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) break; } - /* set initialized flag so that View2D doesn't get reinitialised next time again */ - v2d->flag |= V2D_IS_INITIALISED; + /* set initialized flag so that View2D doesn't get reinitialized next time again */ + v2d->flag |= V2D_IS_INIT; /* store view size */ v2d->winx = winx; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 64cacd44e3d..d62058699d9 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -56,7 +56,7 @@ static bool view2d_poll(bContext *C) { ARegion *region = CTX_wm_region(C); - return (region != NULL) && (region->v2d.flag & V2D_IS_INITIALISED); + return (region != NULL) && (region->v2d.flag & V2D_IS_INIT); } /** \} */ diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index 3786ed2789c..12ce358a501 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -753,8 +753,7 @@ void ED_mask_draw_region( IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex( - &state, 0.0f, 0.0f, width, height, GL_RED, GL_FLOAT, GL_NEAREST, buffer, 1.0f, 1.0f, NULL); + immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GL_R16F, false, buffer, 1.0f, 1.0f, NULL); GPU_matrix_pop(); diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 61b40dd3e60..dd4b8146154 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -1158,12 +1158,12 @@ void MESH_OT_bevel(wmOperatorType *ot) PROFILE_HARD_MIN, 1.0f); - prop = RNA_def_enum(ot->srna, - "affect", - prop_affect_items, - BEVEL_AFFECT_EDGES, - "Affect", - "Affect Edges or Vertices"); + RNA_def_enum(ot->srna, + "affect", + prop_affect_items, + BEVEL_AFFECT_EDGES, + "Affect", + "Affect Edges or Vertices"); RNA_def_boolean(ot->srna, "clamp_overlap", diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 347f6806d13..1f411617c68 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1628,9 +1628,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) plane_from_point_normal_v3(plane, v1, plane); } - /* First use bvh tree to find faces, knife edges, and knife verts that might + /* First use BVH tree to find faces, knife edges, and knife verts that might * intersect the cut plane with rays v1-v3 and v2-v4. - * This deduplicates the candidates before doing more expensive intersection tests. */ + * This de-duplicates the candidates before doing more expensive intersection tests. */ tree = BKE_bmbvh_tree_get(kcd->bmbvh); results = BLI_bvhtree_intersect_plane(tree, plane, &tot); diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index ed22d381a02..ef78d31a6bb 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -164,7 +164,7 @@ void MESH_OT_knife_project(wmOperatorType *ot) /* description */ ot->name = "Knife Project"; ot->idname = "MESH_OT_knife_project"; - ot->description = "Use other objects outlines & boundaries to project knife cuts"; + ot->description = "Use other objects outlines and boundaries to project knife cuts"; /* callbacks */ ot->exec = knifeproject_exec; diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 1ea25353598..d2e9b57e950 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -205,7 +205,7 @@ static BMElem *edbm_select_id_bm_elem_get(Base **bases, const uint sel_id, uint /* -------------------------------------------------------------------- */ /** \name Find Nearest Vert/Edge/Face * - * \note Screen-space manhatten distances are used here, + * \note Screen-space manhattan distances are used here, * since its faster and good enough for the purpose of selection. * * \note \a dist_bias is used so we can bias against selected items. @@ -415,7 +415,7 @@ struct NearestEdgeUserData_Hit { int index; BMEdge *edge; - /* edges only, un-biased manhatten distance to which ever edge we pick + /* edges only, un-biased manhattan distance to which ever edge we pick * (not used for choosing) */ float dist_center; }; diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 4de7143682a..29860de88f1 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -7219,7 +7219,7 @@ void MESH_OT_wireframe(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Wire Frame"; + ot->name = "Wireframe"; ot->idname = "MESH_OT_wireframe"; ot->description = "Create a solid wire-frame from faces"; diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 9c364d41e24..78839a0dca5 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -692,6 +692,46 @@ void OBJECT_OT_lightprobe_add(wmOperatorType *ot) * \{ */ /* for object add operator */ + +static const char *get_effector_defname(ePFieldType type) +{ + switch (type) { + case PFIELD_FORCE: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Force"); + case PFIELD_VORTEX: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Vortex"); + case PFIELD_MAGNET: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Magnet"); + case PFIELD_WIND: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Wind"); + case PFIELD_GUIDE: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "CurveGuide"); + case PFIELD_TEXTURE: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "TextureField"); + case PFIELD_HARMONIC: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Harmonic"); + case PFIELD_CHARGE: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Charge"); + case PFIELD_LENNARDJ: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Lennard-Jones"); + case PFIELD_BOID: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Boid"); + case PFIELD_TURBULENCE: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Turbulence"); + case PFIELD_DRAG: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Drag"); + case PFIELD_FLUIDFLOW : + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "FluidField"); + case PFIELD_NULL: + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Field"); + case NUM_PFIELD_TYPES: + break; + } + + BLI_assert(false); + return CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Field"); +} + static int effector_add_exec(bContext *C, wmOperator *op) { Object *ob; @@ -712,8 +752,8 @@ static int effector_add_exec(bContext *C, wmOperator *op) if (type == PFIELD_GUIDE) { Curve *cu; - const char *name = CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "CurveGuide"); - ob = ED_object_add_type(C, OB_CURVE, name, loc, rot, false, local_view_bits); + ob = ED_object_add_type( + C, OB_CURVE, get_effector_defname(type), loc, rot, false, local_view_bits); cu = ob->data; cu->flag |= CU_PATH | CU_3D; @@ -726,8 +766,7 @@ static int effector_add_exec(bContext *C, wmOperator *op) } } else { - const char *name = CTX_DATA_(BLT_I18NCONTEXT_ID_OBJECT, "Field"); - ob = ED_object_add_type(C, OB_EMPTY, name, loc, rot, false, local_view_bits); + ob = ED_object_add_type(C, OB_EMPTY, get_effector_defname(type), loc, rot, false, local_view_bits); BKE_object_obdata_size_init(ob, dia); if (ELEM(type, PFIELD_WIND, PFIELD_VORTEX)) { ob->empty_drawtype = OB_SINGLE_ARROW; @@ -1237,7 +1276,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) break; } - /* if this is a new object, initialise default stuff (colors, etc.) */ + /* If this is a new object, initialize default stuff (colors, etc.) */ if (newob) { /* set default viewport color to black */ copy_v3_fl(ob->color, 0.0f); diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index baa24ab2f4e..ae1aae27b7f 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -60,8 +60,6 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" -#include "GPU_draw.h" /* GPU_free_image */ - #include "WM_api.h" #include "WM_types.h" @@ -530,7 +528,7 @@ static void multiresbake_freejob(void *bkv) /* delete here, since this delete will be called from main thread */ for (link = data->images.first; link; link = link->next) { Image *ima = (Image *)link->data; - GPU_free_image(ima); + BKE_image_free_gputextures(ima); } MEM_freeN(data->ob_image.array); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index c4cb21a67f3..cb92fab3cb0 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -68,8 +68,6 @@ #include "ED_screen.h" #include "ED_uvedit.h" -#include "GPU_draw.h" - #include "object_intern.h" /* prototypes */ @@ -308,7 +306,7 @@ static void refresh_images(BakeImages *bake_images) Image *ima = bake_images->data[i].image; LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { if (tile->ok == IMA_OK_LOADED) { - GPU_free_image(ima); + BKE_image_free_gputextures(ima); DEG_id_tag_update(&ima->id, 0); break; } @@ -675,7 +673,7 @@ static void build_image_lookup(Main *bmain, Object *ob, BakeImages *bake_images) /* * returns the total number of pixels */ -static size_t initialize_internal_images(BakeImages *bake_images, ReportList *reports) +static size_t init_internal_images(BakeImages *bake_images, ReportList *reports) { int i; size_t tot_size = 0; @@ -830,7 +828,7 @@ static int bake(Render *re, build_image_lookup(bmain, ob_low, &bake_images); if (is_save_internal) { - num_pixels = initialize_internal_images(&bake_images, reports); + num_pixels = init_internal_images(&bake_images, reports); if (num_pixels == 0) { goto cleanup; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index e93bd8bd94e..517e791e8fa 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -2813,7 +2813,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) /* make a copy of ocean to use for baking - threadsafety */ ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(ocean, omd); + BKE_ocean_init_from_modifier(ocean, omd, omd->resolution); #if 0 BKE_ocean_bake(ocean, och); diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 8981851394d..28f58a34814 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -72,7 +72,6 @@ #include "RNA_define.h" #include "RNA_enum_types.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 749c6cd640e..82a1139c860 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -4346,7 +4346,7 @@ static int brush_add(const bContext *C, PEData *data, short number) } pa->size = 1.0f; - initialize_particle(&sim, pa); + init_particle(&sim, pa); reset_particle(&sim, pa, 0.0, 1.0); point->flag |= PEP_EDIT_RECALC; if (pe_x_mirror(ob)) { diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 673df69bf9b..d279958df8a 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -79,7 +79,6 @@ #include "RNA_define.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "render_intern.h" @@ -351,7 +350,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R G.f &= ~G_FLAG_RENDER_VIEWPORT; gp_rect = MEM_mallocN(sizex * sizey * sizeof(uchar) * 4, "offscreen rect"); - GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect); + GPU_offscreen_read_pixels(oglrender->ofs, GPU_DATA_UNSIGNED_BYTE, gp_rect); for (i = 0; i < sizex * sizey * 4; i += 4) { blend_color_mix_byte(&render_rect[i], &render_rect[i], &gp_rect[i]); @@ -963,7 +962,7 @@ static void screen_opengl_render_cancel(bContext *C, wmOperator *op) } /* share between invoke and exec */ -static bool screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) +static bool screen_opengl_render_anim_init(bContext *C, wmOperator *op) { /* initialize animation */ OGLRender *oglrender; @@ -1257,7 +1256,7 @@ static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEven } if (anim) { - if (!screen_opengl_render_anim_initialize(C, op)) { + if (!screen_opengl_render_anim_init(C, op)) { return OPERATOR_CANCELLED; } } @@ -1293,7 +1292,7 @@ static int screen_opengl_render_exec(bContext *C, wmOperator *op) bool ret = true; - if (!screen_opengl_render_anim_initialize(C, op)) { + if (!screen_opengl_render_anim_init(C, op)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 19e4f652963..85fc6927063 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -82,7 +82,6 @@ #include "BIF_glutil.h" -#include "GPU_glew.h" #include "GPU_shader.h" #include "RE_engine.h" @@ -632,18 +631,8 @@ static bool ed_preview_draw_rect(ScrArea *area, int split, int first, rcti *rect } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTex(&state, - fx, - fy, - rres.rectx, - rres.recty, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - rect_byte, - 1.0f, - 1.0f, - NULL); + immDrawPixelsTex( + &state, fx, fy, rres.rectx, rres.recty, GPU_RGBA8, false, rect_byte, 1.0f, 1.0f, NULL); MEM_freeN(rect_byte); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index f87f631c643..9616b8114ba 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -813,7 +813,7 @@ void ED_workspace_status_text(bContext *C, const char *str) /* ************************************************************ */ -static void area_azone_initialize(wmWindow *win, const bScreen *screen, ScrArea *area) +static void area_azone_init(wmWindow *win, const bScreen *screen, ScrArea *area) { AZone *az; @@ -883,7 +883,7 @@ static void area_azone_initialize(wmWindow *win, const bScreen *screen, ScrArea } } -static void fullscreen_azone_initialize(ScrArea *area, ARegion *region) +static void fullscreen_azone_init(ScrArea *area, ARegion *region) { AZone *az; @@ -1007,10 +1007,10 @@ static bool region_azone_edge_poll(const ARegion *region, const bool is_fullscre return true; } -static void region_azone_edge_initialize(ScrArea *area, - ARegion *region, - AZEdge edge, - const bool is_fullscreen) +static void region_azone_edge_init(ScrArea *area, + ARegion *region, + AZEdge edge, + const bool is_fullscreen) { AZone *az = NULL; const bool is_hidden = (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)); @@ -1033,9 +1033,9 @@ static void region_azone_edge_initialize(ScrArea *area, } } -static void region_azone_scrollbar_initialize(ScrArea *area, - ARegion *region, - AZScrollDirection direction) +static void region_azone_scrollbar_init(ScrArea *area, + ARegion *region, + AZScrollDirection direction) { rcti scroller_vert = (direction == AZ_SCROLL_VERT) ? region->v2d.vert : region->v2d.hor; AZone *az = MEM_callocN(sizeof(*az), __func__); @@ -1061,16 +1061,16 @@ static void region_azone_scrollbar_initialize(ScrArea *area, BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } -static void region_azones_scrollbars_initialize(ScrArea *area, ARegion *region) +static void region_azones_scrollbars_init(ScrArea *area, ARegion *region) { const View2D *v2d = ®ion->v2d; if ((v2d->scroll & V2D_SCROLL_VERTICAL) && ((v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES) == 0)) { - region_azone_scrollbar_initialize(area, region, AZ_SCROLL_VERT); + region_azone_scrollbar_init(area, region, AZ_SCROLL_VERT); } if ((v2d->scroll & V2D_SCROLL_HORIZONTAL) && ((v2d->scroll & V2D_SCROLL_HORIZONTAL_HANDLES) == 0)) { - region_azone_scrollbar_initialize(area, region, AZ_SCROLL_HOR); + region_azone_scrollbar_init(area, region, AZ_SCROLL_HOR); } } @@ -1083,16 +1083,16 @@ static void region_azones_add_edge(ScrArea *area, /* edge code (t b l r) is along which area edge azone will be drawn */ if (alignment == RGN_ALIGN_TOP) { - region_azone_edge_initialize(area, region, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); + region_azone_edge_init(area, region, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); } else if (alignment == RGN_ALIGN_BOTTOM) { - region_azone_edge_initialize(area, region, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); + region_azone_edge_init(area, region, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); } else if (alignment == RGN_ALIGN_RIGHT) { - region_azone_edge_initialize(area, region, AE_LEFT_TO_TOPRIGHT, is_fullscreen); + region_azone_edge_init(area, region, AE_LEFT_TO_TOPRIGHT, is_fullscreen); } else if (alignment == RGN_ALIGN_LEFT) { - region_azone_edge_initialize(area, region, AE_RIGHT_TO_TOPLEFT, is_fullscreen); + region_azone_edge_init(area, region, AE_RIGHT_TO_TOPLEFT, is_fullscreen); } } @@ -1116,10 +1116,10 @@ static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *reg } if (is_fullscreen) { - fullscreen_azone_initialize(area, region); + fullscreen_azone_init(area, region); } - region_azones_scrollbars_initialize(area, region); + region_azones_scrollbars_init(area, region); } /* dir is direction to check, not the splitting edge direction! */ @@ -1828,7 +1828,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar region_rect_recursive(area, area->regionbase.first, &rect, &overlap_rect, 0); /* Dynamically sized regions may have changed region sizes, so we have to force azone update. */ - area_azone_initialize(win, screen, area); + area_azone_init(win, screen, area); LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { region_subwindow(region); @@ -1847,7 +1847,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar } /* called in screen_refresh, or screens_init, also area size changes */ -void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *area) +void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) { WorkSpace *workspace = WM_window_get_active_workspace(win); const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); @@ -1890,7 +1890,7 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *area) } /* clear all azones, add the area triangle widgets */ - area_azone_initialize(win, screen, area); + area_azone_init(win, screen, area); /* region windows, default and own handlers */ for (region = area->regionbase.first; region; region = region->next) { @@ -1944,7 +1944,7 @@ void ED_region_update_rect(ARegion *region) } /* externally called for floating regions like menus */ -void ED_region_floating_initialize(ARegion *region) +void ED_region_floating_init(ARegion *region) { BLI_assert(region->alignment == RGN_ALIGN_FLOAT); @@ -1980,7 +1980,7 @@ void ED_region_visibility_change_update(bContext *C, ScrArea *area, ARegion *reg WM_event_remove_handlers(C, ®ion->handlers); } - ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), area); + ED_area_init(CTX_wm_manager(C), CTX_wm_window(C), area); ED_area_tag_redraw(area); } @@ -2066,8 +2066,8 @@ void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2) ED_area_data_copy(tmp, sa1, false); ED_area_data_copy(sa1, sa2, true); ED_area_data_copy(sa2, tmp, true); - ED_area_initialize(CTX_wm_manager(C), win, sa1); - ED_area_initialize(CTX_wm_manager(C), win, sa2); + ED_area_init(CTX_wm_manager(C), win, sa1); + ED_area_init(CTX_wm_manager(C), win, sa2); BKE_screen_area_free(tmp); MEM_freeN(tmp); @@ -2126,7 +2126,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi area->spacetype = type; area->type = st; - /* If st->new may be called, don't use context until then. The + /* If st->create may be called, don't use context until then. The * area->type->context() callback has changed but data may be invalid * (e.g. with properties editor) until space-data is properly created */ @@ -2166,7 +2166,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi if (st) { /* Don't get scene from context here which may depend on space-data. */ Scene *scene = WM_window_get_active_scene(win); - sl = st->new (area, scene); + sl = st->create(area, scene); BLI_addhead(&area->spacedata, sl); /* swap regions */ @@ -2204,7 +2204,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi } } - ED_area_initialize(CTX_wm_manager(C), win, area); + ED_area_init(CTX_wm_manager(C), win, area); /* tell WM to refresh, cursor types etc */ WM_event_add_mousemove(win); diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 5c3b1944164..07a122c7094 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -95,9 +95,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -115,21 +114,30 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y)); float white[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - if (type != GL_FLOAT) { - BLI_assert(type == GL_UNSIGNED_BYTE); - type = GL_UNSIGNED_BYTE; + if (ELEM(gpu_format, GPU_RGBA8, GPU_RGBA16F)) { + components = 4; + } + else if (ELEM(gpu_format, GPU_RGB16F)) { + components = 3; + } + else if (ELEM(gpu_format, GPU_R8, GPU_R16F)) { + components = 1; + } + else { + BLI_assert(!"Incompatible format passed to immDrawPixels"); + return; } - eGPUTextureFormat gpu_format = (type == GL_FLOAT) ? GPU_RGBA16F : GPU_RGBA8; - eGPUDataFormat gpu_data = (type == GL_FLOAT) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; - GPUTexture *texture = GPU_texture_create_nD( - tex_w, tex_h, 0, 2, NULL, gpu_format, gpu_data, 0, false, NULL); + const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F); + eGPUDataFormat gpu_data = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + size_t stride = components * ((use_float_data) ? sizeof(float) : sizeof(uchar)); - /* TODO replace GL_NEAREST/LINEAR in callers. */ - GPU_texture_filter_mode(texture, (zoomfilter == GL_LINEAR)); - GPU_texture_wrap_mode(texture, false, true); + GPUTexture *tex = GPU_texture_create_2d(tex_w, tex_h, gpu_format, NULL, NULL); - GPU_texture_bind(texture, 0); + GPU_texture_filter_mode(tex, use_filter); + GPU_texture_wrap_mode(tex, false, true); + + GPU_texture_bind(tex, 0); /* setup seamless 2=on, 0=off */ seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0; @@ -140,20 +148,6 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, nsubparts_x = (img_w + (offset_x - 1)) / (offset_x); nsubparts_y = (img_h + (offset_y - 1)) / (offset_y); - if (format == GL_RGBA) { - components = 4; - } - else if (format == GL_RGB) { - components = 3; - } - else if (format == GL_RED) { - components = 1; - } - else { - BLI_assert(!"Incompatible format passed to glaDrawPixelsTexScaled"); - return; - } - /* optional */ /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since * it does not need color. @@ -199,26 +193,32 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, { int src_y = subpart_y * offset_y; int src_x = subpart_x * offset_x; - size_t stride = components * ((type == GL_FLOAT) ? sizeof(float) : sizeof(uchar)); #define DATA(_y, _x) ((char *)rect + stride * ((size_t)(_y)*img_w + (_x))) { void *data = DATA(src_y, src_x); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, type, data); + GPU_texture_update_sub(tex, gpu_data, data, 0, 0, 0, subpart_w, subpart_h, 0); } /* Add an extra border of pixels so linear interpolation looks ok * at edges of full image. */ if (subpart_w < tex_w) { void *data = DATA(src_y, src_x + subpart_w - 1); - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, type, data); + int offset[2] = {subpart_w, 0}; + int extent[2] = {1, subpart_h}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } if (subpart_h < tex_h) { void *data = DATA(src_y + subpart_h - 1, src_x); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, type, data); + int offset[2] = {0, subpart_h}; + int extent[2] = {subpart_w, 1}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } + if (subpart_w < tex_w && subpart_h < tex_h) { void *data = DATA(src_y + subpart_h - 1, src_x + subpart_w - 1); - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, type, data); + int offset[2] = {subpart_w, subpart_h}; + int extent[2] = {1, 1}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } #undef DATA } @@ -253,8 +253,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, immUnbindProgram(); } - GPU_texture_unbind(texture); - GPU_texture_free(texture); + GPU_texture_unbind(tex); + GPU_texture_free(tex); /* Restore default. */ GPU_unpack_row_length_set(0); @@ -265,9 +265,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -280,9 +279,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, scaleX, scaleY, @@ -300,9 +298,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float xzoom, float yzoom, @@ -313,9 +310,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, 1.0f, 1.0f, @@ -333,9 +329,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float clip_min_x, float clip_min_y, @@ -350,9 +345,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, 1.0f, 1.0f, @@ -371,7 +365,7 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, void ED_draw_imbuf_clipping(ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, ColorManagedViewSettings *view_settings, ColorManagedDisplaySettings *display_settings, float clip_min_x, @@ -421,13 +415,13 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, if (ok) { if (ibuf->rect_float) { - int format = 0; + eGPUTextureFormat format = 0; if (ibuf->channels == 3) { - format = GL_RGB; + format = GPU_RGB16F; } else if (ibuf->channels == 4) { - format = GL_RGBA; + format = GPU_RGBA16F; } else { BLI_assert(!"Incompatible number of channels for GLSL display"); @@ -440,8 +434,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, ibuf->x, ibuf->y, format, - GL_FLOAT, - zoomfilter, + use_filter, ibuf->rect_float, clip_min_x, clip_min_y, @@ -459,9 +452,8 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - zoomfilter, + GPU_RGBA8, + use_filter, ibuf->rect, clip_min_x, clip_min_y, @@ -493,9 +485,8 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - zoomfilter, + GPU_RGBA8, + use_filter, display_buffer, clip_min_x, clip_min_y, @@ -513,7 +504,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, void ED_draw_imbuf(ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, ColorManagedViewSettings *view_settings, ColorManagedDisplaySettings *display_settings, float zoom_x, @@ -522,7 +513,7 @@ void ED_draw_imbuf(ImBuf *ibuf, ED_draw_imbuf_clipping(ibuf, x, y, - zoomfilter, + use_filter, view_settings, display_settings, 0.0f, @@ -537,7 +528,7 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float clip_min_x, float clip_min_y, float clip_max_x, @@ -553,7 +544,7 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, ED_draw_imbuf_clipping(ibuf, x, y, - zoomfilter, + use_filter, view_settings, display_settings, clip_min_x, @@ -565,9 +556,9 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, } void ED_draw_imbuf_ctx( - const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter, float zoom_x, float zoom_y) + const bContext *C, ImBuf *ibuf, float x, float y, bool use_filter, float zoom_x, float zoom_y) { - ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y); + ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, use_filter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y); } int ED_draw_imbuf_method(ImBuf *ibuf) diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 7983ac889ef..c17a34f97b9 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -475,7 +475,6 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult CTX_data_id_pointer_set(result, &obpose->id); } return 1; - return -1; /* found but not available */ } if (CTX_data_equals(member, "sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 1608e842376..40a452a5363 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -619,7 +619,7 @@ void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uin screen_preview_draw(screen, size_x, size_y); - GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, r_rect); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_UNSIGNED_BYTE, r_rect); GPU_offscreen_unbind(offscreen, true); GPU_offscreen_free(offscreen); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index b6f210d7f13..62720d8ca37 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -516,7 +516,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) ED_screen_areas_iter (win, screen, area) { /* set spacetype and region callbacks, calls init() */ /* sets subwindows for regions, adds handlers */ - ED_area_initialize(wm, win, area); + ED_area_init(wm, win, area); } /* wake up animtimer */ @@ -536,7 +536,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) } /* file read, set all screens, ... */ -void ED_screens_initialize(Main *bmain, wmWindowManager *wm) +void ED_screens_init(Main *bmain, wmWindowManager *wm) { wmWindow *win; @@ -897,7 +897,7 @@ static void screen_global_area_refresh(wmWindow *win, else { area = screen_area_create_with_geometry(&win->global_areas, rect, space_type); SpaceType *stype = BKE_spacetype_from_id(space_type); - SpaceLink *slink = stype->new (area, WM_window_get_active_scene(win)); + SpaceLink *slink = stype->create(area, WM_window_get_active_scene(win)); area->regionbase = slink->regionbase; @@ -985,39 +985,14 @@ void ED_screen_global_areas_refresh(wmWindow *win) /* -------------------------------------------------------------------- */ /* Screen changing */ -static bScreen *screen_fullscreen_find_associated_normal_screen(const Main *bmain, bScreen *screen) -{ - for (bScreen *screen_iter = bmain->screens.first; screen_iter; - screen_iter = screen_iter->id.next) { - if ((screen_iter != screen) && ELEM(screen_iter->state, SCREENMAXIMIZED, SCREENFULL)) { - ScrArea *area = screen_iter->areabase.first; - if (area && area->full == screen) { - return screen_iter; - } - } - } - - return screen; -} - /** * \return the screen to activate. * \warning The returned screen may not always equal \a screen_new! */ -bScreen *screen_change_prepare( +void screen_change_prepare( bScreen *screen_old, bScreen *screen_new, Main *bmain, bContext *C, wmWindow *win) { - /* validate screen, it's called with notifier reference */ - if (BLI_findindex(&bmain->screens, screen_new) == -1) { - return NULL; - } - - screen_new = screen_fullscreen_find_associated_normal_screen(bmain, screen_new); - - /* check for valid winid */ - if (!(screen_new->winid == 0 || screen_new->winid == win->winid)) { - return NULL; - } + BLI_assert(BLI_findindex(&bmain->screens, screen_new) != -1); if (screen_old != screen_new) { wmTimer *wt = screen_old->animtimer; @@ -1038,11 +1013,7 @@ bScreen *screen_change_prepare( if (wt) { screen_new->animtimer = wt; } - - return screen_new; } - - return NULL; } void screen_change_update(bContext *C, wmWindow *win, bScreen *screen) @@ -1075,12 +1046,20 @@ bool ED_screen_change(bContext *C, bScreen *screen) { Main *bmain = CTX_data_main(C); wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, screen); bScreen *screen_old = CTX_wm_screen(C); - bScreen *screen_new = screen_change_prepare(screen_old, screen, bmain, C, win); - if (screen_new) { - WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); - WM_window_set_active_screen(win, workspace, screen); + /* Get the actual layout/screen to be activated (guaranteed to be unused, even if that means + * having to duplicate an existing one). */ + WorkSpaceLayout *layout_new = ED_workspace_screen_change_ensure_unused_layout( + bmain, workspace, layout, layout, win); + bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new); + + screen_change_prepare(screen_old, screen_new, bmain, C, win); + + if (screen_old != screen_new) { + WM_window_set_active_screen(win, workspace, screen_new); screen_change_update(C, win, screen_new); return true; diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 21168a992b5..a5b5e222ae9 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -49,11 +49,11 @@ bScreen *screen_add(struct Main *bmain, const char *name, const rcti *rect); void screen_data_copy(bScreen *to, bScreen *from); void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new); void screen_change_update(struct bContext *C, wmWindow *win, bScreen *screen); -bScreen *screen_change_prepare(bScreen *screen_old, - bScreen *screen_new, - struct Main *bmain, - struct bContext *C, - wmWindow *win); +void screen_change_prepare(bScreen *screen_old, + bScreen *screen_new, + struct Main *bmain, + struct bContext *C, + wmWindow *win); ScrArea *area_split( const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge); int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index fde1498bc5e..f4d36a15d30 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -5184,7 +5184,7 @@ static void region_blend_end(bContext *C, ARegion *region, const bool is_running else { if (rgi->hidden) { rgi->region->flag |= rgi->hidden; - ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), rgi->area); + ED_area_init(CTX_wm_manager(C), CTX_wm_window(C), rgi->area); } /* area decoration needs redraw in end */ ED_area_tag_redraw(rgi->area); @@ -5215,7 +5215,7 @@ void ED_region_visibility_change_update_animated(bContext *C, ScrArea *area, ARe /* blend in, reinitialize regions because it got unhidden */ if (rgi->hidden == 0) { - ED_area_initialize(wm, win, area); + ED_area_init(wm, win, area); } else { WM_event_remove_handlers(C, ®ion->handlers); diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index 478a0adfd9a..b20dc80d158 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -88,22 +88,15 @@ static void workspace_change_update(WorkSpace *workspace_new, #endif } -static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) -{ - /* return false to stop the iterator if we've found a layout that can be activated */ - return workspace_layout_set_poll(layout) ? false : true; -} - static WorkSpaceLayout *workspace_change_get_new_layout(Main *bmain, WorkSpace *workspace_new, wmWindow *win) { - /* ED_workspace_duplicate may have stored a layout to activate - * once the workspace gets activated. */ WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); WorkSpaceLayout *layout_new; - bScreen *screen_new; + /* ED_workspace_duplicate may have stored a layout to activate + * once the workspace gets activated. */ if (win->workspace_hook->temp_workspace_store) { layout_new = win->workspace_hook->temp_layout_store; } @@ -113,20 +106,9 @@ static WorkSpaceLayout *workspace_change_get_new_layout(Main *bmain, layout_new = workspace_new->layouts.first; } } - screen_new = BKE_workspace_layout_screen_get(layout_new); - - if (screen_new->winid) { - /* screen is already used, try to find a free one */ - WorkSpaceLayout *layout_temp = BKE_workspace_layout_iter_circular( - workspace_new, layout_new, workspace_change_find_new_layout_cb, NULL, false); - if (!layout_temp) { - /* fallback solution: duplicate layout from old workspace */ - layout_temp = ED_workspace_layout_duplicate(bmain, workspace_new, layout_old, win); - } - layout_new = layout_temp; - } - return layout_new; + return ED_workspace_screen_change_ensure_unused_layout( + bmain, workspace_new, layout_new, layout_old, win); } /** @@ -153,10 +135,7 @@ bool ED_workspace_change(WorkSpace *workspace_new, bContext *C, wmWindowManager return false; } - screen_new = screen_change_prepare(screen_old, screen_new, bmain, C, win); - if (BKE_workspace_layout_screen_get(layout_new) != screen_new) { - layout_new = BKE_workspace_layout_find(workspace_new, screen_new); - } + screen_change_prepare(screen_old, screen_new, bmain, C, win); if (screen_new == NULL) { return false; diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c index 0af81e0db21..8a36cffa1f1 100644 --- a/source/blender/editors/screen/workspace_layout_edit.c +++ b/source/blender/editors/screen/workspace_layout_edit.c @@ -160,6 +160,66 @@ bool ED_workspace_layout_delete(WorkSpace *workspace, WorkSpaceLayout *layout_ol return false; } +static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) +{ + /* return false to stop the iterator if we've found a layout that can be activated */ + return workspace_layout_set_poll(layout) ? false : true; +} + +static bScreen *screen_fullscreen_find_associated_normal_screen(const Main *bmain, bScreen *screen) +{ + for (bScreen *screen_iter = bmain->screens.first; screen_iter; + screen_iter = screen_iter->id.next) { + if ((screen_iter != screen) && ELEM(screen_iter->state, SCREENMAXIMIZED, SCREENFULL)) { + ScrArea *area = screen_iter->areabase.first; + if (area && area->full == screen) { + return screen_iter; + } + } + } + + return screen; +} + +static bool screen_is_used_by_other_window(const wmWindow *win, const bScreen *screen) +{ + return BKE_screen_is_used(screen) && (screen->winid != win->winid); +} + +/** + * Make sure there is a non-fullscreen layout to switch to that is not used yet by an other window. + * Needed for workspace or screen switching to ensure valid screens. + * + * \param layout_fallback_base: As last resort, this layout is duplicated and returned. + */ +WorkSpaceLayout *ED_workspace_screen_change_ensure_unused_layout( + Main *bmain, + WorkSpace *workspace, + WorkSpaceLayout *layout_new, + const WorkSpaceLayout *layout_fallback_base, + wmWindow *win) +{ + WorkSpaceLayout *layout_temp = layout_new; + bScreen *screen_temp = BKE_workspace_layout_screen_get(layout_new); + + screen_temp = screen_fullscreen_find_associated_normal_screen(bmain, screen_temp); + layout_temp = BKE_workspace_layout_find(workspace, screen_temp); + + if (screen_is_used_by_other_window(win, screen_temp)) { + /* Screen is already used, try to find a free one. */ + layout_temp = BKE_workspace_layout_iter_circular( + workspace, layout_new, workspace_change_find_new_layout_cb, NULL, false); + screen_temp = layout_temp ? BKE_workspace_layout_screen_get(layout_temp) : NULL; + + if (!layout_temp || screen_is_used_by_other_window(win, screen_temp)) { + /* Fallback solution: duplicate layout. */ + layout_temp = ED_workspace_layout_duplicate(bmain, workspace, layout_fallback_base, win); + } + } + + return layout_temp; +} + static bool workspace_layout_cycle_iter_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) { /* return false to stop iterator when we have found a layout to activate */ diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index d0082769575..c5f1063d494 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -56,7 +56,6 @@ #include "DEG_depsgraph.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" @@ -456,7 +455,7 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) } buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex"); - BKE_curvemapping_initialize(br->curve); + BKE_curvemapping_init(br->curve); LoadTexData data = { .br = br, @@ -540,54 +539,6 @@ static int project_brush_radius(ViewContext *vc, float radius, const float locat return 0; } -static bool sculpt_get_brush_geometry(bContext *C, - ViewContext *vc, - int x, - int y, - int *pixel_radius, - float location[3], - UnifiedPaintSettings *ups) -{ - Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active_from_context(C); - float mouse[2]; - bool hit = false; - - mouse[0] = x; - mouse[1] = y; - - if (vc->obact->sculpt && vc->obact->sculpt->pbvh) { - if (!ups->stroke_active) { - hit = SCULPT_stroke_get_location(C, location, mouse); - } - else { - hit = ups->last_hit; - copy_v3_v3(location, ups->last_location); - } - } - - if (hit) { - Brush *brush = BKE_paint_brush(paint); - - *pixel_radius = project_brush_radius( - vc, BKE_brush_unprojected_radius_get(scene, brush), location); - - if (*pixel_radius == 0) { - *pixel_radius = BKE_brush_size_get(scene, brush); - } - - mul_m4_v3(vc->obact->obmat, location); - } - else { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - *pixel_radius = BKE_brush_size_get(scene, brush); - } - - return hit; -} - /* Draw an overlay that shows what effect the brush's texture will * have on brush strength. */ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, @@ -1042,10 +993,10 @@ static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc) /* Special actions taken when paint cursor goes over mesh */ /* TODO: sculpt only for now. */ -static void paint_cursor_on_hit(UnifiedPaintSettings *ups, - Brush *brush, - ViewContext *vc, - const float location[3]) +static void paint_cursor_update_unprojected_radius(UnifiedPaintSettings *ups, + Brush *brush, + ViewContext *vc, + const float location[3]) { float unprojected_radius, projected_radius; @@ -1077,18 +1028,6 @@ static void paint_cursor_on_hit(UnifiedPaintSettings *ups, } } -static bool ommit_cursor_drawing(Paint *paint, ePaintMode mode, Brush *brush) -{ - if (paint->flags & PAINT_SHOW_BRUSH) { - if (ELEM(mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D) && - brush->imagepaint_tool == PAINT_TOOL_FILL) { - return true; - } - return false; - } - return true; -} - static void cursor_draw_point_screen_space(const uint gpuattr, const ARegion *region, const float true_location[3], @@ -1188,8 +1127,27 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr, } } -static void sculpt_geometry_preview_lines_draw(const uint gpuattr, SculptSession *ss) +static void sculpt_geometry_preview_lines_draw(const uint gpuattr, + Brush *brush, + const bool is_multires, + SculptSession *ss) { + if (!(brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) { + return; + } + + if (is_multires) { + return; + } + + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return; + } + + if (!ss->deform_modifiers_active) { + return; + } + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.6f); /* Cursor normally draws on top, but for this part we need depth tests. */ @@ -1215,30 +1173,21 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, SculptSession static void SCULPT_layer_brush_height_preview_draw(const uint gpuattr, const Brush *brush, - const float obmat[4][4], - const float location[3], - const float normal[3], const float rds, const float line_width, const float outline_col[3], const float alpha) { - float cursor_trans[4][4], cursor_rot[4][4]; - float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; - float quat[4]; - float height_preview_trans[3]; - copy_m4_m4(cursor_trans, obmat); - madd_v3_v3v3fl(height_preview_trans, location, normal, brush->height); - translate_m4( - cursor_trans, height_preview_trans[0], height_preview_trans[1], height_preview_trans[2]); - rotation_between_vecs_to_quat(quat, z_axis, normal); - quat_to_mat4(cursor_rot, quat); + float cursor_trans[4][4]; + unit_m4(cursor_trans); + translate_m4(cursor_trans, 0.0f, 0.0f, brush->height); + GPU_matrix_push(); GPU_matrix_mul(cursor_trans); - GPU_matrix_mul(cursor_rot); GPU_line_width(line_width); immUniformColor3fvAlpha(outline_col, alpha * 0.5f); imm_draw_circle_wire_3d(gpuattr, 0, 0, rds, 80); + GPU_matrix_pop(); } static bool paint_use_2d_cursor(ePaintMode mode) @@ -1249,428 +1198,667 @@ static bool paint_use_2d_cursor(ePaintMode mode) return false; } -static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) +typedef enum PaintCursorDrawingType { + PAINT_CURSOR_CURVE, + PAINT_CURSOR_2D, + PAINT_CURSOR_3D, +} PaintCursorDrawingType; + +typedef struct PaintCursorContext { + bContext *C; + ARegion *region; + wmWindow *win; + wmWindowManager *wm; + Depsgraph *depsgraph; + Scene *scene; + UnifiedPaintSettings *ups; + Brush *brush; + Paint *paint; + ePaintMode mode; + ViewContext vc; + + /* Sculpt related data. */ + Sculpt *sd; + SculptSession *ss; + int prev_active_vertex_index; + bool is_stroke_active; + bool is_cursor_over_mesh; + bool is_multires; + float radius; + + /* 3D view cursor position and normal. */ + float location[3]; + float scene_space_location[3]; + float normal[3]; + + /* Cursor main colors. */ + float outline_col[3]; + float outline_alpha; + + /* GPU attribute for drawing. */ + uint pos; + + PaintCursorDrawingType cursor_type; + + /* This variable is set after drawing the overlay, not on initialization. It can't be used for + * checking if alpha overlay is enabled before drawing it. */ + bool alpha_overlay_drawn; + + float zoomx; + int x, y; + float translation[2]; + + float final_radius; + int pixel_radius; + +} PaintCursorContext; + +static bool paint_cursor_context_init(bContext *C, + const int x, + const int y, + PaintCursorContext *pcontext) { ARegion *region = CTX_wm_region(C); if (region && region->regiontype != RGN_TYPE_WINDOW) { - return; + return false; } - const wmWindowManager *wm = CTX_wm_manager(C); - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - ePaintMode mode = BKE_paintmode_get_active_from_context(C); + pcontext->C = C; + pcontext->region = region; + pcontext->wm = CTX_wm_manager(C); + pcontext->win = CTX_wm_window(C); + pcontext->depsgraph = CTX_data_depsgraph_pointer(C); + pcontext->scene = CTX_data_scene(C); + pcontext->ups = &pcontext->scene->toolsettings->unified_paint_settings; + pcontext->paint = BKE_paint_get_active_from_context(C); + pcontext->brush = BKE_paint_brush(pcontext->paint); + pcontext->mode = BKE_paintmode_get_active_from_context(C); + + ED_view3d_viewcontext_init(C, &pcontext->vc, pcontext->depsgraph); + + if (pcontext->brush->flag & BRUSH_CURVE) { + pcontext->cursor_type = PAINT_CURSOR_CURVE; + } + else if (paint_use_2d_cursor(pcontext->mode)) { + pcontext->cursor_type = PAINT_CURSOR_2D; + } + else { + pcontext->cursor_type = PAINT_CURSOR_3D; + } - /* 2d or 3d painting? */ - const bool use_2d_cursor = paint_use_2d_cursor(mode); + pcontext->x = x; + pcontext->y = y; + pcontext->translation[0] = (float)x; + pcontext->translation[1] = (float)y; - /* check that brush drawing is enabled */ - if (ommit_cursor_drawing(paint, mode, brush)) { - return; + float zoomx, zoomy; + get_imapaint_zoom(C, &zoomx, &zoomy); + pcontext->zoomx = max_ff(zoomx, zoomy); + pcontext->final_radius = (BKE_brush_size_get(pcontext->scene, pcontext->brush) * zoomx); + + /* There is currently no way to check if the direction is invertex before starting the stroke, so + * this does not reflect the state of the brush in the UI. */ + if (((pcontext->ups->draw_inverted == 0) ^ ((pcontext->brush->flag & BRUSH_DIR_IN) == 0)) && + BKE_brush_sculpt_has_secondary_color(pcontext->brush)) { + copy_v3_v3(pcontext->outline_col, pcontext->brush->sub_col); + } + else { + copy_v3_v3(pcontext->outline_col, pcontext->brush->add_col); } + pcontext->outline_alpha = pcontext->brush->add_col[3]; - /* Can't use stroke vc here because this will be called during - * mouse over too, not just during a stroke. */ - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc, depsgraph); + Object *active_object = pcontext->vc.obact; + pcontext->ss = active_object ? active_object->sculpt : NULL; - if (vc.rv3d && (vc.rv3d->rflag & RV3D_NAVIGATING)) { - return; + pcontext->is_stroke_active = pcontext->ups->stroke_active; + + return true; +} + +static void paint_cursor_update_pixel_radius(PaintCursorContext *pcontext) +{ + if (pcontext->is_cursor_over_mesh) { + Brush *brush = BKE_paint_brush(pcontext->paint); + pcontext->pixel_radius = project_brush_radius( + &pcontext->vc, + BKE_brush_unprojected_radius_get(pcontext->scene, brush), + pcontext->location); + + if (pcontext->pixel_radius == 0) { + pcontext->pixel_radius = BKE_brush_size_get(pcontext->scene, brush); + } + + copy_v3_v3(pcontext->scene_space_location, pcontext->location); + mul_m4_v3(pcontext->vc.obact->obmat, pcontext->scene_space_location); } + else { + Sculpt *sd = CTX_data_tool_settings(pcontext->C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - /* Skip everything and draw brush here. */ - if (brush->flag & BRUSH_CURVE) { - paint_draw_curve_cursor(brush, &vc); - return; + pcontext->pixel_radius = BKE_brush_size_get(pcontext->scene, brush); } +} - float zoomx, zoomy; - get_imapaint_zoom(C, &zoomx, &zoomy); - zoomx = max_ff(zoomx, zoomy); +static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcontext) +{ + BLI_assert(pcontext->ss != NULL); + BLI_assert(pcontext->mode == PAINT_MODE_SCULPT); + + bContext *C = pcontext->C; + SculptSession *ss = pcontext->ss; + Brush *brush = pcontext->brush; + Scene *scene = pcontext->scene; + UnifiedPaintSettings *ups = pcontext->ups; + ViewContext *vc = &pcontext->vc; + SculptCursorGeometryInfo gi; + + float mouse[2] = {pcontext->x - pcontext->region->winrct.xmin, + pcontext->y - pcontext->region->winrct.ymin}; + + /* This updates the active vertex, which is needed for most of the Sculpt/Vertex Colors tools to + * work correctly */ + pcontext->prev_active_vertex_index = ss->active_vertex_index; + if (!ups->stroke_active) { + pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update( + C, &gi, mouse, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); + copy_v3_v3(pcontext->location, gi.location); + copy_v3_v3(pcontext->normal, gi.normal); + } + else { + pcontext->is_cursor_over_mesh = ups->last_hit; + copy_v3_v3(pcontext->location, ups->last_location); + } - /* Set various defaults. */ - const float *outline_col = brush->add_col; - const float outline_alpha = brush->add_col[3]; - float translation[2] = {x, y}; - float final_radius = (BKE_brush_size_get(scene, brush) * zoomx); + paint_cursor_update_pixel_radius(pcontext); - /* Don't calculate rake angles while a stroke is active because the rake variables are global - * and we may get interference with the stroke itself. - * For line strokes, such interference is visible. */ - if (!ups->stroke_active) { - paint_calculate_rake_rotation(ups, brush, translation); + if (BKE_brush_use_locked_size(scene, brush)) { + BKE_brush_size_set(scene, brush, pcontext->pixel_radius); + } + + if (pcontext->is_cursor_over_mesh) { + paint_cursor_update_unprojected_radius(ups, brush, vc, pcontext->scene_space_location); } - /* Draw overlay. */ - bool alpha_overlay_active = paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); + pcontext->is_multires = ss->pbvh != NULL && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; - if (ups->draw_anchored) { - final_radius = ups->anchored_size; - copy_v2_fl2(translation, - ups->anchored_initial_mouse[0] + region->winrct.xmin, - ups->anchored_initial_mouse[1] + region->winrct.ymin); + pcontext->sd = CTX_data_tool_settings(pcontext->C)->sculpt; +} + +static void paint_update_mouse_cursor(PaintCursorContext *pcontext) +{ + WM_cursor_set(pcontext->win, WM_CURSOR_PAINT); +} + +static void paint_draw_2D_view_brush_cursor(PaintCursorContext *pcontext) +{ + immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha); + + /* Draw brush outline. */ + if (pcontext->ups->stroke_active && BKE_brush_use_size_pressure(pcontext->brush)) { + imm_draw_circle_wire_2d(pcontext->pos, + pcontext->translation[0], + pcontext->translation[1], + pcontext->final_radius * pcontext->ups->size_pressure_value, + 40); + /* Outer at half alpha. */ + immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha * 0.5f); } - /* Make lines pretty. */ + GPU_line_width(1.0f); + imm_draw_circle_wire_2d(pcontext->pos, + pcontext->translation[0], + pcontext->translation[1], + pcontext->final_radius, + 40); + + immUnbindProgram(); +} + +static void paint_draw_legacy_3D_view_brush_cursor(PaintCursorContext *pcontext) +{ + GPU_line_width(1.0f); + immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha); + imm_draw_circle_wire_3d(pcontext->pos, + pcontext->translation[0], + pcontext->translation[1], + pcontext->final_radius, + 40); +} + +static void paint_draw_3D_view_inactive_brush_cursor(PaintCursorContext *pcontext) +{ + GPU_line_width(1.0f); + /* Reduce alpha to increase the contrast when the cursor is over the mesh. */ + immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha * 0.8); + imm_draw_circle_wire_3d(pcontext->pos, + pcontext->translation[0], + pcontext->translation[1], + pcontext->final_radius, + 80); + immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha * 0.35f); + imm_draw_circle_wire_3d(pcontext->pos, + pcontext->translation[0], + pcontext->translation[1], + pcontext->final_radius * clamp_f(pcontext->brush->alpha, 0.0f, 1.0f), + 80); +} + +static void paint_cursor_update_object_space_radius(PaintCursorContext *pcontext) +{ + if (!BKE_brush_use_locked_size(pcontext->scene, pcontext->brush)) { + pcontext->radius = paint_calc_object_space_radius( + &pcontext->vc, pcontext->location, BKE_brush_size_get(pcontext->scene, pcontext->brush)); + } + else { + pcontext->radius = BKE_brush_unprojected_radius_get(pcontext->scene, pcontext->brush); + } +} + +static void paint_cursor_drawing_setup_cursor_space(PaintCursorContext *pcontext) +{ + float cursor_trans[4][4], cursor_rot[4][4]; + float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; + float quat[4]; + copy_m4_m4(cursor_trans, pcontext->vc.obact->obmat); + translate_m4(cursor_trans, pcontext->location[0], pcontext->location[1], pcontext->location[2]); + rotation_between_vecs_to_quat(quat, z_axis, pcontext->normal); + quat_to_mat4(cursor_rot, quat); + GPU_matrix_mul(cursor_trans); + GPU_matrix_mul(cursor_rot); +} + +static void paint_cursor_draw_main_inactive_cursor(PaintCursorContext *pcontext) +{ + immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha); GPU_line_width(2.0f); + imm_draw_circle_wire_3d(pcontext->pos, 0, 0, pcontext->radius, 80); - /* TODO: also set blend mode? */ - GPU_blend(true); + GPU_line_width(1.0f); + immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha * 0.5f); + imm_draw_circle_wire_3d( + pcontext->pos, 0, 0, pcontext->radius * clamp_f(pcontext->brush->alpha, 0.0f, 1.0f), 80); +} - GPU_line_smooth(true); +static void paint_cursor_pose_brush_segments_draw(PaintCursorContext *pcontext) +{ + SculptSession *ss = pcontext->ss; + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); + GPU_line_width(2.0f); - if (use_2d_cursor) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBegin(GPU_PRIM_LINES, ss->pose_ik_chain_preview->tot_segments * 2); + for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { + immVertex3fv(pcontext->pos, ss->pose_ik_chain_preview->segments[i].initial_orig); + immVertex3fv(pcontext->pos, ss->pose_ik_chain_preview->segments[i].initial_head); + } - immUniformColor3fvAlpha(outline_col, outline_alpha); + immEnd(); +} - /* Draw brush outline. */ - if (ups->stroke_active && BKE_brush_use_size_pressure(brush)) { - imm_draw_circle_wire_2d( - pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); - /* Outer at half alpha. */ - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); - } +static void paint_cursor_pose_brush_origins_draw(PaintCursorContext *pcontext) +{ - GPU_line_width(1.0f); - imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40); + SculptSession *ss = pcontext->ss; + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); + for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { + cursor_draw_point_screen_space(pcontext->pos, + pcontext->region, + ss->pose_ik_chain_preview->segments[i].initial_orig, + pcontext->vc.obact->obmat, + 3); } - else { - /* 3D Painting. */ - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - - /* TODO: as sculpt and other paint modes are unified, this - * special mode of drawing will go away. */ - Object *obact = vc.obact; - SculptSession *ss = obact ? obact->sculpt : NULL; - if ((mode == PAINT_MODE_SCULPT) && ss) { - float location[3]; - int pixel_radius; - - /* Test if brush is over the mesh. */ - bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups); - - if (BKE_brush_use_locked_size(scene, brush)) { - BKE_brush_size_set(scene, brush, pixel_radius); - } +} - /* Check if brush is subtracting, use different color then */ - /* TODO: no way currently to know state of pen flip or - * invert key modifier without starting a stroke. */ - if (((ups->draw_inverted == 0) ^ ((brush->flag & BRUSH_DIR_IN) == 0)) && - BKE_brush_sculpt_has_secondary_color(brush)) { - outline_col = brush->sub_col; - } +static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *pcontext) +{ + Brush *brush = pcontext->brush; + + /* 2D fallof is better represented with the default 2D cursor, there is no need to draw anything + * else. */ + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + paint_draw_legacy_3D_view_brush_cursor(pcontext); + return; + } + + if (pcontext->alpha_overlay_drawn) { + paint_draw_legacy_3D_view_brush_cursor(pcontext); + return; + } - /* Only do if brush is over the mesh. */ - if (hit) { - paint_cursor_on_hit(ups, brush, &vc, location); + if (!pcontext->is_cursor_over_mesh) { + paint_draw_3D_view_inactive_brush_cursor(pcontext); + return; + } + + paint_cursor_update_object_space_radius(pcontext); + + const bool update_previews = pcontext->prev_active_vertex_index != + SCULPT_active_vertex_get(pcontext->ss); + + /* Setup drawing. */ + wmViewport(&pcontext->region->winrct); + + /* Drawing of Cursor overlays in 2D screen space. */ + + /* Cursor location symmetry points. */ + + const float *active_vertex_co = SCULPT_active_vertex_co_get(pcontext->ss); + if (len_v3v3(active_vertex_co, pcontext->location) < pcontext->radius) { + immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha); + cursor_draw_point_with_symmetry(pcontext->pos, + pcontext->region, + active_vertex_co, + pcontext->sd, + pcontext->vc.obact, + pcontext->radius); + } + + /* Pose brush updates and rotation origins. */ + + if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + /* Just after switching to the Pose Brush, the active vertex can be the same and the + * cursor won't be tagged to update, so always initialize the preview chain if it is + * null before drawing it. */ + SculptSession *ss = pcontext->ss; + if (update_previews || !ss->pose_ik_chain_preview) { + BKE_sculpt_update_object_for_edit( + pcontext->depsgraph, pcontext->vc.obact, true, false, false); + + /* Free the previous pose brush preview. */ + if (ss->pose_ik_chain_preview) { + SCULPT_pose_ik_chain_free(ss->pose_ik_chain_preview); } + + /* Generate a new pose brush preview from the current cursor location. */ + ss->pose_ik_chain_preview = SCULPT_pose_ik_chain_init( + pcontext->sd, pcontext->vc.obact, ss, brush, pcontext->location, pcontext->radius); } - immUniformColor3fvAlpha(outline_col, outline_alpha); + /* Draw the pose brush rotation origins. */ + paint_cursor_pose_brush_origins_draw(pcontext); + } - if (ups->stroke_active && BKE_brush_use_size_pressure(brush) && mode != PAINT_MODE_SCULPT) { - imm_draw_circle_wire_3d( - pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); - /* Outer at half alpha. */ - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); - } + /* Setup 3D perspective drawing. */ + GPU_matrix_push_projection(); + ED_view3d_draw_setup_view(pcontext->wm, + pcontext->win, + pcontext->depsgraph, + pcontext->scene, + pcontext->region, + CTX_wm_view3d(pcontext->C), + NULL, + NULL, + NULL); - /* Only sculpt mode cursor for now. */ - /* Disable for PBVH_GRIDS. */ - bool is_multires = ss && ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; + GPU_matrix_push(); + GPU_matrix_mul(pcontext->vc.obact->obmat); - SculptCursorGeometryInfo gi; - float mouse[2] = {x - region->winrct.xmin, y - region->winrct.ymin}; - int prev_active_vertex_index = -1; - bool is_cursor_over_mesh = false; + /* Drawing Cursor overlays in 3D object space. */ + if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) { + SCULPT_geometry_preview_lines_update(pcontext->C, pcontext->ss, pcontext->radius); + sculpt_geometry_preview_lines_draw( + pcontext->pos, pcontext->brush, pcontext->is_multires, pcontext->ss); + } - /* Update the active vertex. */ - if ((mode == PAINT_MODE_SCULPT) && ss && !ups->stroke_active) { - prev_active_vertex_index = ss->active_vertex_index; - is_cursor_over_mesh = SCULPT_cursor_geometry_info_update( - C, &gi, mouse, (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); + if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + paint_cursor_pose_brush_segments_draw(pcontext); + } + + GPU_matrix_pop(); + + /* Drawing Cursor overlays in Paint Cursor space (as additional info on top of the brush cursor) + */ + GPU_matrix_push(); + paint_cursor_drawing_setup_cursor_space(pcontext); + /* Main inactive cursor. */ + paint_cursor_draw_main_inactive_cursor(pcontext); + + /* Cloth brush local simulation areas. */ + if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && + brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) { + const float white[3] = {1.0f, 1.0f, 1.0f}; + const float zero_v[3] = {0.0f}; + /* This functions sets its own drawing space in order to draw the simulation limits when the + * cursor is active. When used here, this cursor overlay is already in cursor space, so its + * position and normal should be set to 0. */ + SCULPT_cloth_simulation_limits_draw( + pcontext->pos, brush, zero_v, zero_v, pcontext->radius, 1.0f, white, 0.25f); + } + + /* Layer brush height. */ + if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { + SCULPT_layer_brush_height_preview_draw(pcontext->pos, + brush, + pcontext->radius, + 1.0f, + pcontext->outline_col, + pcontext->outline_alpha); + } + + GPU_matrix_pop(); + + /* Reset drawing. */ + GPU_matrix_pop_projection(); + wmWindowViewport(pcontext->win); +} + +static void paint_cursor_cursor_draw_3d_view_brush_cursor_active(PaintCursorContext *pcontext) +{ + BLI_assert(pcontext->ss != NULL); + BLI_assert(pcontext->mode == PAINT_MODE_SCULPT); + + SculptSession *ss = pcontext->ss; + Brush *brush = pcontext->brush; + + /* The cursor can be updated as active before creating the StrokeCache, so this needs to be + * checked. */ + if (!ss->cache) { + return; + } + + /* Most of the brushes initialize the necessary data for the custom cursor drawing after the + * first brush step, so make sure that it is not drawn before being initialized. */ + if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { + return; + } + + /* Setup drawing. */ + wmViewport(&pcontext->region->winrct); + GPU_matrix_push_projection(); + ED_view3d_draw_setup_view(pcontext->wm, + pcontext->win, + pcontext->depsgraph, + pcontext->scene, + pcontext->region, + CTX_wm_view3d(pcontext->C), + NULL, + NULL, + NULL); + GPU_matrix_push(); + GPU_matrix_mul(pcontext->vc.obact->obmat); + + /* Draw the special active cursors different tools may have. */ + + if (brush->sculpt_tool == SCULPT_TOOL_GRAB) { + sculpt_geometry_preview_lines_draw(pcontext->pos, brush, pcontext->is_multires, ss); + } + + if (brush->sculpt_tool == SCULPT_TOOL_MULTIPLANE_SCRAPE) { + SCULPT_multiplane_scrape_preview_draw( + pcontext->pos, brush, ss, pcontext->outline_col, pcontext->outline_alpha); + } + + if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { + if (brush->cloth_force_falloff_type == BRUSH_CLOTH_FORCE_FALLOFF_PLANE) { + SCULPT_cloth_plane_falloff_preview_draw( + pcontext->pos, ss, pcontext->outline_col, pcontext->outline_alpha); } - /* Use special paint crosshair cursor in all paint modes. */ - wmWindow *win = CTX_wm_window(C); - WM_cursor_set(win, WM_CURSOR_PAINT); - - if ((mode == PAINT_MODE_SCULPT) && ss && - (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - - if (!ups->stroke_active) { - bool update_previews = false; - if (is_cursor_over_mesh && !alpha_overlay_active) { - - if (prev_active_vertex_index != ss->active_vertex_index) { - update_previews = true; - } - - float rds; - if (!BKE_brush_use_locked_size(scene, brush)) { - rds = paint_calc_object_space_radius( - &vc, gi.location, BKE_brush_size_get(scene, brush)); - } - else { - rds = BKE_brush_unprojected_radius_get(scene, brush); - } - - wmViewport(®ion->winrct); - - /* Draw 3D active vertex preview with symmetry. */ - if (len_v3v3(gi.active_vertex_co, gi.location) < rds) { - cursor_draw_point_with_symmetry(pos, region, gi.active_vertex_co, sd, vc.obact, rds); - } - - /* Draw pose brush origins. */ - if (brush->sculpt_tool == SCULPT_TOOL_POSE) { - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); - - /* Just after switching to the Pose Brush, the active vertex can be the same and the - * cursor won't be tagged to update, so always initialize the preview chain if it is - * null before drawing it. */ - if (update_previews || !ss->pose_ik_chain_preview) { - BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false, false); - - /* Free the previous pose brush preview. */ - if (ss->pose_ik_chain_preview) { - SCULPT_pose_ik_chain_free(ss->pose_ik_chain_preview); - } - - /* Generate a new pose brush preview from the current cursor location. */ - ss->pose_ik_chain_preview = SCULPT_pose_ik_chain_init( - sd, vc.obact, ss, brush, gi.location, rds); - } - - /* Draw the pose brush rotation origins. */ - for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { - cursor_draw_point_screen_space(pos, - region, - ss->pose_ik_chain_preview->segments[i].initial_orig, - vc.obact->obmat, - 3); - } - } - - /* Draw 3D brush cursor. */ - GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(wm, - CTX_wm_window(C), - CTX_data_depsgraph_pointer(C), - CTX_data_scene(C), - region, - CTX_wm_view3d(C), - NULL, - NULL, - NULL); - - float cursor_trans[4][4], cursor_rot[4][4]; - float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; - float quat[4]; - - copy_m4_m4(cursor_trans, vc.obact->obmat); - translate_m4(cursor_trans, gi.location[0], gi.location[1], gi.location[2]); - rotation_between_vecs_to_quat(quat, z_axis, gi.normal); - quat_to_mat4(cursor_rot, quat); - - GPU_matrix_push(); - GPU_matrix_mul(cursor_trans); - GPU_matrix_mul(cursor_rot); - immUniformColor3fvAlpha(outline_col, outline_alpha); - GPU_line_width(2.0f); - imm_draw_circle_wire_3d(pos, 0, 0, rds, 80); - - GPU_line_width(1.0f); - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); - imm_draw_circle_wire_3d(pos, 0, 0, rds * clamp_f(brush->alpha, 0.0f, 1.0f), 80); - GPU_matrix_pop(); - - /* Cloth brush simulation areas. */ - if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { - GPU_matrix_push(); - const float white[3] = {1.0f, 1.0f, 1.0f}; - SCULPT_cloth_simulation_limits_draw( - pos, brush, vc.obact->obmat, gi.location, gi.normal, rds, 1.0f, white, 0.25f); - GPU_matrix_pop(); - } - - /* Layer brush height. */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { - GPU_matrix_push(); - SCULPT_layer_brush_height_preview_draw(pos, - brush, - vc.obact->obmat, - gi.location, - gi.normal, - rds, - 1.0f, - outline_col, - outline_alpha); - GPU_matrix_pop(); - } - - /* Update and draw dynamic mesh preview lines. */ - GPU_matrix_push(); - GPU_matrix_mul(vc.obact->obmat); - if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) && - !is_multires) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->deform_modifiers_active) { - SCULPT_geometry_preview_lines_update(C, ss, rds); - sculpt_geometry_preview_lines_draw(pos, ss); - } - } - - /* Draw pose brush line preview. */ - if (brush->sculpt_tool == SCULPT_TOOL_POSE) { - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); - GPU_line_width(2.0f); - - immBegin(GPU_PRIM_LINES, ss->pose_ik_chain_preview->tot_segments * 2); - for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { - immVertex3fv(pos, ss->pose_ik_chain_preview->segments[i].initial_orig); - immVertex3fv(pos, ss->pose_ik_chain_preview->segments[i].initial_head); - } - - immEnd(); - } - - GPU_matrix_pop(); - - GPU_matrix_pop_projection(); - - wmWindowViewport(win); - } - else { - /* Draw default cursor when the mouse is not over the mesh or there are no supported - * overlays active. */ - GPU_line_width(1.0f); - /* Reduce alpha to increase the contrast when the cursor is over the mesh. */ - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.8); - imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 80); - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.35f); - imm_draw_circle_wire_3d(pos, - translation[0], - translation[1], - final_radius * clamp_f(brush->alpha, 0.0f, 1.0f), - 80); - } - } - else { - if (vc.obact->sculpt->cache && - !SCULPT_stroke_is_first_brush_step_of_symmetry_pass(vc.obact->sculpt->cache)) { - wmViewport(®ion->winrct); - - /* Draw cached dynamic mesh preview lines. */ - if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) && - !is_multires) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->deform_modifiers_active) { - GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(wm, - CTX_wm_window(C), - CTX_data_depsgraph_pointer(C), - CTX_data_scene(C), - region, - CTX_wm_view3d(C), - NULL, - NULL, - NULL); - GPU_matrix_push(); - GPU_matrix_mul(vc.obact->obmat); - sculpt_geometry_preview_lines_draw(pos, ss); - GPU_matrix_pop(); - GPU_matrix_pop_projection(); - } - } - - if (brush->sculpt_tool == SCULPT_TOOL_MULTIPLANE_SCRAPE && - brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW && - !SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(wm, - CTX_wm_window(C), - CTX_data_depsgraph_pointer(C), - CTX_data_scene(C), - region, - CTX_wm_view3d(C), - NULL, - NULL, - NULL); - GPU_matrix_push(); - GPU_matrix_mul(vc.obact->obmat); - SCULPT_multiplane_scrape_preview_draw(pos, ss, outline_col, outline_alpha); - GPU_matrix_pop(); - GPU_matrix_pop_projection(); - } - - if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && - !SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(CTX_wm_manager(C), - CTX_wm_window(C), - CTX_data_depsgraph_pointer(C), - CTX_data_scene(C), - region, - CTX_wm_view3d(C), - NULL, - NULL, - NULL); - - /* Plane falloff preview */ - if (brush->cloth_force_falloff_type == BRUSH_CLOTH_FORCE_FALLOFF_PLANE) { - GPU_matrix_push(); - GPU_matrix_mul(vc.obact->obmat); - SCULPT_cloth_plane_falloff_preview_draw(pos, ss, outline_col, outline_alpha); - GPU_matrix_pop(); - } - - /* Display the simulation limits if sculpting outside them. */ - /* This does not makes much sense of plane fallof as the fallof is infinte. */ - else if (brush->cloth_force_falloff_type == BRUSH_CLOTH_FORCE_FALLOFF_RADIAL) { - if (len_v3v3(ss->cache->true_location, ss->cache->true_initial_location) > - ss->cache->radius * (1.0f + brush->cloth_sim_limit)) { - const float red[3] = {1.0f, 0.2f, 0.2f}; - GPU_matrix_push(); - SCULPT_cloth_simulation_limits_draw(pos, - brush, - vc.obact->obmat, - ss->cache->true_initial_location, - ss->cache->true_initial_normal, - ss->cache->radius, - 2.0f, - red, - 0.8f); - GPU_matrix_pop(); - } - } - - GPU_matrix_pop_projection(); - } - - wmWindowViewport(win); - } + else if (brush->cloth_force_falloff_type == BRUSH_CLOTH_FORCE_FALLOFF_RADIAL && + brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) { + /* Display the simulation limits if sculpting outside them. */ + /* This does not makes much sense of plane fallof as the fallof is infinte or global. */ + + if (len_v3v3(ss->cache->true_location, ss->cache->true_initial_location) > + ss->cache->radius * (1.0f + brush->cloth_sim_limit)) { + const float red[3] = {1.0f, 0.2f, 0.2f}; + SCULPT_cloth_simulation_limits_draw(pcontext->pos, + brush, + ss->cache->true_initial_location, + ss->cache->true_initial_normal, + ss->cache->radius, + 2.0f, + red, + 0.8f); } } - else { - /* Draw default cursor in unsupported modes. */ - GPU_line_width(1.0f); - imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40); + } + + GPU_matrix_pop(); + + /* This Cloth brush cursor overlay always works in cursor space. */ + paint_cursor_drawing_setup_cursor_space(pcontext); + + GPU_matrix_pop_projection(); + wmWindowViewport(pcontext->win); +} + +static void paint_cursor_draw_3D_view_brush_cursor(PaintCursorContext *pcontext) +{ + + /* These paint tools are not using the SculptSession, so they need to use the default 2D brush + * cursor in the 3D view. */ + if (pcontext->mode != PAINT_MODE_SCULPT || !pcontext->ss) { + paint_draw_legacy_3D_view_brush_cursor(pcontext); + return; + } + + paint_cursor_sculpt_session_update_and_init(pcontext); + + if (pcontext->is_stroke_active) { + paint_cursor_cursor_draw_3d_view_brush_cursor_active(pcontext); + } + else { + paint_cursor_draw_3d_view_brush_cursor_inactive(pcontext); + } +} + +static bool paint_cursor_is_3d_view_navigating(PaintCursorContext *pcontext) +{ + ViewContext *vc = &pcontext->vc; + return vc->rv3d && (vc->rv3d->rflag & RV3D_NAVIGATING); +} + +static bool paint_cursor_is_brush_cursor_enabled(PaintCursorContext *pcontext) +{ + if (pcontext->paint->flags & PAINT_SHOW_BRUSH) { + if (ELEM(pcontext->mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D) && + pcontext->brush->imagepaint_tool == PAINT_TOOL_FILL) { + return false; } + return true; } + return false; +} - immUnbindProgram(); +static void paint_cursor_update_rake_rotation(PaintCursorContext *pcontext) +{ + /* Don't calculate rake angles while a stroke is active because the rake variables are global + * and we may get interference with the stroke itself. + * For line strokes, such interference is visible. */ + if (!pcontext->ups->stroke_active) { + paint_calculate_rake_rotation(pcontext->ups, pcontext->brush, pcontext->translation); + } +} - /* Restore GL state. */ +static void paint_cursor_check_and_draw_alpha_overlays(PaintCursorContext *pcontext) +{ + pcontext->alpha_overlay_drawn = paint_draw_alpha_overlay(pcontext->ups, + pcontext->brush, + &pcontext->vc, + pcontext->x, + pcontext->y, + pcontext->zoomx, + pcontext->mode); +} + +static void paint_cursor_update_anchored_location(PaintCursorContext *pcontext) +{ + UnifiedPaintSettings *ups = pcontext->ups; + if (ups->draw_anchored) { + pcontext->final_radius = ups->anchored_size; + copy_v2_fl2(pcontext->translation, + ups->anchored_initial_mouse[0] + pcontext->region->winrct.xmin, + ups->anchored_initial_mouse[1] + pcontext->region->winrct.ymin); + } +} + +static void paint_cursor_setup_2D_drawing(PaintCursorContext *pcontext) +{ + GPU_line_width(2.0f); + GPU_blend(true); + GPU_line_smooth(true); + pcontext->pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); +} + +static void paint_cursor_setup_3D_drawing(PaintCursorContext *pcontext) +{ + GPU_line_width(2.0f); + GPU_blend(true); + GPU_line_smooth(true); + pcontext->pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); +} + +static void paint_cursor_restore_drawing_state(void) +{ + immUnbindProgram(); GPU_blend(false); GPU_line_smooth(false); } +static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) +{ + PaintCursorContext pcontext; + if (!paint_cursor_context_init(C, x, y, &pcontext)) { + return; + } + + if (!paint_cursor_is_brush_cursor_enabled(&pcontext)) { + return; + } + if (paint_cursor_is_3d_view_navigating(&pcontext)) { + return; + } + + switch (pcontext.cursor_type) { + case PAINT_CURSOR_CURVE: + paint_draw_curve_cursor(pcontext.brush, &pcontext.vc); + break; + case PAINT_CURSOR_2D: + paint_cursor_update_rake_rotation(&pcontext); + paint_cursor_check_and_draw_alpha_overlays(&pcontext); + paint_cursor_update_anchored_location(&pcontext); + + paint_cursor_setup_2D_drawing(&pcontext); + paint_draw_2D_view_brush_cursor(&pcontext); + paint_cursor_restore_drawing_state(); + break; + case PAINT_CURSOR_3D: + paint_update_mouse_cursor(&pcontext); + + paint_cursor_update_rake_rotation(&pcontext); + paint_cursor_check_and_draw_alpha_overlays(&pcontext); + paint_cursor_update_anchored_location(&pcontext); + + paint_cursor_setup_3D_drawing(&pcontext); + paint_cursor_draw_3D_view_brush_cursor(&pcontext); + paint_cursor_restore_drawing_state(); + break; + } +} + /* Public API */ void paint_cursor_start(Paint *p, bool (*poll)(bContext *C)) diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 7ee3d991eb7..34c9cac67c8 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -74,7 +74,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_state.h" @@ -182,7 +181,7 @@ void imapaint_image_update( int h = imapaintpartial.y2 - imapaintpartial.y1; if (w && h) { /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h); + BKE_image_update_gputexture(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h); } } } @@ -1164,9 +1163,9 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint); if (U.glreslimit != 0) { - GPU_free_images(bmain); + BKE_image_free_all_gputextures(bmain); } - GPU_paint_set_mipmap(bmain, 0); + BKE_image_paint_set_mipmap(bmain, 0); toggle_paint_cursor(scene, true); @@ -1189,9 +1188,9 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob) ob->mode &= ~OB_MODE_TEXTURE_PAINT; if (U.glreslimit != 0) { - GPU_free_images(bmain); + BKE_image_free_all_gputextures(bmain); } - GPU_paint_set_mipmap(bmain, 1); + BKE_image_paint_set_mipmap(bmain, 1); toggle_paint_cursor(scene, false); Mesh *me = BKE_mesh_from_object(ob); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index a7f09390a3d..6d588d1450b 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -57,8 +57,6 @@ #include "UI_view2d.h" -#include "GPU_draw.h" - #include "paint_intern.h" /* Brush Painting for 2D image editor */ @@ -1784,7 +1782,7 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final) if (final) { if (s->image && !(s->sima && s->sima->lock)) { - GPU_free_image(s->image); + BKE_image_free_gputextures(s->image); } /* compositor listener deals with updating */ diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 5af3a3f4241..af2762889e8 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -100,8 +100,6 @@ #include "RNA_define.h" #include "RNA_enum_types.h" -#include "GPU_draw.h" - #include "IMB_colormanagement.h" //#include "bmesh_tools.h" @@ -6134,7 +6132,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) project_image_refresh_tagged(&ps); for (a = 0; a < ps.image_tot; a++) { - GPU_free_image(ps.projImages[a].ima); + BKE_image_free_gputextures(ps.projImages[a].ima); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima); } @@ -6175,7 +6173,7 @@ static bool texture_paint_image_from_view_poll(bContext *C) CTX_wm_operator_poll_msg_set(C, "No 3D viewport found to create image from"); return false; } - if (G.background || !GPU_is_initialized()) { + if (G.background || !GPU_is_init()) { return false; } return true; diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index d532ef977fe..65f78a2d988 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -340,6 +340,7 @@ typedef enum { void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot); void PAINT_OT_mask_lasso_gesture(struct wmOperatorType *ot); +void PAINT_OT_mask_box_gesture(struct wmOperatorType *ot); /* paint_curve.c */ void PAINTCURVE_OT_new(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 6e0402fc6e0..c255cbcaaab 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -25,6 +25,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_vec_types.h" #include "BLI_bitmap_draw_2d.h" #include "BLI_lasso_2d.h" @@ -290,27 +291,32 @@ static void mask_box_select_task_cb(void *__restrict userdata, } } -bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *rect, bool select) +static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) { + ViewContext vc; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Sculpt *sd = vc->scene->toolsettings->sculpt; + ED_view3d_viewcontext_init(C, &vc, depsgraph); + + Sculpt *sd = vc.scene->toolsettings->sculpt; BoundBox bb; float clip_planes[4][4]; float clip_planes_final[4][4]; - ARegion *region = vc->region; - Object *ob = vc->obact; - PaintMaskFloodMode mode; + ARegion *region = vc.region; + Object *ob = vc.obact; bool multires; PBVH *pbvh; PBVHNode **nodes; int totnode; int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - mode = PAINT_MASK_FLOOD_VALUE; - float value = select ? 1.0f : 0.0f; + const PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); + const float value = RNA_float_get(op->ptr, "value"); + + rcti rect; + WM_operator_properties_border_to_rcti(op, &rect); /* Transform the clip planes in object space. */ - ED_view3d_clipping_calc(&bb, clip_planes, vc->region, vc->obact, rect); + ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &rect); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); pbvh = ob->sculpt->pbvh; @@ -589,3 +595,33 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) 0.0f, 1.0f); } + +void PAINT_OT_mask_box_gesture(wmOperatorType *ot) +{ + ot->name = "Mask Box Gesture"; + ot->idname = "PAINT_OT_mask_box_gesture"; + ot->description = "Add mask within the box as you move the brush"; + + ot->invoke = WM_gesture_box_invoke; + ot->modal = WM_gesture_box_modal; + ot->exec = paint_mask_gesture_box_exec; + + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* Properties. */ + WM_operator_properties_border(ot); + + RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); + RNA_def_float( + ot->srna, + "value", + 1.0f, + 0.0f, + 1.0f, + "Value", + "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", + 0.0f, + 1.0f); +} diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index d65f158174f..0fe6e259d8d 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -1356,6 +1356,7 @@ void ED_operatortypes_paint(void) /* paint masking */ WM_operatortype_append(PAINT_OT_mask_flood_fill); WM_operatortype_append(PAINT_OT_mask_lasso_gesture); + WM_operatortype_append(PAINT_OT_mask_box_gesture); } void ED_keymap_paint(wmKeyConfig *keyconf) diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index caecc7b708a..b9361726826 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -920,9 +920,9 @@ PaintStroke *paint_stroke_new(bContext *C, ups->average_stroke_counter = 0; /* initialize here to avoid initialization conflict with threaded strokes */ - BKE_curvemapping_initialize(br->curve); + BKE_curvemapping_init(br->curve); if (p->flags & PAINT_USE_CAVITY_MASK) { - BKE_curvemapping_initialize(p->cavity_curve); + BKE_curvemapping_init(p->cavity_curve); } BKE_paint_set_overlay_override(br->overlay_flags); 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 6965946d2ce..83d76c166ce 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -811,7 +811,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) VPaint *wp = ts->wpaint; struct Brush *brush = BKE_paint_brush(&wp->paint); - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); data.brush = brush; data.weightpaint = BKE_brush_weight_get(scene, brush); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index e838d87aeff..89fc0ac1a58 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -196,7 +196,7 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) } } -static const float *sculpt_vertex_persistent_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index) { if (ss->persistent_base) { return ss->persistent_base[index].co; @@ -204,7 +204,7 @@ static const float *sculpt_vertex_persistent_co_get(SculptSession *ss, int index return SCULPT_vertex_co_get(ss, index); } -static void sculpt_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]) { if (ss->persistent_base) { copy_v3_v3(no, ss->persistent_base[index].no); @@ -2966,7 +2966,7 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise * initialize before threads so they can do curve mapping. */ - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ SculptThreadedTaskData data = { @@ -3043,7 +3043,7 @@ static void do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise * initialize before threads so they can do curve mapping. */ - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ SculptThreadedTaskData data = { @@ -3273,7 +3273,7 @@ static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t return; } - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); SculptThreadedTaskData data = { .sd = sd, @@ -4341,9 +4341,9 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, float normal[3]; if (use_persistent_base) { - sculpt_vertex_persistent_normal_get(ss, vi, normal); + SCULPT_vertex_persistent_normal_get(ss, vi, normal); mul_v3_fl(normal, brush->height); - madd_v3_v3v3fl(final_co, sculpt_vertex_persistent_co_get(ss, vi), normal, *disp_factor); + madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor); } else { normal_short_to_float_v3(normal, orig_data.no); @@ -5513,15 +5513,21 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); } else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)), - .original = false, - .ignore_fully_ineffective = false, - .center = ss->cache->initial_location, - }; - BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); + if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) { + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)), + .original = false, + .ignore_fully_ineffective = false, + .center = ss->cache->initial_location, + }; + BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); + } + else { + /* Gobal simulation, get all nodes. */ + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + } } else { const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : @@ -6390,7 +6396,7 @@ static void sculpt_update_cache_invariants( brush = br; cache->saved_smooth_size = BKE_brush_size_get(scene, brush); BKE_brush_size_set(scene, brush, size); - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); } } } @@ -6458,7 +6464,7 @@ static void sculpt_update_cache_invariants( #define PIXEL_INPUT_THRESHHOLD 5 if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { - cache->dial = BLI_dial_initialize(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); + cache->dial = BLI_dial_init(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); } #undef PIXEL_INPUT_THRESHHOLD @@ -8160,6 +8166,14 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float return; } + if (!ss->deform_modifiers_active) { + return; + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return; + } + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); if (!ss->pmap) { diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 4232be91034..c0862795594 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -43,7 +43,9 @@ #include "DNA_scene_types.h" #include "BKE_brush.h" +#include "BKE_bvhutils.h" #include "BKE_ccg.h" +#include "BKE_collision.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_image.h" @@ -69,6 +71,7 @@ #include "BKE_subsurf.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "WM_api.h" #include "WM_message.h" @@ -85,7 +88,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" @@ -101,6 +103,33 @@ #include <stdlib.h> #include <string.h> +static float cloth_brush_simulation_falloff_get(const Brush *brush, + const float radius, + const float location[3], + const float co[3]) +{ + /* Global simulation does not have any falloff as the entire mesh is being simulated. */ + if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_GLOBAL) { + return 1.0f; + } + + const float distance = len_v3v3(location, co); + const float limit = radius + (radius * brush->cloth_sim_limit); + const float falloff = radius + (radius * brush->cloth_sim_limit * brush->cloth_sim_falloff); + + if (distance > limit) { + /* Outiside the limits. */ + return 0.0f; + } + if (distance < falloff) { + /* Before the falloff area. */ + return 1.0f; + } + /* Do a smoothstep transition inside the falloff area. */ + float p = 1.0f - ((distance - falloff) / (limit - falloff)); + return 3.0f * p * p - 2.0f * p * p * p; +} + #define CLOTH_LENGTH_CONSTRAINTS_BLOCK 100000 #define CLOTH_SIMULATION_ITERATIONS 5 #define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024 @@ -113,19 +142,8 @@ static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_s return BLI_edgeset_haskey(cloth_sim->created_length_constraints, v1, v2); } -static void cloth_brush_add_length_constraint(SculptSession *ss, - SculptClothSimulation *cloth_sim, - const int v1, - const int v2) +static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim) { - cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v1 = v1; - cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v2 = v2; - cloth_sim->length_constraints[cloth_sim->tot_length_constraints].length = len_v3v3( - SCULPT_vertex_co_get(ss, v1), SCULPT_vertex_co_get(ss, v2)); - - cloth_sim->tot_length_constraints++; - - /* Reallocation if the array capacity is exceeded. */ if (cloth_sim->tot_length_constraints >= cloth_sim->capacity_length_constraints) { cloth_sim->capacity_length_constraints += CLOTH_LENGTH_CONSTRAINTS_BLOCK; cloth_sim->length_constraints = MEM_reallocN_id(cloth_sim->length_constraints, @@ -133,23 +151,119 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, sizeof(SculptClothLengthConstraint), "length constraints"); } +} + +static void cloth_brush_add_length_constraint(SculptSession *ss, + SculptClothSimulation *cloth_sim, + const int v1, + const int v2, + const bool use_persistent) +{ + SculptClothLengthConstraint *length_constraint = + &cloth_sim->length_constraints[cloth_sim->tot_length_constraints]; + + length_constraint->elem_index_a = v1; + length_constraint->elem_index_b = v2; + + length_constraint->elem_position_a = cloth_sim->pos[v1]; + length_constraint->elem_position_b = cloth_sim->pos[v2]; + + if (use_persistent) { + length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), + SCULPT_vertex_persistent_co_get(ss, v2)); + } + else { + length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1), + SCULPT_vertex_co_get(ss, v2)); + } + length_constraint->strength = 1.0f; + + cloth_sim->tot_length_constraints++; + + /* Reallocation if the array capacity is exceeded. */ + cloth_brush_reallocate_constraints(cloth_sim); /* Add the constraint to the GSet to avoid creating it again. */ BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2); } +static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim, + const int v, + const float strength) +{ + SculptClothLengthConstraint *length_constraint = + &cloth_sim->length_constraints[cloth_sim->tot_length_constraints]; + + length_constraint->elem_index_a = v; + length_constraint->elem_index_b = v; + + length_constraint->elem_position_a = cloth_sim->pos[v]; + length_constraint->elem_position_b = cloth_sim->init_pos[v]; + + length_constraint->length = 0.0f; + length_constraint->strength = strength; + + cloth_sim->tot_length_constraints++; + + /* Reallocation if the array capacity is exceeded. */ + cloth_brush_reallocate_constraints(cloth_sim); +} + +static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_sim, + const int v, + const float strength) +{ + SculptClothLengthConstraint *length_constraint = + &cloth_sim->length_constraints[cloth_sim->tot_length_constraints]; + + length_constraint->elem_index_a = v; + length_constraint->elem_index_b = v; + + length_constraint->elem_position_a = cloth_sim->pos[v]; + length_constraint->elem_position_b = cloth_sim->deformation_pos[v]; + + length_constraint->length = 0.0f; + length_constraint->strength = strength; + + cloth_sim->tot_length_constraints++; + + /* Reallocation if the array capacity is exceeded. */ + cloth_brush_reallocate_constraints(cloth_sim); +} + static void do_cloth_brush_build_constraints_task_cb_ex( void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; PBVHVertexIter vd; + const bool pin_simulation_boundary = ss->cache != NULL && brush != NULL && + brush->flag2 & BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY; + + const bool use_persistent = brush != NULL && brush->flag & BRUSH_PERSISTENT; + + /* Brush can be NULL in tools that use the solver without relying of constraints with deformation + * positions. */ + const bool cloth_is_deform_brush = ss->cache != NULL && brush != NULL && + SCULPT_is_cloth_deform_brush(brush); + float radius_squared = 0.0f; + if (cloth_is_deform_brush) { + radius_squared = ss->cache->initial_radius * ss->cache->initial_radius; + } + + /* Only limit the contraint creation to a radius when the simulation is local. */ + const float cloth_sim_radius_squared = brush->cloth_simulation_area_type == + BRUSH_CLOTH_SIMULATION_AREA_LOCAL ? + data->cloth_sim_radius * data->cloth_sim_radius : + FLT_MAX; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (len_squared_v3v3(vd.co, data->cloth_sim_initial_location) < - data->cloth_sim_radius * data->cloth_sim_radius) { + const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location); + if (len_squared < cloth_sim_radius_squared) { SculptVertexNeighborIter ni; int build_indices[CLOTH_MAX_CONSTRAINTS_PER_VERTEX]; @@ -162,6 +276,11 @@ static void do_cloth_brush_build_constraints_task_cb_ex( } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + if (brush->cloth_constraint_softbody_strength > 0.0f) { + cloth_brush_add_softbody_constraint( + data->cloth_sim, vd.index, brush->cloth_constraint_softbody_strength); + } + /* As we don't know the order of the neighbor vertices, we create all possible combinations * between the neighbor and the original vertex as length constraints. */ /* This results on a pattern that contains structural, shear and bending constraints for all @@ -172,35 +291,29 @@ static void do_cloth_brush_build_constraints_task_cb_ex( if (c_i != c_j && !cloth_brush_sim_has_length_constraint( data->cloth_sim, build_indices[c_i], build_indices[c_j])) { cloth_brush_add_length_constraint( - ss, data->cloth_sim, build_indices[c_i], build_indices[c_j]); + ss, data->cloth_sim, build_indices[c_i], build_indices[c_j], use_persistent); } } } } - } - BKE_pbvh_vertex_iter_end; -} -static float cloth_brush_simulation_falloff_get(const Brush *brush, - const float radius, - const float location[3], - const float co[3]) -{ - const float distance = len_v3v3(location, co); - const float limit = radius + (radius * brush->cloth_sim_limit); - const float falloff = radius + (radius * brush->cloth_sim_limit * brush->cloth_sim_falloff); + if (cloth_is_deform_brush && len_squared < radius_squared) { + const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius); + cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade); + } - if (distance > limit) { - /* Outiside the limits. */ - return 0.0f; - } - if (distance < falloff) { - /* Before the falloff area. */ - return 1.0f; + if (pin_simulation_boundary) { + const float sim_falloff = cloth_brush_simulation_falloff_get( + brush, ss->cache->initial_radius, ss->cache->location, vd.co); + /* Vertex is inside the area of the simulation without any falloff aplied. */ + if (sim_falloff < 1.0f) { + /* Create constraints with more strength the closer the vertex is to the simulation + * boundary. */ + cloth_brush_add_softbody_constraint(data->cloth_sim, vd.index, 1.0f - sim_falloff); + } + } } - /* Do a smoothstep transition inside the falloff area. */ - float p = 1.0f - ((distance - falloff) / (limit - falloff)); - return 3.0f * p * p - 2.0f * p * p * p; + BKE_pbvh_vertex_iter_end; } static void cloth_brush_apply_force_to_vertex(SculptSession *UNUSED(ss), @@ -323,9 +436,10 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(force, offset, -fade); break; case BRUSH_CLOTH_DEFORM_GRAB: - /* Grab writes the positions in the simulation directly without applying forces. */ - madd_v3_v3v3fl( - cloth_sim->pos[vd.index], orig_data.co, ss->cache->grab_delta_symmetry, fade); + madd_v3_v3v3fl(cloth_sim->deformation_pos[vd.index], + orig_data.co, + ss->cache->grab_delta_symmetry, + fade); zero_v3(force); break; case BRUSH_CLOTH_DEFORM_PINCH_POINT: @@ -366,9 +480,36 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } +static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph) +{ + ListBase *cache = NULL; + DEG_OBJECT_ITER_BEGIN (depsgraph, + ob, + DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE | + DEG_ITER_OBJECT_FLAG_DUPLI) { + CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( + ob, eModifierType_Collision); + if (cmd && cmd->bvhtree) { + if (cache == NULL) { + cache = MEM_callocN(sizeof(ListBase), "ColliderCache array"); + } + + ColliderCache *col = MEM_callocN(sizeof(ColliderCache), "ColliderCache"); + col->ob = ob; + col->collmd = cmd; + collision_move_object(cmd, 1.0, 0.0, true); + BLI_addtail(cache, col); + } + } + DEG_OBJECT_ITER_END; + return cache; +} + static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, + Brush *brush, const float cloth_mass, - const float cloth_damping) + const float cloth_damping, + const bool use_collisions) { const int totverts = SCULPT_vertex_count_get(ss); SculptClothSimulation *cloth_sim; @@ -380,19 +521,130 @@ static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, "cloth length constraints"); cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK; - cloth_sim->acceleration = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim acceleration"); - cloth_sim->pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim pos"); - cloth_sim->prev_pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim prev pos"); - cloth_sim->init_pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim init pos"); - cloth_sim->length_constraint_tweak = MEM_callocN(sizeof(float) * totverts, - "cloth sim length tweak"); + cloth_sim->acceleration = MEM_calloc_arrayN( + totverts, 3 * sizeof(float), "cloth sim acceleration"); + cloth_sim->pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim pos"); + cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim prev pos"); + cloth_sim->last_iteration_pos = MEM_calloc_arrayN( + totverts, sizeof(float) * 3, "cloth sim last iteration pos"); + cloth_sim->init_pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim init pos"); + cloth_sim->length_constraint_tweak = MEM_calloc_arrayN( + totverts, sizeof(float), "cloth sim length tweak"); + + /* Brush can be NULL for tools that need the solver but don't rely on constraint to deformation + * positions. */ + if (brush && SCULPT_is_cloth_deform_brush(brush)) { + cloth_sim->deformation_pos = MEM_calloc_arrayN( + totverts, 3 * sizeof(float), "cloth sim deformation positions"); + } cloth_sim->mass = cloth_mass; cloth_sim->damping = cloth_damping; + if (use_collisions) { + cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph); + } + return cloth_sim; } +typedef struct ClothBrushCollision { + CollisionModifierData *col_data; + struct IsectRayPrecalc isect_precalc; +} ClothBrushCollision; + +static void cloth_brush_collision_cb(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + ClothBrushCollision *col = (ClothBrushCollision *)userdata; + CollisionModifierData *col_data = col->col_data; + MVertTri *verttri = &col_data->tri[index]; + MVert *mverts = col_data->x; + float *tri[3], no[3], co[3]; + + tri[0] = mverts[verttri->tri[0]].co; + tri[1] = mverts[verttri->tri[1]].co; + tri[2] = mverts[verttri->tri[2]].co; + float dist = 0.0f; + + bool tri_hit = isect_ray_tri_watertight_v3( + ray->origin, &col->isect_precalc, UNPACK3(tri), &dist, NULL); + normal_tri_v3(no, UNPACK3(tri)); + madd_v3_v3v3fl(co, ray->origin, ray->direction, dist); + + if (tri_hit && dist < hit->dist) { + hit->index = index; + hit->dist = dist; + + copy_v3_v3(hit->co, co); + copy_v3_v3(hit->no, no); + } +} + +static void cloth_brush_solve_collision(Object *object, + SculptClothSimulation *cloth_sim, + const int i) +{ + const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT); + + ColliderCache *collider_cache; + BVHTreeRayHit hit; + + float obmat_inv[4][4]; + invert_m4_m4(obmat_inv, object->obmat); + + for (collider_cache = cloth_sim->collider_list->first; collider_cache; + collider_cache = collider_cache->next) { + float ray_start[3], ray_normal[3]; + float pos_world_space[3], prev_pos_world_space[3]; + + mul_v3_m4v3(pos_world_space, object->obmat, cloth_sim->pos[i]); + mul_v3_m4v3(prev_pos_world_space, object->obmat, cloth_sim->last_iteration_pos[i]); + sub_v3_v3v3(ray_normal, pos_world_space, prev_pos_world_space); + copy_v3_v3(ray_start, prev_pos_world_space); + hit.index = -1; + hit.dist = len_v3(ray_normal); + normalize_v3(ray_normal); + + ClothBrushCollision col; + CollisionModifierData *collmd = collider_cache->collmd; + col.col_data = collmd; + isect_ray_tri_watertight_v3_precalc(&col.isect_precalc, ray_normal); + + BLI_bvhtree_ray_cast_ex(collmd->bvhtree, + ray_start, + ray_normal, + 0.3f, + &hit, + cloth_brush_collision_cb, + &col, + raycast_flag); + + if (hit.index != -1) { + + float collision_disp[3]; + float movement_disp[3]; + mul_v3_v3fl(collision_disp, hit.no, 0.005f); + sub_v3_v3v3(movement_disp, pos_world_space, prev_pos_world_space); + float friction_plane[4]; + float pos_on_friction_plane[3]; + plane_from_point_normal_v3(friction_plane, hit.co, hit.no); + closest_to_plane_v3(pos_on_friction_plane, friction_plane, pos_world_space); + sub_v3_v3v3(movement_disp, pos_on_friction_plane, hit.co); + + /* TODO(pablodp606): This can be exposed in a brush/filter property as friction. */ + mul_v3_fl(movement_disp, 0.35f); + + copy_v3_v3(cloth_sim->pos[i], hit.co); + add_v3_v3(cloth_sim->pos[i], movement_disp); + add_v3_v3(cloth_sim->pos[i], collision_disp); + mul_v3_m4v3(cloth_sim->pos[i], obmat_inv, cloth_sim->pos[i]); + } + } +} + static void do_cloth_brush_solve_simulation_task_cb_ex( void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { @@ -423,14 +675,22 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) * SCULPT_automasking_factor_get(ss, vd.index); + madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v); madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v); - copy_v3_v3(cloth_sim->prev_pos[i], temp); + if (cloth_sim->collider_list != NULL) { + cloth_brush_solve_collision(data->ob, cloth_sim, i); + } + + copy_v3_v3(cloth_sim->last_iteration_pos[i], cloth_sim->pos[i]); + copy_v3_v3(cloth_sim->prev_pos[i], temp); + copy_v3_v3(cloth_sim->last_iteration_pos[i], cloth_sim->pos[i]); copy_v3_fl(cloth_sim->acceleration[i], 0.0f); copy_v3_v3(vd.co, cloth_sim->pos[vd.index]); + if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } @@ -484,11 +744,11 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, for (int i = 0; i < cloth_sim->tot_length_constraints; i++) { const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i]; - const int v1 = constraint->v1; - const int v2 = constraint->v2; + const int v1 = constraint->elem_index_a; + const int v2 = constraint->elem_index_b; float v1_to_v2[3]; - sub_v3_v3v3(v1_to_v2, cloth_sim->pos[v2], cloth_sim->pos[v1]); + sub_v3_v3v3(v1_to_v2, constraint->elem_position_b, constraint->elem_position_a); const float current_distance = len_v3(v1_to_v2); float correction_vector[3]; float correction_vector_half[3]; @@ -524,8 +784,14 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, cloth_sim->init_pos[v2]) : 1.0f; - madd_v3_v3fl(cloth_sim->pos[v1], correction_vector_half, 1.0f * mask_v1 * sim_factor_v1); - madd_v3_v3fl(cloth_sim->pos[v2], correction_vector_half, -1.0f * mask_v2 * sim_factor_v2); + madd_v3_v3fl(cloth_sim->pos[v1], + correction_vector_half, + 1.0f * mask_v1 * sim_factor_v1 * constraint->strength); + if (v1 != v2) { + madd_v3_v3fl(cloth_sim->pos[v2], + correction_vector_half, + -1.0f * mask_v2 * sim_factor_v2 * constraint->strength); + } } } } @@ -577,7 +843,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod .mat = imat, }; - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Init the grab delta. */ copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); @@ -648,11 +914,20 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode /* The simulation structure only needs to be created on the first symmetry pass. */ if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) { + const bool is_cloth_deform_brush = SCULPT_is_cloth_deform_brush(brush); ss->cache->cloth_sim = cloth_brush_simulation_create( - ss, brush->cloth_mass, brush->cloth_damping); + ss, + brush, + brush->cloth_mass, + brush->cloth_damping, + (brush->flag2 & BRUSH_CLOTH_USE_COLLISION)); for (int i = 0; i < totverts; i++) { - copy_v3_v3(ss->cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(ss->cache->cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i)); copy_v3_v3(ss->cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(ss->cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); + if (is_cloth_deform_brush) { + copy_v3_v3(ss->cache->cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); + } } } @@ -680,18 +955,22 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) { MEM_SAFE_FREE(cloth_sim->pos); + MEM_SAFE_FREE(cloth_sim->last_iteration_pos); MEM_SAFE_FREE(cloth_sim->prev_pos); MEM_SAFE_FREE(cloth_sim->acceleration); MEM_SAFE_FREE(cloth_sim->length_constraints); MEM_SAFE_FREE(cloth_sim->length_constraint_tweak); + MEM_SAFE_FREE(cloth_sim->deformation_pos); MEM_SAFE_FREE(cloth_sim->init_pos); + if (cloth_sim->collider_list) { + BKE_collider_cache_free(&cloth_sim->collider_list); + } MEM_SAFE_FREE(cloth_sim); } /* Cursor drawing function. */ void SCULPT_cloth_simulation_limits_draw(const uint gpuattr, const Brush *brush, - const float obmat[4][4], const float location[3], const float normal[3], const float rds, @@ -702,10 +981,11 @@ void SCULPT_cloth_simulation_limits_draw(const uint gpuattr, float cursor_trans[4][4], cursor_rot[4][4]; float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; float quat[4]; - copy_m4_m4(cursor_trans, obmat); + unit_m4(cursor_trans); translate_m4(cursor_trans, location[0], location[1], location[2]); rotation_between_vecs_to_quat(quat, z_axis, normal); quat_to_mat4(cursor_rot, quat); + GPU_matrix_push(); GPU_matrix_mul(cursor_trans); GPU_matrix_mul(cursor_rot); @@ -715,6 +995,7 @@ void SCULPT_cloth_simulation_limits_draw(const uint gpuattr, gpuattr, 0, 0, rds + (rds * brush->cloth_sim_limit * brush->cloth_sim_falloff), 320); immUniformColor3fvAlpha(outline_col, alpha * 0.7f); imm_draw_circle_wire_3d(gpuattr, 0, 0, rds + rds * brush->cloth_sim_limit, 80); + GPU_matrix_pop(); } void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr, @@ -861,6 +1142,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); const int totverts = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totverts; i++) { copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); } @@ -892,7 +1174,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; SculptSession *ss = ob->sculpt; @@ -913,11 +1195,15 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass"); const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping"); - ss->filter_cache->cloth_sim = cloth_brush_simulation_create(ss, cloth_mass, cloth_damping); + const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions"); + ss->filter_cache->cloth_sim = cloth_brush_simulation_create( + ss, NULL, cloth_mass, cloth_damping, use_collisions); + copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss)); const int totverts = SCULPT_vertex_count_get(ss); for (int i = 0; i < totverts; i++) { + copy_v3_v3(ss->filter_cache->cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i)); copy_v3_v3(ss->filter_cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); copy_v3_v3(ss->filter_cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); } @@ -989,4 +1275,9 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot) false, "Use Face Sets", "Apply the filter only to the Face Set under the cursor"); + ot->prop = RNA_def_boolean(ot->srna, + "use_collisions", + false, + "Use Collisions", + "Collide with other collider objects in the scene"); } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 1940b007cb0..5cc32be331e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -192,7 +192,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ SculptThreadedTaskData data = { diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index e9a98a17f8a..ba11988156c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -132,6 +132,7 @@ void SCULPT_filter_cache_free(SculptSession *ss) MEM_SAFE_FREE(ss->filter_cache->automask); MEM_SAFE_FREE(ss->filter_cache->surface_smooth_laplacian_disp); MEM_SAFE_FREE(ss->filter_cache->sharpen_factor); + MEM_SAFE_FREE(ss->filter_cache->sharpen_detail_directions); MEM_SAFE_FREE(ss->filter_cache); } @@ -356,6 +357,17 @@ static void mesh_filter_task_cb(void *__restrict userdata, mul_v3_v3fl( disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index])); add_v3_v3v3(disp, disp_avg, disp_sharpen); + + /* Intensify details. */ + if (ss->filter_cache->sharpen_intensify_detail_strength > 0.0f) { + float detail_strength[3]; + normal_short_to_float_v3(detail_strength, orig_data.no); + copy_v3_v3(detail_strength, ss->filter_cache->sharpen_detail_directions[vd.index]); + madd_v3_v3fl(disp, + detail_strength, + -ss->filter_cache->sharpen_intensify_detail_strength * + ss->filter_cache->sharpen_factor[vd.index]); + } break; } } @@ -388,8 +400,10 @@ static void mesh_filter_sharpen_init_factors(SculptSession *ss) for (int i = 0; i < totvert; i++) { float avg[3]; SCULPT_neighbor_coords_average(ss, avg, i); - ss->filter_cache->sharpen_factor[i] = len_v3v3(avg, SCULPT_vertex_co_get(ss, i)); + sub_v3_v3v3(ss->filter_cache->sharpen_detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + ss->filter_cache->sharpen_factor[i] = len_v3(ss->filter_cache->sharpen_detail_directions[i]); } + float max_factor = 0.0f; for (int i = 0; i < totvert; i++) { if (ss->filter_cache->sharpen_factor[i] > max_factor) { @@ -402,6 +416,31 @@ static void mesh_filter_sharpen_init_factors(SculptSession *ss) ss->filter_cache->sharpen_factor[i] *= max_factor; ss->filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - ss->filter_cache->sharpen_factor[i]); } + + /* Smooth the calculated factors and directions to remove high frecuency detail. */ + for (int smooth_iterations = 0; + smooth_iterations < ss->filter_cache->sharpen_curvature_smooth_iterations; + smooth_iterations++) { + for (int i = 0; i < totvert; i++) { + float direction_avg[3] = {0.0f, 0.0f, 0.0f}; + float sharpen_avg = 0; + int total = 0; + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + add_v3_v3(direction_avg, ss->filter_cache->sharpen_detail_directions[ni.index]); + sharpen_avg += ss->filter_cache->sharpen_factor[ni.index]; + total++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (total > 0) { + mul_v3_v3fl( + ss->filter_cache->sharpen_detail_directions[i], direction_avg, 1.0f / (float)total); + ss->filter_cache->sharpen_factor[i] = sharpen_avg / (float)total; + } + } + } } static void mesh_filter_surface_smooth_displace_task_cb( @@ -566,7 +605,14 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SHARPEN) { ss->filter_cache->sharpen_smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio"); + ss->filter_cache->sharpen_intensify_detail_strength = RNA_float_get( + op->ptr, "sharpen_intensify_detail_strength"); + ss->filter_cache->sharpen_curvature_smooth_iterations = RNA_int_get( + op->ptr, "sharpen_curvature_smooth_iterations"); + ss->filter_cache->sharpen_factor = MEM_mallocN(sizeof(float) * totvert, "sharpen factor"); + ss->filter_cache->sharpen_detail_directions = MEM_malloc_arrayN( + totvert, 3 * sizeof(float), "sharpen detail direction"); mesh_filter_sharpen_init_factors(ss); } @@ -642,4 +688,24 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) "How much smoothing is applied to polished surfaces", 0.0f, 1.0f); + + RNA_def_float(ot->srna, + "sharpen_intensify_detail_strength", + 0.0f, + 0.0f, + 10.0f, + "Intensify Details", + "How much creases and valleys are intensified", + 0.0f, + 1.0f); + + RNA_def_int(ot->srna, + "sharpen_curvature_smooth_iterations", + 0, + 0, + 10, + "Curvature Smooth Iterations", + "How much smooth the resulting shape is, ignoring high frequency details", + 0, + 10); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 6a989ffea7e..74feaa8ca00 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -97,6 +97,9 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]); float SCULPT_vertex_mask_get(struct SculptSession *ss, int index); const float *SCULPT_vertex_color_get(SculptSession *ss, int index); +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index); +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); + #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 typedef struct SculptVertexNeighborIter { /* Storage */ @@ -347,7 +350,6 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim); void SCULPT_cloth_simulation_limits_draw(const uint gpuattr, const struct Brush *brush, - const float obmat[4][4], const float location[3], const float normal[3], const float rds, @@ -393,6 +395,7 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain); /* Multiplane Scrape Brush. */ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr, + Brush *brush, SculptSession *ss, const float outline_col[3], const float outline_alpha); @@ -905,7 +908,10 @@ typedef struct FilterCache { /* Sharpen mesh filter. */ float sharpen_smooth_ratio; + float sharpen_intensify_detail_strength; + int sharpen_curvature_smooth_iterations; float *sharpen_factor; + float (*sharpen_detail_directions)[3]; /* unmasked nodes */ PBVHNode **nodes; diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index b52036d753c..35f916c8f90 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -47,7 +47,6 @@ #include "paint_intern.h" #include "sculpt_intern.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" @@ -400,10 +399,15 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, } void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr, + Brush *brush, SculptSession *ss, const float outline_col[3], const float outline_alpha) { + if (!(brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW)) { + return; + } + float local_mat_inv[4][4]; invert_m4_m4(local_mat_inv, ss->cache->stroke_local_mat); GPU_matrix_mul(local_mat_inv); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index f01a914fdd3..00a59949130 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -255,7 +255,7 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode return; } - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); float area_no[3]; float mat[4][4]; @@ -468,7 +468,7 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode } } - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); SculptThreadedTaskData data = { .sd = sd, diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index dc556fa1945..8a288877d43 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -1006,7 +1006,7 @@ void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br static void sculpt_pose_do_translate_deform(SculptSession *ss, Brush *brush) { SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); pose_solve_translate_chain(ik_chain, ss->cache->grab_delta); } @@ -1031,8 +1031,10 @@ static void sculpt_pose_do_scale_deform(SculptSession *ss, Brush *brush) copy_v3_v3(ik_target, ss->cache->true_location); add_v3_v3(ik_target, ss->cache->grab_delta); - /* Solve the IK for the first segment to include rotation as part of scale. */ - pose_solve_ik_chain(ik_chain, ik_target, brush->flag2 & BRUSH_POSE_IK_ANCHORED); + /* Solve the IK for the first segment to include rotation as part of scale if enabled. */ + if (!(brush->flag2 & BRUSH_POSE_USE_LOCK_ROTATION)) { + pose_solve_ik_chain(ik_chain, ik_target, brush->flag2 & BRUSH_POSE_IK_ANCHORED); + } float scale[3]; copy_v3_fl(scale, sculpt_pose_get_scale_from_grab_delta(ss, ik_target)); @@ -1047,7 +1049,7 @@ static void sculpt_pose_do_twist_deform(SculptSession *ss, Brush *brush) /* Calculate the maximum roll. 0.02 radians per pixel works fine. */ float roll = (ss->cache->initial_mouse[0] - ss->cache->mouse[0]) * ss->cache->bstrength * 0.02f; - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); pose_solve_roll_chain(ik_chain, brush, roll); } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 4b3df2dfea2..be509f4aed6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -488,7 +488,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm op->customdata = data; - BKE_curvemapping_initialize(ts->uvsculpt->paint.brush->curve); + BKE_curvemapping_init(ts->uvsculpt->paint.brush->curve); if (data) { int counter = 0, i; diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 079cee290ae..db55eff8284 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -60,7 +60,7 @@ /* ******************** default callbacks for action space ***************** */ -static SpaceLink *action_new(const ScrArea *area, const Scene *scene) +static SpaceLink *action_create(const ScrArea *area, const Scene *scene) { SpaceAction *saction; ARegion *region; @@ -863,7 +863,7 @@ void ED_spacetype_action(void) st->spaceid = SPACE_ACTION; strncpy(st->name, "Action", BKE_ST_MAXNAME); - st->new = action_new; + st->create = action_create; st->free = action_free; st->init = action_init; st->duplicate = action_duplicate; diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 3ae203b563b..1656a76e2d4 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -280,7 +280,7 @@ void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) void ED_spacetype_xxx(void); /* allocate and init some vars */ -static SpaceLink *xxx_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *xxx_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { return NULL; } @@ -324,7 +324,7 @@ void ED_spacetype_xxx(void) st.spaceid = SPACE_VIEW3D; - st.new = xxx_new; + st.create = xxx_create; st.free = xxx_free; st.init = xxx_init; st.duplicate = xxx_duplicate; diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 16256f6c97e..5885d3dcbb0 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -101,7 +101,7 @@ static PointerRNA *get_pointer_type(ButsContextPath *path, StructRNA *type) /************************* Creating the Path ************************/ -static int buttons_context_path_scene(ButsContextPath *path) +static bool buttons_context_path_scene(ButsContextPath *path) { PointerRNA *ptr = &path->ptr[path->len - 1]; @@ -162,14 +162,14 @@ static int buttons_context_path_world(ButsContextPath *path) return 0; } -static int buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window) +static bool buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window) { FreestyleLineStyle *linestyle; PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a (pinned) linestyle, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_FreestyleLineStyle)) { - return 1; + return true; } /* if we have a view layer, use the lineset's linestyle */ if (buttons_context_path_view_layer(path, window)) { @@ -178,24 +178,24 @@ static int buttons_context_path_linestyle(ButsContextPath *path, wmWindow *windo if (linestyle) { RNA_id_pointer_create(&linestyle->id, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to a linestyle possible */ - return 0; + return false; } -static int buttons_context_path_object(ButsContextPath *path) +static bool buttons_context_path_object(ButsContextPath *path) { PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a (pinned) object, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Object)) { - return 1; + return true; } if (!RNA_struct_is_a(ptr->type, &RNA_ViewLayer)) { - return 0; + return false; } ViewLayer *view_layer = ptr->data; @@ -205,58 +205,58 @@ static int buttons_context_path_object(ButsContextPath *path) RNA_id_pointer_create(&ob->id, &path->ptr[path->len]); path->len++; - return 1; + return true; } /* no path to a object possible */ - return 0; + return false; } -static int buttons_context_path_data(ButsContextPath *path, int type) +static bool buttons_context_path_data(ButsContextPath *path, int type) { Object *ob; PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a data, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Mesh) && (type == -1 || type == OB_MESH)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Curve) && (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Camera) && (type == -1 || type == OB_CAMERA)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Light) && (type == -1 || type == OB_LAMP)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) { - return 1; + return true; } /* try to get an object in the path, no pinning supported here */ if (buttons_context_path_object(path)) { @@ -266,15 +266,15 @@ static int buttons_context_path_data(ButsContextPath *path, int type) RNA_id_pointer_create(ob->data, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to data possible */ - return 0; + return false; } -static int buttons_context_path_modifier(ButsContextPath *path) +static bool buttons_context_path_modifier(ButsContextPath *path) { Object *ob; @@ -291,14 +291,14 @@ static int buttons_context_path_modifier(ButsContextPath *path) OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) { - return 1; + return true; } } - return 0; + return false; } -static int buttons_context_path_shaderfx(ButsContextPath *path) +static bool buttons_context_path_shaderfx(ButsContextPath *path) { Object *ob; @@ -306,14 +306,14 @@ static int buttons_context_path_shaderfx(ButsContextPath *path) ob = path->ptr[path->len - 1].data; if (ob && ELEM(ob->type, OB_GPENCIL)) { - return 1; + return true; } } - return 0; + return false; } -static int buttons_context_path_material(ButsContextPath *path) +static bool buttons_context_path_material(ButsContextPath *path) { Object *ob; PointerRNA *ptr = &path->ptr[path->len - 1]; @@ -321,7 +321,7 @@ static int buttons_context_path_material(ButsContextPath *path) /* if we already have a (pinned) material, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Material)) { - return 1; + return true; } /* if we have an object, use the object material slot */ if (buttons_context_path_object(path)) { @@ -331,15 +331,15 @@ static int buttons_context_path_material(ButsContextPath *path) ma = BKE_object_material_get(ob, ob->actcol); RNA_id_pointer_create(&ma->id, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to a material possible */ - return 0; + return false; } -static int buttons_context_path_bone(ButsContextPath *path) +static bool buttons_context_path_bone(ButsContextPath *path) { bArmature *arm; EditBone *edbo; @@ -353,29 +353,29 @@ static int buttons_context_path_bone(ButsContextPath *path) edbo = arm->act_edbone; RNA_pointer_create(&arm->id, &RNA_EditBone, edbo, &path->ptr[path->len]); path->len++; - return 1; + return true; } } else { if (arm->act_bone) { RNA_pointer_create(&arm->id, &RNA_Bone, arm->act_bone, &path->ptr[path->len]); path->len++; - return 1; + return true; } } } /* no path to a bone possible */ - return 0; + return false; } -static int buttons_context_path_pose_bone(ButsContextPath *path) +static bool buttons_context_path_pose_bone(ButsContextPath *path) { PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a (pinned) PoseBone, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_PoseBone)) { - return 1; + return true; } /* if we have an armature, get the active bone */ @@ -384,7 +384,7 @@ static int buttons_context_path_pose_bone(ButsContextPath *path) bArmature *arm = ob->data; /* path->ptr[path->len-1].data - works too */ if (ob->type != OB_ARMATURE || arm->edbo) { - return 0; + return false; } if (arm->act_bone) { @@ -392,16 +392,16 @@ static int buttons_context_path_pose_bone(ButsContextPath *path) if (pchan) { RNA_pointer_create(&ob->id, &RNA_PoseBone, pchan, &path->ptr[path->len]); path->len++; - return 1; + return true; } } } /* no path to a bone possible */ - return 0; + return false; } -static int buttons_context_path_particle(ButsContextPath *path) +static bool buttons_context_path_particle(ButsContextPath *path) { Object *ob; ParticleSystem *psys; @@ -409,7 +409,7 @@ static int buttons_context_path_particle(ButsContextPath *path) /* if we already have (pinned) particle settings, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_ParticleSettings)) { - return 1; + return true; } /* if we have an object, get the active particle system */ if (buttons_context_path_object(path)) { @@ -420,15 +420,15 @@ static int buttons_context_path_particle(ButsContextPath *path) RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to a particle system possible */ - return 0; + return false; } -static int buttons_context_path_brush(const bContext *C, ButsContextPath *path) +static bool buttons_context_path_brush(const bContext *C, ButsContextPath *path) { Scene *scene; Brush *br = NULL; @@ -436,7 +436,7 @@ static int buttons_context_path_brush(const bContext *C, ButsContextPath *path) /* if we already have a (pinned) brush, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Brush)) { - return 1; + return true; } /* if we have a scene, use the toolsettings brushes */ if (buttons_context_path_scene(path)) { @@ -452,32 +452,32 @@ static int buttons_context_path_brush(const bContext *C, ButsContextPath *path) RNA_id_pointer_create((ID *)br, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to a brush possible */ - return 0; + return false; } -static int buttons_context_path_texture(const bContext *C, - ButsContextPath *path, - ButsContextTexture *ct) +static bool buttons_context_path_texture(const bContext *C, + ButsContextPath *path, + ButsContextTexture *ct) { PointerRNA *ptr = &path->ptr[path->len - 1]; ID *id; if (!ct) { - return 0; + return false; } /* if we already have a (pinned) texture, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Texture)) { - return 1; + return true; } if (!ct->user) { - return 0; + return false; } id = ct->user->id; @@ -502,7 +502,7 @@ static int buttons_context_path_texture(const bContext *C, path->len++; } - return 1; + return true; } #ifdef WITH_FREESTYLE @@ -531,7 +531,7 @@ static bool buttons_context_linestyle_pinnable(const bContext *C, ViewLayer *vie } #endif -static int buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int flag) +static bool buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int flag) { /* Note we don't use CTX_data here, instead we get it from the window. * Otherwise there is a loop reading the context that we are setting. */ @@ -626,27 +626,27 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma found = buttons_context_path_pose_bone(path); break; default: - found = 0; + found = false; break; } return found; } -static int buttons_shading_context(const bContext *C, int mainb) +static bool buttons_shading_context(const bContext *C, int mainb) { wmWindow *window = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(window); Object *ob = OBACT(view_layer); if (ELEM(mainb, BCONTEXT_MATERIAL, BCONTEXT_WORLD, BCONTEXT_TEXTURE)) { - return 1; + return true; } if (mainb == BCONTEXT_DATA && ob && ELEM(ob->type, OB_LAMP, OB_CAMERA)) { - return 1; + return true; } - return 0; + return false; } static int buttons_shading_new_context(const bContext *C, int flag) diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index 733f344fbc6..a062b178fc8 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -52,7 +52,9 @@ #include "buttons_intern.h" /* own include */ -/********************** context_menu operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Context Menu Operator + * \{ */ static int context_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) { @@ -67,17 +69,21 @@ static int context_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven void BUTTONS_OT_context_menu(wmOperatorType *ot) { - /* identifiers */ + /* Identifiers. */ ot->name = "Context Menu"; ot->description = "Display properties editor context_menu"; ot->idname = "BUTTONS_OT_context_menu"; - /* api callbacks */ + /* Callbacks. */ ot->invoke = context_menu_invoke; ot->poll = ED_operator_buttons_active; } -/********************** filebrowse operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name File Browse Operator + * \{ */ typedef struct FileBrowseOp { PointerRNA ptr; @@ -101,7 +107,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) str = RNA_string_get_alloc(op->ptr, path_prop, NULL, 0); - /* add slash for directories, important for some properties */ + /* Add slash for directories, important for some properties. */ if (RNA_property_subtype(fbo->prop) == PROP_DIRPATH) { const bool is_relative = RNA_boolean_get(op->ptr, "relative_path"); id = fbo->ptr.owner_id; @@ -110,7 +116,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) BLI_path_abs(path, id ? ID_BLEND_PATH(bmain, id) : BKE_main_blendfile_path(bmain)); if (BLI_is_dir(path)) { - /* do this first so '//' isnt converted to '//\' on windows */ + /* Do this first so '//' isnt converted to '//\' on windows. */ BLI_path_slash_ensure(path); if (is_relative) { BLI_strncpy(path, str, FILE_MAX); @@ -139,7 +145,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) ED_undo_push(C, undostr); } - /* special, annoying exception, filesel on redo panel [#26618] */ + /* Special annoying exception, filesel on redo panel [#26618]. */ { wmOperator *redo_op = WM_operator_last_redo(C); if (redo_op) { @@ -187,8 +193,8 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) str = RNA_property_string_get_alloc(&ptr, prop, NULL, 0, NULL); - /* useful yet irritating feature, Shift+Click to open the file - * Alt+Click to browse a folder in the OS's browser */ + /* Useful yet irritating feature, Shift+Click to open the file + * Alt+Click to browse a folder in the OS's browser. */ if (event->shift || event->alt) { wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true); PointerRNA props_ptr; @@ -219,13 +225,13 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) fbo->is_userdef = is_userdef; op->customdata = fbo; - /* normally ED_fileselect_get_params would handle this but we need to because of stupid - * user-prefs exception - campbell */ + /* Normally ED_fileselect_get_params would handle this but we need to because of stupid + * user-prefs exception. - campbell */ if ((prop_relpath = RNA_struct_find_property(op->ptr, "relative_path"))) { if (!RNA_property_is_set(op->ptr, prop_relpath)) { bool is_relative = (U.flag & USER_RELPATHS) != 0; - /* while we want to follow the defaults, + /* While we want to follow the defaults, * we better not switch existing paths relative/absolute state. */ if (str[0]) { is_relative = BLI_path_is_rel(str); @@ -235,7 +241,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) is_relative = false; } - /* annoying exception!, if we're dealing with the user prefs, default relative to be off */ + /* Annoying exception!, if we're dealing with the user prefs, default relative to be off. */ RNA_property_boolean_set(op->ptr, prop_relpath, is_relative); } } @@ -250,21 +256,21 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) void BUTTONS_OT_file_browse(wmOperatorType *ot) { - /* identifiers */ + /* Identifiers. */ ot->name = "Accept"; ot->description = "Open a file browser, Hold Shift to open the file, Alt to browse containing directory"; ot->idname = "BUTTONS_OT_file_browse"; - /* api callbacks */ + /* Callbacks. */ ot->invoke = file_browse_invoke; ot->exec = file_browse_exec; ot->cancel = file_browse_cancel; - /* conditional undo based on button flag */ + /* Conditional undo based on button flag. */ ot->flag = 0; - /* properties */ + /* Properties. */ WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, @@ -274,7 +280,7 @@ void BUTTONS_OT_file_browse(wmOperatorType *ot) FILE_SORT_ALPHA); } -/* second operator, only difference from BUTTONS_OT_file_browse is WM_FILESEL_DIRECTORY */ +/* Second operator, only difference from BUTTONS_OT_file_browse is WM_FILESEL_DIRECTORY. */ void BUTTONS_OT_directory_browse(wmOperatorType *ot) { /* identifiers */ @@ -300,3 +306,5 @@ void BUTTONS_OT_directory_browse(wmOperatorType *ot) FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); } + +/** \} */ diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index ac59bb245f3..dc34e56dc92 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -35,6 +35,7 @@ #include "BKE_screen.h" #include "BKE_shader_fx.h" +#include "ED_buttons.h" #include "ED_screen.h" #include "ED_space_api.h" #include "ED_view3d.h" /* To draw toolbar UI. */ @@ -49,13 +50,11 @@ #include "UI_resources.h" -#include "GPU_glew.h" - #include "buttons_intern.h" /* own include */ /* ******************** default callbacks for buttons space ***************** */ -static SpaceLink *buttons_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *buttons_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceProperties *sbuts; @@ -139,6 +138,98 @@ static void buttons_main_region_init(wmWindowManager *wm, ARegion *region) WM_event_add_keymap_handler(®ion->handlers, keymap); } +/** + * Fills an array with the tab context values for the properties editor. -1 signals a separator. + * + * \return The total number of items in the array returned. + */ +int ED_buttons_tabs_list(SpaceProperties *sbuts, int *context_tabs_array) +{ + int length = 0; + if (sbuts->pathflag & (1 << BCONTEXT_TOOL)) { + context_tabs_array[length] = BCONTEXT_TOOL; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_RENDER)) { + context_tabs_array[length] = BCONTEXT_RENDER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_OUTPUT)) { + context_tabs_array[length] = BCONTEXT_OUTPUT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) { + context_tabs_array[length] = BCONTEXT_VIEW_LAYER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) { + context_tabs_array[length] = BCONTEXT_SCENE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_WORLD)) { + context_tabs_array[length] = BCONTEXT_WORLD; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) { + context_tabs_array[length] = BCONTEXT_OBJECT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) { + context_tabs_array[length] = BCONTEXT_MODIFIER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) { + context_tabs_array[length] = BCONTEXT_SHADERFX; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) { + context_tabs_array[length] = BCONTEXT_PARTICLE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) { + context_tabs_array[length] = BCONTEXT_PHYSICS; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) { + context_tabs_array[length] = BCONTEXT_CONSTRAINT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_DATA)) { + context_tabs_array[length] = BCONTEXT_DATA; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_BONE)) { + context_tabs_array[length] = BCONTEXT_BONE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_BONE_CONSTRAINT)) { + context_tabs_array[length] = BCONTEXT_BONE_CONSTRAINT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_MATERIAL)) { + context_tabs_array[length] = BCONTEXT_MATERIAL; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) { + context_tabs_array[length] = BCONTEXT_TEXTURE; + length++; + } + + return length; +} + static void buttons_main_region_layout_properties(const bContext *C, SpaceProperties *sbuts, ARegion *region) @@ -618,7 +709,7 @@ void ED_spacetype_buttons(void) st->spaceid = SPACE_PROPERTIES; strncpy(st->name, "Buttons", BKE_ST_MAXNAME); - st->new = buttons_new; + st->create = buttons_create; st->free = buttons_free; st->init = buttons_init; st->duplicate = buttons_duplicate; diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index d33f624063a..1d510d2989c 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -322,7 +322,7 @@ static void draw_movieclip_buffer(const bContext *C, float zoomy) { MovieClip *clip = ED_space_clip_get_clip(sc); - int filter = GL_LINEAR; + bool use_filter = true; int x, y; /* find window pixel coordinates of origin */ @@ -340,10 +340,10 @@ static void draw_movieclip_buffer(const bContext *C, /* non-scaled proxy shouldn't use filtering */ if ((clip->flag & MCLIP_USE_PROXY) == 0 || ELEM(sc->user.render_size, MCLIP_PROXY_RENDER_SIZE_FULL, MCLIP_PROXY_RENDER_SIZE_100)) { - filter = GL_NEAREST; + use_filter = false; } - ED_draw_imbuf_ctx(C, ibuf, x, y, filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y); + ED_draw_imbuf_ctx(C, ibuf, x, y, use_filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y); if (ibuf->planes == 32) { GPU_blend(false); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 9c251fb619a..d27b80efd40 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -56,7 +56,6 @@ #include "IMB_imbuf.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "WM_api.h" @@ -238,7 +237,7 @@ static void clip_area_sync_frame_from_scene(ScrArea *area, Scene *scene) /* ******************** default callbacks for clip space ***************** */ -static SpaceLink *clip_new(const ScrArea *area, const Scene *scene) +static SpaceLink *clip_create(const ScrArea *area, const Scene *scene) { ARegion *region; SpaceClip *sc; @@ -685,7 +684,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (main_visible) { if (region_main && (region_main->flag & RGN_FLAG_HIDDEN)) { region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } @@ -697,7 +696,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_main && !(region_main->flag & RGN_FLAG_HIDDEN)) { region_main->flag |= RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_main->handlers); view_changed = true; } @@ -710,7 +709,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (properties_visible) { if (region_properties && (region_properties->flag & RGN_FLAG_HIDDEN)) { region_properties->flag &= ~RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INITIALISED; + region_properties->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } if (region_properties && region_properties->alignment != RGN_ALIGN_RIGHT) { @@ -721,7 +720,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_properties && !(region_properties->flag & RGN_FLAG_HIDDEN)) { region_properties->flag |= RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INITIALISED; + region_properties->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_properties->handlers); view_changed = true; } @@ -734,7 +733,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (tools_visible) { if (region_tools && (region_tools->flag & RGN_FLAG_HIDDEN)) { region_tools->flag &= ~RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INITIALISED; + region_tools->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } if (region_tools && region_tools->alignment != RGN_ALIGN_LEFT) { @@ -745,7 +744,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_tools && !(region_tools->flag & RGN_FLAG_HIDDEN)) { region_tools->flag |= RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INITIALISED; + region_tools->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_tools->handlers); view_changed = true; } @@ -758,7 +757,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (preview_visible) { if (region_preview && (region_preview->flag & RGN_FLAG_HIDDEN)) { region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; region_preview->v2d.cur = region_preview->v2d.tot; view_changed = true; } @@ -770,7 +769,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_preview && !(region_preview->flag & RGN_FLAG_HIDDEN)) { region_preview->flag |= RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_preview->handlers); view_changed = true; } @@ -783,7 +782,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (channels_visible) { if (region_channels && (region_channels->flag & RGN_FLAG_HIDDEN)) { region_channels->flag &= ~RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INITIALISED; + region_channels->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } if (region_channels && region_channels->alignment != RGN_ALIGN_LEFT) { @@ -794,7 +793,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_channels && !(region_channels->flag & RGN_FLAG_HIDDEN)) { region_channels->flag |= RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INITIALISED; + region_channels->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_channels->handlers); view_changed = true; } @@ -805,7 +804,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) } if (view_changed) { - ED_area_initialize(wm, window, area); + ED_area_init(wm, window, area); ED_area_tag_redraw(area); } @@ -1352,7 +1351,7 @@ void ED_spacetype_clip(void) st->spaceid = SPACE_CLIP; strncpy(st->name, "Clip", BKE_ST_MAXNAME); - st->new = clip_new; + st->create = clip_create; st->free = clip_free; st->init = clip_init; st->duplicate = clip_duplicate; diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 3c62aeb1759..3a0125356f7 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -46,7 +46,7 @@ /* ******************** default callbacks for console space ***************** */ -static SpaceLink *console_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *console_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceConsole *sconsole; @@ -312,7 +312,7 @@ void ED_spacetype_console(void) st->spaceid = SPACE_CONSOLE; strncpy(st->name, "Console", BKE_ST_MAXNAME); - st->new = console_new; + st->create = console_create; st->free = console_free; st->init = console_init; st->duplicate = console_duplicate; diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 5150f6bed69..083d41747b3 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -300,9 +300,8 @@ static void file_draw_preview(uiBlock *block, (float)yco, imb->x, imb->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, imb->rect, scale, scale, diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 43939b9ff54..f520f91b89b 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -91,7 +91,7 @@ static ARegion *file_tool_props_region_ensure(ScrArea *area, ARegion *region_pre /* ******************** default callbacks for file space ***************** */ -static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *file_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceFile *sfile; @@ -235,7 +235,7 @@ static void file_ensure_valid_region_state(bContext *C, ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI); ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS); ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE); - bool needs_init = false; /* To avoid multiple ED_area_initialize() calls. */ + bool needs_init = false; /* To avoid multiple ED_area_init() calls. */ /* If there's an file-operation, ensure we have the option and execute region */ if (sfile->op && (region_props == NULL)) { @@ -261,7 +261,7 @@ static void file_ensure_valid_region_state(bContext *C, } if (needs_init) { - ED_area_initialize(wm, win, area); + ED_area_init(wm, win, area); } } @@ -693,7 +693,7 @@ void ED_spacetype_file(void) st->spaceid = SPACE_FILE; strncpy(st->name, "File", BKE_ST_MAXNAME); - st->new = file_new; + st->create = file_create; st->free = file_free; st->init = file_init; st->exit = file_exit; diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 68fdef54a53..90fe95c6818 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1487,7 +1487,7 @@ static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent dgo->area = CTX_wm_area(C); dgo->region = CTX_wm_region(C); - /* initialise percentage so that it will have the correct value before the first mouse move. */ + /* Initialize percentage so that it will have the correct value before the first mouse move. */ decimate_mouse_update_percentage(dgo, op, event); decimate_draw_status_header(op, dgo); diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index b1d995a7a0b..a4f76384cc6 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -64,7 +64,7 @@ /* ******************** default callbacks for ipo space ***************** */ -static SpaceLink *graph_new(const ScrArea *UNUSED(area), const Scene *scene) +static SpaceLink *graph_create(const ScrArea *UNUSED(area), const Scene *scene) { ARegion *region; SpaceGraph *sipo; @@ -838,7 +838,7 @@ void ED_spacetype_ipo(void) st->spaceid = SPACE_GRAPH; strncpy(st->name, "Graph", BKE_ST_MAXNAME); - st->new = graph_new; + st->create = graph_create; st->free = graph_free; st->init = graph_init; st->duplicate = graph_duplicate; diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index a7fa7709c51..1038011e480 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -468,20 +468,19 @@ static void sima_draw_zbuf_pixels( float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; /* Slowwww */ - int *recti = MEM_mallocN(rectx * recty * sizeof(int), "temp"); + float *rectf = MEM_mallocN(rectx * recty * sizeof(float), "temp"); for (int a = rectx * recty - 1; a >= 0; a--) { /* zbuffer values are signed, so we need to shift color range */ - recti[a] = rect[a] * 0.5f + 0.5f; + rectf[a] = rect[a] * 0.5f + 0.5f; } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex( - &state, x1, y1, rectx, recty, GL_RED, GL_INT, GL_NEAREST, recti, zoomx, zoomy, NULL); + immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL); - MEM_freeN(recti); + MEM_freeN(rectf); } static void sima_draw_zbuffloat_pixels(Scene *scene, @@ -526,8 +525,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex( - &state, x1, y1, rectx, recty, GL_RED, GL_FLOAT, GL_NEAREST, rectf, zoomx, zoomy, NULL); + immDrawPixelsTex(&state, x1, y1, rectx, recty, GL_R16F, false, rectf, zoomx, zoomy, NULL); MEM_freeN(rectf); } @@ -612,8 +610,7 @@ static void draw_image_buffer(const bContext *C, /* If RGBA display with color management */ if ((sima_flag & (SI_SHOW_R | SI_SHOW_G | SI_SHOW_B | SI_SHOW_ALPHA)) == 0) { - ED_draw_imbuf_ctx_clipping( - C, ibuf, x, y, GL_NEAREST, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy); + ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, false, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy); } else { float shuffle[4] = {0.0f, 0.0f, 0.0f, 0.0f}; @@ -649,9 +646,8 @@ static void draw_image_buffer(const bContext *C, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, display_buffer, 0, 0, @@ -780,18 +776,8 @@ static void draw_image_paint_helpers( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTex(&state, - x, - y, - ibuf->x, - ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - display_buffer, - zoomx, - zoomy, - col); + immDrawPixelsTex( + &state, x, y, ibuf->x, ibuf->y, GPU_RGBA8, false, display_buffer, zoomx, zoomy, col); GPU_blend(false); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 4e410d35df0..1a98ec0e7c1 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -69,7 +69,6 @@ #include "DEG_depsgraph.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_state.h" @@ -2769,7 +2768,7 @@ static int image_invert_exec(bContext *C, wmOperator *op) ED_image_undo_push_end(); /* force GPU reupload, all image is invalid */ - GPU_free_image(ima); + BKE_image_free_gputextures(ima); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -2860,7 +2859,7 @@ static int image_scale_exec(bContext *C, wmOperator *op) ED_image_undo_push_end(); /* force GPU reupload, all image is invalid */ - GPU_free_image(ima); + BKE_image_free_gputextures(ima); DEG_id_tag_update(&ima->id, 0); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -3715,7 +3714,7 @@ static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout) uiItemR(col[1], ptr, "float", 0, NULL, ICON_NONE); } -static void initialize_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile) +static void tile_fill_init(PointerRNA *ptr, Image *ima, ImageTile *tile) { ImageUser iuser; BKE_imageuser_default(&iuser); @@ -3828,7 +3827,7 @@ static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev } ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index); - initialize_fill_tile(op->ptr, ima, tile); + tile_fill_init(op->ptr, ima, tile); RNA_int_set(op->ptr, "number", next_number); RNA_int_set(op->ptr, "count", 1); @@ -3974,7 +3973,7 @@ static int tile_fill_exec(bContext *C, wmOperator *op) static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - initialize_fill_tile(op->ptr, CTX_data_edit_image(C), NULL); + tile_fill_init(op->ptr, CTX_data_edit_image(C), NULL); return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X); } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index e0c44c3a0ba..27b84307f7d 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -60,8 +60,6 @@ #include "ED_undo.h" #include "ED_util.h" -#include "GPU_draw.h" - #include "WM_api.h" static CLG_LogRef LOG = {"ed.image.undo"}; @@ -295,7 +293,8 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) SWAP(uint *, ptile->rect.uint, tmpibuf->rect); } - GPU_free_image(image); /* force OpenGL reload (maybe partial update will operate better?) */ + BKE_image_free_gputextures( + image); /* force OpenGL reload (maybe partial update will operate better?) */ if (ibuf->rect_float) { ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ } @@ -570,7 +569,7 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init) if (changed) { BKE_image_mark_dirty(image, ibuf); - GPU_free_image(image); /* force OpenGL reload */ + BKE_image_free_gputextures(image); /* force OpenGL reload */ if (ibuf->rect_float) { ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index ac0dbba1606..c01bc01588e 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -116,7 +116,7 @@ static void image_user_refresh_scene(const bContext *C, SpaceImage *sima) /* ******************** default callbacks for image space ***************** */ -static SpaceLink *image_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceImage *simage; @@ -1093,7 +1093,7 @@ void ED_spacetype_image(void) st->spaceid = SPACE_IMAGE; strncpy(st->name, "Image", BKE_ST_MAXNAME); - st->new = image_new; + st->create = image_create; st->free = image_free; st->init = image_init; st->duplicate = image_duplicate; diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index 836830916ed..b9153ec0cbd 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -53,7 +53,7 @@ /* ******************** default callbacks for info space ***************** */ -static SpaceLink *info_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *info_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceInfo *sinfo; @@ -287,7 +287,7 @@ void ED_spacetype_info(void) st->spaceid = SPACE_INFO; strncpy(st->name, "Info", BKE_ST_MAXNAME); - st->new = info_new; + st->create = info_create; st->free = info_free; st->init = info_init; st->duplicate = info_duplicate; diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index b09536e0621..7bbfe451eed 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -57,7 +57,7 @@ /* ******************** default callbacks for nla space ***************** */ -static SpaceLink *nla_new(const ScrArea *area, const Scene *scene) +static SpaceLink *nla_create(const ScrArea *area, const Scene *scene) { ARegion *region; SpaceNla *snla; @@ -608,7 +608,7 @@ void ED_spacetype_nla(void) st->spaceid = SPACE_NLA; strncpy(st->name, "NLA", BKE_ST_MAXNAME); - st->new = nla_new; + st->create = nla_create; st->free = nla_free; st->init = nla_init; st->duplicate = nla_duplicate; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 01883f1c086..207f67aed1b 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3678,9 +3678,8 @@ void draw_nodespace_back_pix(const bContext *C, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, display_buffer, snode->zoom, snode->zoom, @@ -3693,12 +3692,12 @@ void draw_nodespace_back_pix(const bContext *C, GPU_blend_set_func_separate( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - ED_draw_imbuf_ctx(C, ibuf, x, y, GL_NEAREST, snode->zoom, snode->zoom); + ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom); GPU_blend(false); } else { - ED_draw_imbuf_ctx(C, ibuf, x, y, GL_NEAREST, snode->zoom, snode->zoom); + ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom); } if (cache_handle) { diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 22b549cbd5d..52bd4d68649 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -947,9 +947,8 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv) draw_rect.ymin, preview->xsize, preview->ysize, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_LINEAR, + GPU_RGBA8, + true, preview->rect, scale, scale, diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 7af64e75656..c88b6a1b297 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -1697,6 +1697,8 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) } } + do_tag_update |= ED_node_is_simulation(snode); + snode_notify(C, snode); if (do_tag_update) { snode_dag_update(C, snode); @@ -1739,6 +1741,8 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) } } + do_tag_update |= ED_node_is_simulation(snode); + ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); @@ -2739,7 +2743,7 @@ static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op)) void NODE_OT_clear_viewer_border(wmOperatorType *ot) { /* identifiers */ - ot->name = "Clear Viewer Border"; + ot->name = "Clear Viewer Region"; ot->description = "Clear the boundaries for viewer operations"; ot->idname = "NODE_OT_clear_viewer_border"; diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 3e898b7d400..9110d82fb84 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -670,6 +670,8 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) } ntree->is_updating = false; + do_tag_update |= ED_node_is_simulation(snode); + ntreeUpdateTree(bmain, ntree); snode_notify(C, snode); if (do_tag_update) { @@ -1070,6 +1072,8 @@ static int cut_links_exec(bContext *C, wmOperator *op) } } + do_tag_update |= ED_node_is_simulation(snode); + if (found) { ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index d4adad3fc25..6d570001347 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -244,7 +244,7 @@ void snode_group_offset(SpaceNode *snode, float *x, float *y) /* ******************** default callbacks for node space ***************** */ -static SpaceLink *node_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *node_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceNode *snode; @@ -954,7 +954,7 @@ void ED_spacetype_node(void) st->spaceid = SPACE_NODE; strncpy(st->name, "Node", BKE_ST_MAXNAME); - st->new = node_new; + st->create = node_create; st->free = node_free; st->init = node_init; st->duplicate = node_duplicate; diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index aa1663dff01..8a78211f145 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -294,7 +294,7 @@ static void outliner_header_region_listener(wmWindow *UNUSED(win), /* ******************** default callbacks for outliner space ***************** */ -static SpaceLink *outliner_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *outliner_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceOutliner *soutliner; @@ -407,7 +407,7 @@ void ED_spacetype_outliner(void) st->spaceid = SPACE_OUTLINER; strncpy(st->name, "Outliner", BKE_ST_MAXNAME); - st->new = outliner_new; + st->create = outliner_create; st->free = outliner_free; st->init = outliner_init; st->duplicate = outliner_duplicate; diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c index 343f35421a4..4d0c2b658c6 100644 --- a/source/blender/editors/space_script/space_script.c +++ b/source/blender/editors/space_script/space_script.c @@ -51,7 +51,7 @@ /* ******************** default callbacks for script space ***************** */ -static SpaceLink *script_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *script_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceScript *sscript; @@ -179,7 +179,7 @@ void ED_spacetype_script(void) st->spaceid = SPACE_SCRIPT; strncpy(st->name, "Script", BKE_ST_MAXNAME); - st->new = script_new; + st->create = script_create; st->free = script_free; st->init = script_init; st->duplicate = script_duplicate; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 995e980aba0..7863d2b724d 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1599,7 +1599,7 @@ static void sequencer_draw_display_buffer(const bContext *C, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); } - /* Format needs to be created prior to any immBindProgram call. + /* Format needs to be created prior to any immBindShader call. * Do it here because OCIO binds it's own shader. */ eGPUTextureFormat format; eGPUDataFormat data; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index b2d0362602e..99d4c2d9f1a 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -666,74 +666,6 @@ int seq_effect_find_selected(Scene *scene, /** \name Delete Utilities * \{ */ -static Sequence *del_seq_find_replace_recurs(Scene *scene, Sequence *seq) -{ - Sequence *seq1, *seq2, *seq3; - - /* Try to find a replacement input sequence, and flag for later deletion if - * no replacement can be found. */ - - if (!seq) { - return NULL; - } - if (!(seq->type & SEQ_TYPE_EFFECT)) { - return ((seq->flag & SELECT) ? NULL : seq); - } - if (!(seq->flag & SELECT)) { - /* Try to find replacement for effect inputs. */ - seq1 = del_seq_find_replace_recurs(scene, seq->seq1); - seq2 = del_seq_find_replace_recurs(scene, seq->seq2); - seq3 = del_seq_find_replace_recurs(scene, seq->seq3); - - if (seq1 == seq->seq1 && seq2 == seq->seq2 && seq3 == seq->seq3) { - /* Pass. */ - } - else if (seq1 || seq2 || seq3) { - seq->seq1 = (seq1) ? seq1 : (seq2) ? seq2 : seq3; - seq->seq2 = (seq2) ? seq2 : (seq1) ? seq1 : seq3; - seq->seq3 = (seq3) ? seq3 : (seq1) ? seq1 : seq2; - - BKE_sequencer_update_changed_seq_and_deps(scene, seq, 1, 1); - } - else { - seq->flag |= SELECT; /* Mark for delete. */ - } - } - - if (seq->flag & SELECT) { - if ((seq1 = del_seq_find_replace_recurs(scene, seq->seq1))) { - return seq1; - } - if ((seq2 = del_seq_find_replace_recurs(scene, seq->seq2))) { - return seq2; - } - if ((seq3 = del_seq_find_replace_recurs(scene, seq->seq3))) { - return seq3; - } - return NULL; - } - return seq; -} - -static void del_seq_clear_modifiers_recurs(Scene *scene, Sequence *deleting_sequence) -{ - Editing *ed = BKE_sequencer_editing_get(scene, false); - Sequence *current_sequence; - - SEQP_BEGIN (ed, current_sequence) { - if (!(current_sequence->flag & SELECT) && current_sequence != deleting_sequence) { - SequenceModifierData *smd; - - for (smd = current_sequence->modifiers.first; smd; smd = smd->next) { - if (smd->mask_sequence == deleting_sequence) { - smd->mask_sequence = NULL; - } - } - } - } - SEQ_END; -} - static void recurs_del_seq_flag(Scene *scene, ListBase *lb, short flag, short deleteall) { Sequence *seq, *seqn; @@ -2586,61 +2518,20 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); Sequence *seq; - MetaStack *ms; - bool nothing_selected = true; BKE_sequencer_prefetch_stop(scene); - seq = BKE_sequencer_active_get(scene); - if (seq && seq->flag & SELECT) { /* Avoid a loop since this is likely to be selected. */ - nothing_selected = false; - } - else { - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->flag & SELECT) { - nothing_selected = false; - break; - } - } - } - - if (nothing_selected) { - return OPERATOR_FINISHED; - } - - /* For effects and modifiers, try to find a replacement input. */ - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (!(seq->flag & SELECT)) { - if ((seq->type & SEQ_TYPE_EFFECT)) { - del_seq_find_replace_recurs(scene, seq); - } - } - else { - del_seq_clear_modifiers_recurs(scene, seq); + SEQP_BEGIN (scene->ed, seq) { + if (seq->flag & SELECT) { + BKE_sequencer_flag_for_removal(scene, ed->seqbasep, seq); } } - - /* Delete all selected strips. */ - recurs_del_seq_flag(scene, ed->seqbasep, SELECT, 0); - - /* Update lengths, etc. */ - seq = ed->seqbasep->first; - while (seq) { - BKE_sequence_calc(scene, seq); - seq = seq->next; - } - - /* Free parent metas. */ - ms = ed->metastack.last; - while (ms) { - BKE_sequence_calc(scene, ms->parseq); - ms = ms->prev; - } + SEQ_END; + BKE_sequencer_remove_flagged_sequences(scene, ed->seqbasep); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - return OPERATOR_FINISHED; } @@ -3036,8 +2927,8 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) } /* This moves strips from meta to parent, sating within same edit and no new strips are - * allocated. If the UUID was unique already (as it should) it will stay unique. Nn need to - * re-generate the UUIDs.*/ + * allocated. If the UUID was unique already (as it should) it will stay unique. + * No need to re-generate the UUIDs. */ BLI_movelisttolist(ed->seqbasep, &last_seq->seqbase); BLI_listbase_clear(&last_seq->seqbase); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 926752c6488..b8bb3e4d43b 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -86,7 +86,7 @@ static ARegion *sequencer_find_region(ScrArea *area, short type) /* ******************** default callbacks for sequencer space ***************** */ -static SpaceLink *sequencer_new(const ScrArea *UNUSED(area), const Scene *scene) +static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *scene) { ARegion *region; SpaceSeq *sseq; @@ -231,12 +231,12 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) case SEQ_VIEW_SEQUENCE: if (region_main && (region_main->flag & RGN_FLAG_HIDDEN)) { region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } if (region_preview && !(region_preview->flag & RGN_FLAG_HIDDEN)) { region_preview->flag |= RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_preview->handlers); view_changed = true; } @@ -252,13 +252,13 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) case SEQ_VIEW_PREVIEW: if (region_main && !(region_main->flag & RGN_FLAG_HIDDEN)) { region_main->flag |= RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_main->handlers); view_changed = true; } if (region_preview && (region_preview->flag & RGN_FLAG_HIDDEN)) { region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; region_preview->v2d.cur = region_preview->v2d.tot; view_changed = true; } @@ -281,13 +281,13 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) * 'full window' views before, though... Better than nothing. */ if (region_main->flag & RGN_FLAG_HIDDEN) { region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; region_preview->sizey = (int)(height - region_main->sizey); view_changed = true; } if (region_preview->flag & RGN_FLAG_HIDDEN) { region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; region_preview->v2d.cur = region_preview->v2d.tot; region_main->sizey = (int)(height - region_preview->sizey); view_changed = true; @@ -312,7 +312,7 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) } if (view_changed) { - ED_area_initialize(wm, window, area); + ED_area_init(wm, window, area); ED_area_tag_redraw(area); } } @@ -852,7 +852,7 @@ void ED_spacetype_sequencer(void) st->spaceid = SPACE_SEQ; strncpy(st->name, "Sequencer", BKE_ST_MAXNAME); - st->new = sequencer_new; + st->create = sequencer_create; st->free = sequencer_free; st->init = sequencer_init; st->duplicate = sequencer_duplicate; diff --git a/source/blender/editors/space_statusbar/space_statusbar.c b/source/blender/editors/space_statusbar/space_statusbar.c index 34d7f8b0216..ae56b111360 100644 --- a/source/blender/editors/space_statusbar/space_statusbar.c +++ b/source/blender/editors/space_statusbar/space_statusbar.c @@ -42,7 +42,7 @@ /* ******************** default callbacks for statusbar space ******************** */ -static SpaceLink *statusbar_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *statusbar_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceStatusBar *sstatusbar; @@ -158,7 +158,7 @@ void ED_spacetype_statusbar(void) st->spaceid = SPACE_STATUSBAR; strncpy(st->name, "Status Bar", BKE_ST_MAXNAME); - st->new = statusbar_new; + st->create = statusbar_create; st->free = statusbar_free; st->init = statusbar_init; st->duplicate = statusbar_duplicate; diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index a2af99ee9f9..f6d00ec94bf 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -53,7 +53,7 @@ /* ******************** default callbacks for text space ***************** */ -static SpaceLink *text_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *text_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceText *stext; @@ -445,7 +445,7 @@ void ED_spacetype_text(void) st->spaceid = SPACE_TEXT; strncpy(st->name, "Text", BKE_ST_MAXNAME); - st->new = text_new; + st->create = text_create; st->free = text_free; st->init = text_init; st->duplicate = text_duplicate; diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index d06c567988d..dc357cdd355 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -52,7 +52,7 @@ /* ******************** default callbacks for topbar space ***************** */ -static SpaceLink *topbar_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *topbar_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceTopBar *stopbar; @@ -250,7 +250,7 @@ void ED_spacetype_topbar(void) st->spaceid = SPACE_TOPBAR; strncpy(st->name, "Top Bar", BKE_ST_MAXNAME); - st->new = topbar_new; + st->create = topbar_create; st->free = topbar_free; st->init = topbar_init; st->duplicate = topbar_duplicate; diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c index 9eae722d5c8..0242bb4fe24 100644 --- a/source/blender/editors/space_userpref/space_userpref.c +++ b/source/blender/editors/space_userpref/space_userpref.c @@ -45,7 +45,7 @@ /* ******************** default callbacks for userpref space ***************** */ -static SpaceLink *userpref_new(const ScrArea *area, const Scene *UNUSED(scene)) +static SpaceLink *userpref_create(const ScrArea *area, const Scene *UNUSED(scene)) { ARegion *region; SpaceUserPref *spref; @@ -115,7 +115,7 @@ static void userpref_main_region_init(wmWindowManager *wm, ARegion *region) { /* do not use here, the properties changed in userprefs do a system-wide refresh, * then scroller jumps back */ - /* region->v2d.flag &= ~V2D_IS_INITIALISED; */ + /* region->v2d.flag &= ~V2D_IS_INIT; */ region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE; @@ -235,7 +235,7 @@ void ED_spacetype_userpref(void) st->spaceid = SPACE_USERPREF; strncpy(st->name, "Userpref", BKE_ST_MAXNAME); - st->new = userpref_new; + st->create = userpref_create; st->free = userpref_free; st->init = userpref_init; st->duplicate = userpref_duplicate; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index c88303daa16..e5ba27cef07 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -260,7 +260,7 @@ void ED_view3d_shade_update(Main *bmain, View3D *v3d, ScrArea *area) /* ******************** default callbacks for view3d space ***************** */ -static SpaceLink *view3d_new(const ScrArea *UNUSED(area), const Scene *scene) +static SpaceLink *view3d_create(const ScrArea *UNUSED(area), const Scene *scene) { ARegion *region; View3D *v3d; @@ -616,11 +616,12 @@ static void view3d_lightcache_update(bContext *C) return; } - WM_operator_properties_create(&op_ptr, "SCENE_OT_light_cache_bake"); + wmOperatorType *ot = WM_operatortype_find("SCENE_OT_light_cache_bake", true); + WM_operator_properties_create_ptr(&op_ptr, ot); RNA_int_set(&op_ptr, "delay", 200); RNA_enum_set_identifier(C, &op_ptr, "subset", "DIRTY"); - WM_operator_name_call(C, "SCENE_OT_light_cache_bake", WM_OP_INVOKE_DEFAULT, &op_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr); WM_operator_properties_free(&op_ptr); } @@ -1610,7 +1611,7 @@ void ED_spacetype_view3d(void) st->spaceid = SPACE_VIEW3D; strncpy(st->name, "View3D", BKE_ST_MAXNAME); - st->new = view3d_new; + st->create = view3d_create; st->free = view3d_free; st->init = view3d_init; st->listener = space_view3d_listener; diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 2e170126574..d78c58c0c64 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1474,6 +1474,7 @@ static void v3d_editmetaball_buts(uiLayout *layout, Object *ob) uiLayout *col; if (!mball || !(mball->lastelem)) { + uiItemL(layout, IFACE_("Nothing selected"), ICON_NONE); return; } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 1af0cc074fc..0442a0f35c9 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -36,6 +36,7 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_global.h" +#include "BKE_image.h" #include "BKE_key.h" #include "BKE_layer.h" #include "BKE_main.h" @@ -74,7 +75,6 @@ #include "GPU_batch.h" #include "GPU_batch_presets.h" -#include "GPU_draw.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" @@ -1620,7 +1620,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *region) view3d_draw_view(C, region); DRW_cache_free_old_batches(bmain); - GPU_free_images_old(bmain); + BKE_image_free_old_gputextures(bmain); GPU_pass_cache_garbage_collect(); /* XXX This is in order to draw UI batches with the DRW @@ -1710,7 +1710,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, { /* free images which can have changed on frame-change * warning! can be slow so only free animated images - campbell */ - GPU_free_images_anim(G.main); /* XXX :((( */ + BKE_image_free_anim_gputextures(G.main); /* XXX :((( */ } GPU_matrix_push_projection(); @@ -1957,10 +1957,10 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, NULL); if (ibuf->rect_float) { - GPU_offscreen_read_pixels(ofs, GL_FLOAT, ibuf->rect_float); + GPU_offscreen_read_pixels(ofs, GPU_DATA_FLOAT, ibuf->rect_float); } else if (ibuf->rect) { - GPU_offscreen_read_pixels(ofs, GL_UNSIGNED_BYTE, ibuf->rect); + GPU_offscreen_read_pixels(ofs, GPU_DATA_UNSIGNED_BYTE, ibuf->rect); } /* unbind */ diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 64447015bdc..9490c807989 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -97,7 +97,6 @@ #include "UI_interface.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "DEG_depsgraph.h" @@ -3336,12 +3335,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) FOREACH_OBJECT_IN_MODE_END; } else { /* No edit-mode, unified for bones and objects. */ - if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) { - /* XXX, this is not selection, could be it's own operator. */ - changed_multi = ED_sculpt_mask_box_select( - C, &vc, &rect, sel_op == SEL_OP_ADD ? true : false); - } - else if (vc.obact && BKE_paint_select_face_test(vc.obact)) { + if (vc.obact && BKE_paint_select_face_test(vc.obact)) { changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op); } else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) { diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 66efa5b5de3..ff9673a4262 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -50,7 +50,6 @@ #include "UI_resources.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_state.h" diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index bc00dd8e221..1917d9463f4 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -80,6 +80,7 @@ typedef struct TransSnap { bool snap_self; bool peel; bool snap_spatial_grid; + bool use_backface_culling; char status; /* Snapped Element Type (currently for objects only). */ char snapElem; diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index 0eb12aeabed..84885dd8c49 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -104,7 +104,7 @@ void createTransGPencil(bContext *C, TransInfo *t) /* initialize falloff curve */ if (is_multiedit) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); } /* First Pass: Count the number of data-points required for the strokes, diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index ad426713719..6f447997fb0 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1033,8 +1033,10 @@ static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t), # define FACE_SUBSTITUTE_INDEX INT_MIN -/* Search for a neighboring face with area and preferably without selected vertex. - * Used to replace arealess faces in customdata correction. */ +/** + * Search for a neighboring face with area and preferably without selected vertex. + * Used to replace area-less faces in custom-data correction. + */ static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f) { BMFace *best_face = NULL; diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c index fa33c1550e7..e508a1fa4c2 100644 --- a/source/blender/editors/transform/transform_mode_shear.c +++ b/source/blender/editors/transform/transform_mode_shear.c @@ -164,7 +164,7 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData *td = tc->data; for (i = 0; i < tc->data_len; i++, td++) { - const float *center, *co; + const float *center; if (td->flag & TD_SKIP) { continue; } @@ -178,19 +178,15 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) if (is_local_center) { center = td->center; - co = td->loc; } else { center = tc->center_local; - co = td->center; } - sub_v3_v3v3(vec, co, center); - + sub_v3_v3v3(vec, td->iloc, center); mul_m3_v3(tmat, vec); - add_v3_v3(vec, center); - sub_v3_v3(vec, co); + sub_v3_v3(vec, td->iloc); if (t->options & CTX_GPENCIL_STROKES) { /* grease pencil multiframe falloff */ diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 2943c3cb8ea..a700dd320b7 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -37,6 +37,7 @@ #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_object.h" +#include "BKE_scene.h" #include "BKE_sequencer.h" #include "RNA_access.h" @@ -100,6 +101,24 @@ int BIF_snappingSupported(Object *obedit) } #endif +static bool snap_use_backface_culling(const TransInfo *t) +{ + BLI_assert(t->spacetype == SPACE_VIEW3D); + View3D *v3d = t->view; + if ((v3d->shading.type == OB_SOLID) && (v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)) { + return true; + } + if (v3d->shading.type == OB_RENDER && + (t->scene->display.shading.flag & V3D_SHADING_BACKFACE_CULLING) && + BKE_scene_uses_blender_workbench(t->scene)) { + return true; + } + if (t->settings->snap_flag & SCE_SNAP_BACKFACE_CULLING) { + return true; + } + return false; +} + bool validSnap(const TransInfo *t) { return (t->tsnap.status & (POINT_INIT | TARGET_INIT)) == (POINT_INIT | TARGET_INIT) || @@ -315,8 +334,7 @@ void applyProject(TransInfo *t) .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, .use_occlusion_test = false, - .use_backface_culling = (t->scene->toolsettings->snap_flag & - SCE_SNAP_BACKFACE_CULLING) != 0, + .use_backface_culling = t->tsnap.use_backface_culling, }, mval_fl, NULL, @@ -601,6 +619,7 @@ static void initSnappingMode(TransInfo *t) if (t->spacetype == SPACE_VIEW3D) { if (t->tsnap.object_context == NULL) { + t->tsnap.use_backface_culling = snap_use_backface_culling(t); t->tsnap.object_context = ED_transform_snap_object_context_create_view3d( t->scene, 0, t->region, t->view); @@ -1120,13 +1139,12 @@ short snapObjectsTransform( return ED_transform_snap_object_project_view3d_ex( t->tsnap.object_context, t->depsgraph, - t->scene->toolsettings->snap_mode, + t->settings->snap_mode, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, - .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE, - .use_backface_culling = (t->scene->toolsettings->snap_flag & - SCE_SNAP_BACKFACE_CULLING) != 0, + .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE, + .use_backface_culling = t->tsnap.use_backface_culling, }, mval, target, diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 3c747a29361..384da6fb931 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -26,6 +26,8 @@ #include "BLI_string_utf8.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_scene.h" #include "BKE_unit.h" @@ -277,8 +279,12 @@ static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf return true; } -bool user_string_to_number( - bContext *C, const char *str, const UnitSettings *unit, int type, double *r_value) +bool user_string_to_number(bContext *C, + const char *str, + const UnitSettings *unit, + int type, + const char *error_prefix, + double *r_value) { #ifdef WITH_PYTHON double unit_scale = BKE_scene_unit_scale(unit, type, 1.0); @@ -288,10 +294,10 @@ bool user_string_to_number( bUnit_ReplaceString( str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type); - return BPY_execute_string_as_number(C, NULL, str_unit_convert, true, r_value); + return BPY_execute_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value); } - int success = BPY_execute_string_as_number(C, NULL, str, true, r_value); + int success = BPY_execute_string_as_number(C, NULL, str, error_prefix, r_value); *r_value *= bUnit_PreferredInputUnitScalar(unit, type); *r_value /= unit_scale; return success; @@ -573,7 +579,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) Scene *sce = CTX_data_scene(C); double val; - int success = user_string_to_number(C, n->str, &sce->unit, n->unit_type[idx], &val); + int success = user_string_to_number( + C, n->str, &sce->unit, n->unit_type[idx], IFACE_("Numeric input evaluation"), &val); if (success) { n->val[idx] = (float)val; diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index daa31869a11..7455004ccb8 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -116,6 +116,7 @@ void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); void UV_OT_rip(struct wmOperatorType *ot); void UV_OT_stitch(struct wmOperatorType *ot); +void UV_OT_smart_project(struct wmOperatorType *ot); /* uvedit_path.c */ void UV_OT_shortest_path_pick(struct wmOperatorType *ot); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 532061e3dc1..956c094c19b 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -2094,6 +2094,7 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_reset); WM_operatortype_append(UV_OT_sphere_project); WM_operatortype_append(UV_OT_unwrap); + WM_operatortype_append(UV_OT_smart_project); WM_operatortype_append(UV_OT_reveal); WM_operatortype_append(UV_OT_hide); diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 6fcfb0e0bfc..2f30ef1caa6 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -35,7 +35,10 @@ #include "DNA_scene_types.h" #include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_linklist.h" #include "BLI_math.h" +#include "BLI_memarena.h" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BLI_uvproject.h" @@ -147,7 +150,13 @@ typedef struct UnwrapOptions { /** Connectivity based on UV coordinates instead of seams. */ bool topology_from_uvs; /** Only affect selected faces. */ - bool only_selected; + bool only_selected_faces; + /** + * Only affect selected UV's. + * \note Disable this for operations that don't run in the image-window. + * Unwrapping from the 3D view for example, where only 'only_selected_faces' should be used. + */ + bool only_selected_uvs; /** Fill holes to better preserve shape. */ bool fill_holes; /** Correct for mapped image texture aspect ratio. */ @@ -299,7 +308,7 @@ static ParamHandle *construct_param_handle(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || - (options->only_selected && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { + (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } @@ -307,10 +316,12 @@ static ParamHandle *construct_param_handle(const Scene *scene, bool is_loopsel = false; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - is_loopsel = true; - break; + if (options->only_selected_uvs && + (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { + continue; } + is_loopsel = true; + break; } if (is_loopsel == false) { continue; @@ -382,7 +393,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || - (options->only_selected && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { + (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } @@ -390,10 +401,12 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, bool is_loopsel = false; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - is_loopsel = true; - break; + if (options->only_selected_uvs && + (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { + continue; } + is_loopsel = true; + break; } if (is_loopsel == false) { continue; @@ -571,7 +584,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, } else { if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN) || - (options->only_selected && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) { + (options->only_selected_faces && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) { continue; } } @@ -671,7 +684,8 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = true, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .correct_aspect = true, }; @@ -933,7 +947,8 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm) { const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = false, }; @@ -975,7 +990,8 @@ static int pack_islands_exec(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = true, }; @@ -1040,7 +1056,8 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = true, }; @@ -1114,7 +1131,8 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit) const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0, .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0, }; @@ -1471,18 +1489,21 @@ static void correct_uv_aspect(Object *ob, BMEditMesh *em) /** \name UV Map Clip & Correct * \{ */ -static void uv_map_clip_correct_properties(wmOperatorType *ot) +static void uv_map_clip_correct_properties_ex(wmOperatorType *ot, bool clip_to_bounds) { RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UVs taking image aspect ratio into account"); - RNA_def_boolean(ot->srna, - "clip_to_bounds", - 0, - "Clip to Bounds", - "Clip UV coordinates to bounds after unwrapping"); + /* Optional, since not all unwrapping types need to be clipped. */ + if (clip_to_bounds) { + RNA_def_boolean(ot->srna, + "clip_to_bounds", + 0, + "Clip to Bounds", + "Clip UV coordinates to bounds after unwrapping"); + } RNA_def_boolean(ot->srna, "scale_to_bounds", 0, @@ -1490,6 +1511,11 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) "Scale UV coordinates to bounds after unwrapping"); } +static void uv_map_clip_correct_properties(wmOperatorType *ot) +{ + uv_map_clip_correct_properties_ex(ot, true); +} + static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op) { BMFace *efa; @@ -1498,7 +1524,8 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper MLoopUV *luv; float dx, dy, min[2], max[2]; const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); - const bool clip_to_bounds = RNA_boolean_get(op->ptr, "clip_to_bounds"); + const bool clip_to_bounds = (RNA_struct_find_property(op->ptr, "clip_to_bounds") && + RNA_boolean_get(op->ptr, "clip_to_bounds")); const bool scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds"); INIT_MINMAX2(min, max); @@ -1635,7 +1662,8 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len if (scene->toolsettings->edge_mode_live_unwrap) { const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0, .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0, }; @@ -1670,7 +1698,8 @@ static int unwrap_exec(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), .correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"), }; @@ -1829,6 +1858,365 @@ void UV_OT_unwrap(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Smart UV Project Operator + * \{ */ + +/* Ignore all areas below this, as the UV's get zeroed. */ +static const float smart_uv_project_area_ignore = 1e-12f; + +typedef struct ThickFace { + float area; + BMFace *efa; +} ThickFace; + +static int smart_uv_project_thickface_area_cmp_fn(const void *tf_a_p, const void *tf_b_p) +{ + + const ThickFace *tf_a = (ThickFace *)tf_a_p; + const ThickFace *tf_b = (ThickFace *)tf_b_p; + + /* Ignore the area of small faces. + * Also, order checks so `!isfinite(...)` values are counted as zero area. */ + if (!((tf_a->area > smart_uv_project_area_ignore) || + (tf_b->area > smart_uv_project_area_ignore))) { + return 0; + } + + if (tf_a->area < tf_b->area) { + return 1; + } + else if (tf_a->area > tf_b->area) { + return -1; + } + else { + return 0; + } +} + +static uint smart_uv_project_calculate_project_normals(const ThickFace *thick_faces, + const uint thick_faces_len, + BMesh *bm, + const float project_angle_limit_half_cos, + const float project_angle_limit_cos, + const float area_weight, + float (**r_project_normal_array)[3]) +{ + if (UNLIKELY(thick_faces_len == 0)) { + *r_project_normal_array = NULL; + return 0; + } + + const float *project_normal = thick_faces[0].efa->no; + + const ThickFace **project_thick_faces = NULL; + BLI_array_declare(project_thick_faces); + + float(*project_normal_array)[3] = NULL; + BLI_array_declare(project_normal_array); + + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); + + while (true) { + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) { + continue; + } + + if (dot_v3v3(thick_faces[f_index].efa->no, project_normal) > project_angle_limit_half_cos) { + BLI_array_append(project_thick_faces, &thick_faces[f_index]); + BM_elem_flag_set(thick_faces[f_index].efa, BM_ELEM_TAG, true); + } + } + + float average_normal[3] = {0.0f, 0.0f, 0.0f}; + + if (area_weight <= 0.0f) { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + add_v3_v3(average_normal, tf->efa->no); + } + } + else if (area_weight >= 1.0f) { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + madd_v3_v3fl(average_normal, tf->efa->no, tf->area); + } + } + else { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + const float area_blend = (tf->area * area_weight) + (1.0f - area_weight); + madd_v3_v3fl(average_normal, tf->efa->no, area_blend); + } + } + + /* Avoid NAN. */ + if (normalize_v3(average_normal) != 0.0f) { + float(*normal)[3] = BLI_array_append_ret(project_normal_array); + copy_v3_v3(*normal, average_normal); + } + + /* Find the most unique angle that points away from other normals. */ + float anble_best = 1.0f; + uint angle_best_index = 0; + + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) { + continue; + } + + float angle_test = -1.0f; + for (int p_index = 0; p_index < BLI_array_len(project_normal_array); p_index++) { + angle_test = max_ff(angle_test, + dot_v3v3(project_normal_array[p_index], thick_faces[f_index].efa->no)); + } + + if (angle_test < anble_best) { + anble_best = angle_test; + angle_best_index = f_index; + } + } + + if (anble_best < project_angle_limit_cos) { + project_normal = thick_faces[angle_best_index].efa->no; + BLI_array_clear(project_thick_faces); + BLI_array_append(project_thick_faces, &thick_faces[angle_best_index]); + BM_elem_flag_enable(thick_faces[angle_best_index].efa, BM_ELEM_TAG); + } + else { + if (BLI_array_len(project_normal_array) >= 1) { + break; + } + } + } + + BLI_array_free(project_thick_faces); + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); + + *r_project_normal_array = project_normal_array; + return BLI_array_len(project_normal_array); +} + +static int smart_project_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + /* May be NULL. */ + View3D *v3d = CTX_wm_view3d(C); + + const float project_angle_limit = RNA_float_get(op->ptr, "angle_limit"); + const float island_margin = RNA_float_get(op->ptr, "island_margin"); + const float area_weight = RNA_float_get(op->ptr, "area_weight"); + + const float project_angle_limit_cos = cosf(project_angle_limit); + const float project_angle_limit_half_cos = cosf(project_angle_limit / 2); + + /* Memory arena for list links (cleared for each object). */ + MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, v3d, &objects_len); + + Object **objects_changed = MEM_mallocN(sizeof(*objects_changed) * objects_len, __func__); + uint object_changed_len = 0; + + BMFace *efa; + BMIter iter; + uint ob_index; + + for (ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + bool changed = false; + + const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + ThickFace *thick_faces = MEM_mallocN(sizeof(*thick_faces) * em->bm->totface, __func__); + + uint thick_faces_len = 0; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } + thick_faces[thick_faces_len].area = BM_face_calc_area(efa); + thick_faces[thick_faces_len].efa = efa; + thick_faces_len++; + } + + qsort(thick_faces, thick_faces_len, sizeof(ThickFace), smart_uv_project_thickface_area_cmp_fn); + + /* Remove all zero area faces. */ + while ((thick_faces_len > 0) && + !(thick_faces[thick_faces_len - 1].area > smart_uv_project_area_ignore)) { + + /* Zero UV's so they don't overlap with other faces being unwrapped. */ + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, thick_faces[thick_faces_len - 1].efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + zero_v2(luv->uv); + changed = true; + } + + thick_faces_len -= 1; + } + + float(*project_normal_array)[3] = NULL; + int project_normals_len = smart_uv_project_calculate_project_normals( + thick_faces, + thick_faces_len, + em->bm, + project_angle_limit_half_cos, + project_angle_limit_cos, + area_weight, + &project_normal_array); + + if (project_normals_len == 0) { + MEM_freeN(thick_faces); + BLI_assert(project_normal_array == NULL); + continue; + } + + /* After finding projection vectors, we find the uv positions. */ + LinkNode **thickface_project_groups = MEM_callocN( + sizeof(*thickface_project_groups) * project_normals_len, __func__); + + BLI_memarena_clear(arena); + + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + const float *f_normal = thick_faces[f_index].efa->no; + + float angle_best = dot_v3v3(f_normal, project_normal_array[0]); + uint angle_best_index = 0; + + for (int p_index = 1; p_index < project_normals_len; p_index++) { + const float angle_test = dot_v3v3(f_normal, project_normal_array[p_index]); + if (angle_test > angle_best) { + angle_best = angle_test; + angle_best_index = p_index; + } + } + + BLI_linklist_prepend_arena( + &thickface_project_groups[angle_best_index], &thick_faces[f_index], arena); + } + + for (int p_index = 0; p_index < project_normals_len; p_index++) { + if (thickface_project_groups[p_index] == NULL) { + continue; + } + + float axis_mat[3][3]; + axis_dominant_v3_to_m3_negate(axis_mat, project_normal_array[p_index]); + + for (LinkNode *list = thickface_project_groups[p_index]; list; list = list->next) { + ThickFace *tf = list->link; + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, tf->efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + mul_v2_m3v3(luv->uv, axis_mat, l->v->co); + } + changed = true; + } + } + + MEM_freeN(thick_faces); + MEM_freeN(project_normal_array); + + /* No need to free the lists in 'thickface_project_groups' values as the 'arena' is used. */ + MEM_freeN(thickface_project_groups); + + if (changed) { + objects_changed[object_changed_len] = objects[ob_index]; + object_changed_len += 1; + } + } + + BLI_memarena_free(arena); + + MEM_freeN(objects); + + /* Pack islands & Stretch to UV bounds */ + if (object_changed_len > 0) { + + scene->toolsettings->uvcalc_margin = island_margin; + const UnwrapOptions options = { + .topology_from_uvs = true, + .only_selected_faces = true, + .only_selected_uvs = false, + .fill_holes = true, + .correct_aspect = false, + }; + + /* Depsgraph refresh functions are called here. */ + uvedit_pack_islands_multi(scene, objects_changed, object_changed_len, &options, true, false); + uv_map_clip_correct_multi(objects_changed, object_changed_len, op); + } + + MEM_freeN(objects_changed); + + return OPERATOR_FINISHED; +} + +void UV_OT_smart_project(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Smart UV Project"; + ot->idname = "UV_OT_smart_project"; + ot->description = "Projection unwraps the selected faces of mesh objects"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = smart_project_exec; + ot->poll = ED_operator_uvmap; + ot->invoke = WM_operator_props_popup_confirm; + + /* properties */ + prop = RNA_def_float_rotation(ot->srna, + "angle_limit", + 0, + NULL, + DEG2RADF(0.0f), + DEG2RADF(90.0f), + "Angle Limit", + "Lower for more projection groups, higher for less distortion", + DEG2RADF(0.0f), + DEG2RADF(89.0f)); + RNA_def_property_float_default(prop, DEG2RADF(66.0f)); + + RNA_def_float(ot->srna, + "island_margin", + 0.0f, + 0.0f, + 1.0f, + "Island Margin", + "Margin to reduce bleed from adjacent islands", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "area_weight", + 0.0f, + 0.0f, + 1.0f, + "Area Weight", + "Weight projections vector by faces with larger areas", + 0.0f, + 1.0f); + + uv_map_clip_correct_properties_ex(ot, false); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Project UV From View Operator * \{ */ diff --git a/source/blender/freestyle/FRS_freestyle.h b/source/blender/freestyle/FRS_freestyle.h index 57b6dc815a3..bc5e9d49bee 100644 --- a/source/blender/freestyle/FRS_freestyle.h +++ b/source/blender/freestyle/FRS_freestyle.h @@ -42,7 +42,7 @@ struct FreestyleGlobals { extern struct FreestyleGlobals g_freestyle; /* Rendering */ -void FRS_initialize(void); +void FRS_init(void); void FRS_set_context(struct bContext *C); int FRS_is_freestyle_enabled(struct ViewLayer *view_layer); void FRS_init_stroke_renderer(struct Render *re); diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index 51ac281e330..2b43e913b3d 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -100,7 +100,7 @@ static bCallbackFuncStore load_post_callback_funcstore = { // Initialization //======================================================= -void FRS_initialize() +void FRS_init() { if (freestyle_is_initialized) { return; diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index 33078e6ba6a..9796dcda964 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -264,7 +264,7 @@ static PyObject *Freestyle_evaluateCurveMappingF(PyObject * /*self*/, PyObject * return NULL; } cumap = (CurveMapping *)py_srna->ptr.data; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); /* disable extrapolation if enabled */ if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE)) { cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; diff --git a/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp b/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp index 2c226e330d7..3c2f022adb6 100644 --- a/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp +++ b/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp @@ -108,7 +108,7 @@ static int FrsMaterial_init(BPy_FrsMaterial *self, PyObject *args, PyObject *kwd self->m = new FrsMaterial(*m); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O&O&O&O&O&fi", diff --git a/source/blender/freestyle/intern/python/BPy_Id.cpp b/source/blender/freestyle/intern/python/BPy_Id.cpp index 4f61ba847f6..eb0eb661e3d 100644 --- a/source/blender/freestyle/intern/python/BPy_Id.cpp +++ b/source/blender/freestyle/intern/python/BPy_Id.cpp @@ -75,7 +75,7 @@ static int Id_init(BPy_Id *self, PyObject *args, PyObject *kwds) if (PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_1, &Id_Type, &brother)) { self->id = new Id(*(((BPy_Id *)brother)->id)); } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "|ii", (char **)kwlist_2, &first, &second)) { self->id = new Id(first, second); } diff --git a/source/blender/freestyle/intern/python/BPy_Operators.cpp b/source/blender/freestyle/intern/python/BPy_Operators.cpp index 510e823ba55..56f95b8ecbb 100644 --- a/source/blender/freestyle/intern/python/BPy_Operators.cpp +++ b/source/blender/freestyle/intern/python/BPy_Operators.cpp @@ -365,8 +365,8 @@ static PyObject *Operators_sequential_split(BPy_Operators * /*self*/, return NULL; } } - else if (PyErr_Clear(), - (f = 0.0f), + else if ((void)PyErr_Clear(), + (void)(f = 0.0f), PyArg_ParseTupleAndKeywords( args, kwds, "O!|f", (char **)kwlist_2, &UnaryPredicate0D_Type, &obj1, &f)) { if (!((BPy_UnaryPredicate0D *)obj1)->up0D) { @@ -484,8 +484,8 @@ static PyObject *Operators_recursive_split(BPy_Operators * /*self*/, return NULL; } } - else if (PyErr_Clear(), - (f = 0.0f), + else if ((void)PyErr_Clear(), + (void)(f = 0.0f), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O!|f", diff --git a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp index 5f5407e82e3..214385f74ad 100644 --- a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp +++ b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp @@ -110,7 +110,7 @@ static int StrokeAttribute_init(BPy_StrokeAttribute *self, PyObject *args, PyObj self->sa = new StrokeAttribute(*(((BPy_StrokeAttribute *)obj1)->sa)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!f", @@ -123,7 +123,7 @@ static int StrokeAttribute_init(BPy_StrokeAttribute *self, PyObject *args, PyObj self->sa = new StrokeAttribute( *(((BPy_StrokeAttribute *)obj1)->sa), *(((BPy_StrokeAttribute *)obj2)->sa), t); } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "ffffff", diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp index f59ae15ae01..2adcae13e6d 100644 --- a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp +++ b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp @@ -88,7 +88,7 @@ static int ViewShape_init(BPy_ViewShape *self, PyObject *args, PyObject *kwds) self->py_ss = ((BPy_ViewShape *)obj)->py_ss; } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &SShape_Type, &obj)) { BPy_SShape *py_ss = (BPy_SShape *)obj; self->vs = new ViewShape(py_ss->ss); diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp index 6af80c4bfd9..81dd79ff270 100644 --- a/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp +++ b/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp @@ -95,7 +95,7 @@ static int CurvePoint_init(BPy_CurvePoint *self, PyObject *args, PyObject *kwds) self->cp = new CurvePoint(*(((BPy_CurvePoint *)obj1)->cp)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!f", @@ -107,7 +107,7 @@ static int CurvePoint_init(BPy_CurvePoint *self, PyObject *args, PyObject *kwds) &t2d)) { self->cp = new CurvePoint(((BPy_SVertex *)obj1)->sv, ((BPy_SVertex *)obj2)->sv, t2d); } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!f", diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp index 71a87c2c01e..c01f1f17000 100644 --- a/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp +++ b/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp @@ -72,7 +72,7 @@ static int SVertex_init(BPy_SVertex *self, PyObject *args, PyObject *kwds) self->sv = new SVertex(*(((BPy_SVertex *)obj)->sv)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "O&O!", (char **)kwlist_2, convert_v3, v, &Id_Type, &obj)) { Vec3r point_3d(v[0], v[1], v[2]); diff --git a/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp index b4c08714af0..519bd72db3b 100644 --- a/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp +++ b/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp @@ -107,7 +107,7 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k self->sv = new StrokeVertex(*(((BPy_StrokeVertex *)obj1)->sv)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!f", @@ -129,7 +129,7 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k } self->sv = new StrokeVertex(sv1, sv2, t3d); } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "O!", (char **)kwlist_3, &CurvePoint_Type, &obj1)) { CurvePoint *cp = ((BPy_CurvePoint *)obj1)->cp; @@ -139,8 +139,8 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k } self->sv = new StrokeVertex(cp); } - else if (PyErr_Clear(), - (obj2 = 0), + else if ((void)PyErr_Clear(), + (void)(obj2 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!", diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp index 8a09e7722ea..187ab94360a 100644 --- a/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp @@ -81,7 +81,7 @@ static int FEdge_init(BPy_FEdge *self, PyObject *args, PyObject *kwds) self->fe = new FEdge(*(((BPy_FEdge *)obj1)->fe)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp index fd434f9c4ef..788dfa78992 100644 --- a/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp @@ -72,7 +72,7 @@ static int FrsCurve_init(BPy_FrsCurve *self, PyObject *args, PyObject *kwds) self->c = new Curve(*(((BPy_FrsCurve *)obj)->c)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &Id_Type, &obj)) { self->c = new Curve(*(((BPy_Id *)obj)->id)); } diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp index 3a25ddb0252..b31efe1f923 100644 --- a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp @@ -157,7 +157,8 @@ static PyObject *Stroke_resample(BPy_Stroke *self, PyObject *args, PyObject *kwd return NULL; } } - else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) { + else if ((void)PyErr_Clear(), + PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) { if (self->s->Resample(f) < 0) { PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex interval) failed"); return NULL; diff --git a/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp b/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp index 285100193d3..9cdc344081e 100644 --- a/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp @@ -71,7 +71,7 @@ static int Chain_init(BPy_Chain *self, PyObject *args, PyObject *kwds) self->c = new Chain(*(((BPy_Chain *)obj)->c)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &Id_Type, &obj)) { self->c = new Chain(*(((BPy_Id *)obj)->id)); } diff --git a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp index 725daa80b5e..5db75c84608 100644 --- a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp @@ -75,7 +75,7 @@ static int FEdgeSharp_init(BPy_FEdgeSharp *self, PyObject *args, PyObject *kwds) self->fes = new FEdgeSharp(*(((BPy_FEdgeSharp *)obj1)->fes)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", diff --git a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp index 65d9dcbe01f..3fb739b18db 100644 --- a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp @@ -73,7 +73,7 @@ static int FEdgeSmooth_init(BPy_FEdgeSmooth *self, PyObject *args, PyObject *kwd self->fes = new FEdgeSmooth(*(((BPy_FEdgeSmooth *)obj1)->fes)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp index 74ae7809284..90e751333b9 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp @@ -82,8 +82,8 @@ static int AdjacencyIterator_init(BPy_AdjacencyIterator *self, PyObject *args, P self->at_start = ((BPy_AdjacencyIterator *)obj1)->at_start; } } - else if (PyErr_Clear(), - (obj2 = obj3 = 0), + else if ((void)PyErr_Clear(), + (void)(obj2 = obj3 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp index 164e1646934..1703fc2bddb 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp @@ -114,8 +114,8 @@ static int ChainPredicateIterator_init(BPy_ChainPredicateIterator *self, Py_INCREF(self->upred); Py_INCREF(self->bpred); } - else if (PyErr_Clear(), - (obj3 = obj4 = obj5 = obj6 = 0), + else if ((void)PyErr_Clear(), + (void)(obj3 = obj4 = obj5 = obj6 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|O!O!O&O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp index 401959be0c0..d8ad82d667c 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp @@ -91,8 +91,8 @@ static int ChainSilhouetteIterator_init(BPy_ChainSilhouetteIterator *self, args, kwds, "O!", (char **)kwlist_1, &ChainSilhouetteIterator_Type, &obj1)) { self->cs_it = new ChainSilhouetteIterator(*(((BPy_ChainSilhouetteIterator *)obj1)->cs_it)); } - else if (PyErr_Clear(), - (obj1 = obj2 = obj3 = 0), + else if ((void)PyErr_Clear(), + (void)(obj1 = obj2 = obj3 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "|O!O&O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp index b6d841c5b64..dbd6e8dd09d 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp @@ -91,8 +91,8 @@ static int ChainingIterator___init__(BPy_ChainingIterator *self, PyObject *args, args, kwds, "O!", (char **)kwlist_1, &ChainingIterator_Type, &obj1)) { self->c_it = new ChainingIterator(*(((BPy_ChainingIterator *)obj1)->c_it)); } - else if (PyErr_Clear(), - (obj1 = obj2 = obj3 = obj4 = 0), + else if ((void)PyErr_Clear(), + (void)(obj1 = obj2 = obj3 = obj4 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "|O!O!O&O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp index 6ea61a060cb..6c496b0308b 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp @@ -75,7 +75,8 @@ static int CurvePointIterator_init(BPy_CurvePointIterator *self, PyObject *args, *(((BPy_CurvePointIterator *)brother)->cp_it)); } } - else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &step)) { + else if ((void)PyErr_Clear(), + PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &step)) { self->cp_it = new CurveInternal::CurvePointIterator(step); } else { diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp index 0dbef9f325c..f0d6acf461b 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp @@ -81,14 +81,14 @@ static int Interface0DIterator_init(BPy_Interface0DIterator *self, PyObject *arg self->at_start = true; self->reversed = false; } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "O!", (char **)kwlist_2, &Interface1D_Type, &inter)) { self->if0D_it = new Interface0DIterator(((BPy_Interface1D *)inter)->if1D->verticesBegin()); self->at_start = true; self->reversed = false; } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "O!", (char **)kwlist_3, &Interface0DIterator_Type, &brother)) { self->if0D_it = new Interface0DIterator(*(((BPy_Interface0DIterator *)brother)->if0D_it)); diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp index dd738b97473..4a5927ff6eb 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp @@ -82,7 +82,7 @@ static int SVertexIterator_init(BPy_SVertexIterator *self, PyObject *args, PyObj self->sv_it = new ViewEdgeInternal::SVertexIterator(*(((BPy_SVertexIterator *)obj1)->sv_it)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O!O!f", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp index 84f57f1fe31..df03ecba96f 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp @@ -74,7 +74,7 @@ static int StrokeVertexIterator_init(BPy_StrokeVertexIterator *self, self->at_start = ((BPy_StrokeVertexIterator *)brother)->at_start; } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "|O!", (char **)kwlist_2, &Stroke_Type, &stroke)) { if (!stroke) { diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp index c8a978784a4..3d0ed5d5a4d 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp @@ -78,8 +78,8 @@ static int ViewEdgeIterator_init(BPy_ViewEdgeIterator *self, PyObject *args, PyO args, kwds, "O!", (char **)kwlist_1, &ViewEdgeIterator_Type, &obj1)) { self->ve_it = new ViewEdgeInternal::ViewEdgeIterator(*(((BPy_ViewEdgeIterator *)obj1)->ve_it)); } - else if (PyErr_Clear(), - (obj1 = obj2 = 0), + else if ((void)PyErr_Clear(), + (void)(obj1 = obj2 = 0), PyArg_ParseTupleAndKeywords( args, kwds, "|O&O!", (char **)kwlist_2, check_begin, &obj1, &PyBool_Type, &obj2)) { ViewEdge *begin = (!obj1 || obj1 == Py_None) ? NULL : ((BPy_ViewEdge *)obj1)->ve; diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 2686275e898..ad29dbe6668 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -57,3 +57,20 @@ set(LIB ) blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_GTESTS) + set(TEST_SRC + tests/FN_array_spans_test.cc + tests/FN_attributes_ref_test.cc + tests/FN_cpp_type_test.cc + tests/FN_generic_vector_array_test.cc + tests/FN_multi_function_network_test.cc + tests/FN_multi_function_test.cc + tests/FN_spans_test.cc + ) + set (TEST_LIB + bf_functions + ) + include(GTestTesting) + blender_add_test_lib(bf_functions_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") +endif() diff --git a/source/blender/functions/FN_array_spans.hh b/source/blender/functions/FN_array_spans.hh index 52325c19c1b..976b9a44d3e 100644 --- a/source/blender/functions/FN_array_spans.hh +++ b/source/blender/functions/FN_array_spans.hh @@ -157,7 +157,7 @@ class GVArraySpan : public VArraySpanBase<void> { this->type_ = &array.type(); this->virtual_size_ = virtual_size; this->category_ = VArraySpanCategory::SingleArray; - this->data_.single_array.start = array.buffer(); + this->data_.single_array.start = array.data(); this->data_.single_array.size = array.size(); } diff --git a/source/blender/functions/FN_attributes_ref.hh b/source/blender/functions/FN_attributes_ref.hh index fe7e59b5e00..0cac8d82d26 100644 --- a/source/blender/functions/FN_attributes_ref.hh +++ b/source/blender/functions/FN_attributes_ref.hh @@ -49,12 +49,12 @@ class AttributesInfoBuilder : NonCopyable, NonMovable { AttributesInfoBuilder() = default; ~AttributesInfoBuilder(); - template<typename T> void add(StringRef name, const T &default_value) + template<typename T> bool add(StringRef name, const T &default_value) { - this->add(name, CPPType::get<T>(), (const void *)&default_value); + return this->add(name, CPPType::get<T>(), (const void *)&default_value); } - void add(StringRef name, const CPPType &type, const void *default_value = nullptr); + bool add(StringRef name, const CPPType &type, const void *default_value = nullptr); }; /** diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 1176a705e66..2f1d3f83c63 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -370,7 +370,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_initialized_n(const void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -379,7 +379,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_initialized_indices(const void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -405,7 +405,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_uninitialized_n(const void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -414,7 +414,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -440,7 +440,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_initialized_n(void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -449,7 +449,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_initialized_indices(void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -475,7 +475,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_uninitialized_n(void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -484,7 +484,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 71b05754717..ba2d1d0edd3 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -49,52 +49,56 @@ class MFParamsBuilder { MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size); - template<typename T> void add_readonly_single_input(const T *value) + template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(GVSpan::FromSingle(CPPType::get<T>(), value, min_array_size_)); + this->add_readonly_single_input(GVSpan::FromSingle(CPPType::get<T>(), value, min_array_size_), + expected_name); } - void add_readonly_single_input(GVSpan ref) + void add_readonly_single_input(GVSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForSingleInput(ref.type())); + this->assert_current_param_type(MFParamType::ForSingleInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); virtual_spans_.append(ref); } - void add_readonly_vector_input(GVArraySpan ref) + void add_readonly_vector_input(GVArraySpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForVectorInput(ref.type())); + this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); virtual_array_spans_.append(ref); } - template<typename T> void add_uninitialized_single_output(T *value) + template<typename T> void add_uninitialized_single_output(T *value, StringRef expected_name = "") { - this->add_uninitialized_single_output(GMutableSpan(CPPType::get<T>(), value, 1)); + this->add_uninitialized_single_output(GMutableSpan(CPPType::get<T>(), value, 1), + expected_name); } - void add_uninitialized_single_output(GMutableSpan ref) + void add_uninitialized_single_output(GMutableSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type())); + this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); mutable_spans_.append(ref); } - void add_vector_output(GVectorArray &vector_array) + void add_vector_output(GVectorArray &vector_array, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type())); + this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type()), + expected_name); BLI_assert(vector_array.size() >= min_array_size_); vector_arrays_.append(&vector_array); } - void add_single_mutable(GMutableSpan ref) + void add_single_mutable(GMutableSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type())); + this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); mutable_spans_.append(ref); } - void add_vector_mutable(GVectorArray &vector_array) + void add_vector_mutable(GVectorArray &vector_array, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type())); + this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type()), + expected_name); BLI_assert(vector_array.size() >= min_array_size_); vector_arrays_.append(&vector_array); } @@ -118,11 +122,17 @@ class MFParamsBuilder { } private: - void assert_current_param_type(MFParamType param_type) + void assert_current_param_type(MFParamType param_type, StringRef expected_name = "") { - UNUSED_VARS_NDEBUG(param_type); + UNUSED_VARS_NDEBUG(param_type, expected_name); #ifdef DEBUG int param_index = this->current_param_index(); + + if (expected_name != "") { + StringRef actual_name = signature_->param_names[param_index]; + BLI_assert(actual_name == expected_name); + } + MFParamType expected_type = signature_->param_types[param_index]; BLI_assert(expected_type == param_type); #endif diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index fd92bb2ff55..ef51ddbaf24 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -31,10 +31,9 @@ namespace blender::fn { struct MFSignature { std::string function_name; - /* Use RawAllocator so that a MultiFunction can have static storage duration. */ - RawVector<std::string> param_names; - RawVector<MFParamType> param_types; - RawVector<int> param_data_indices; + Vector<std::string> param_names; + Vector<MFParamType> param_types; + Vector<int> param_data_indices; bool depends_on_context = false; int data_index(int param_index) const diff --git a/source/blender/functions/FN_spans.hh b/source/blender/functions/FN_spans.hh index 3ceb78ea6bd..76380f46e91 100644 --- a/source/blender/functions/FN_spans.hh +++ b/source/blender/functions/FN_spans.hh @@ -50,12 +50,12 @@ namespace blender::fn { class GSpan { private: const CPPType *type_; - const void *buffer_; + const void *data_; int64_t size_; public: GSpan(const CPPType &type, const void *buffer, int64_t size) - : type_(&type), buffer_(buffer), size_(size) + : type_(&type), data_(buffer), size_(size) { BLI_assert(size >= 0); BLI_assert(buffer != nullptr || size == 0); @@ -86,21 +86,21 @@ class GSpan { return size_; } - const void *buffer() const + const void *data() const { - return buffer_; + return data_; } const void *operator[](int64_t index) const { BLI_assert(index < size_); - return POINTER_OFFSET(buffer_, type_->size() * index); + return POINTER_OFFSET(data_, type_->size() * index); } template<typename T> Span<T> typed() const { BLI_assert(type_->is<T>()); - return Span<T>((const T *)buffer_, size_); + return Span<T>((const T *)data_, size_); } }; @@ -111,12 +111,12 @@ class GSpan { class GMutableSpan { private: const CPPType *type_; - void *buffer_; + void *data_; int64_t size_; public: GMutableSpan(const CPPType &type, void *buffer, int64_t size) - : type_(&type), buffer_(buffer), size_(size) + : type_(&type), data_(buffer), size_(size) { BLI_assert(size >= 0); BLI_assert(buffer != nullptr || size == 0); @@ -135,7 +135,7 @@ class GMutableSpan { operator GSpan() const { - return GSpan(*type_, buffer_, size_); + return GSpan(*type_, data_, size_); } const CPPType &type() const @@ -153,21 +153,21 @@ class GMutableSpan { return size_; } - void *buffer() + void *data() { - return buffer_; + return data_; } void *operator[](int64_t index) { BLI_assert(index < size_); - return POINTER_OFFSET(buffer_, type_->size() * index); + return POINTER_OFFSET(data_, type_->size() * index); } template<typename T> MutableSpan<T> typed() { BLI_assert(type_->is<T>()); - return MutableSpan<T>((T *)buffer_, size_); + return MutableSpan<T>((T *)data_, size_); } }; @@ -208,6 +208,20 @@ template<typename T> struct VSpanBase { return false; } + bool is_full_array() const + { + switch (category_) { + case VSpanCategory::Single: + return virtual_size_ == 1; + case VSpanCategory::FullArray: + return true; + case VSpanCategory::FullPointerArray: + return virtual_size_ <= 1; + } + BLI_assert(false); + return false; + } + bool is_empty() const { return this->virtual_size_ == 0; @@ -284,6 +298,22 @@ template<typename T> class VSpan : public VSpanBase<T> { BLI_assert(false); return *this->data_.single.data; } + + const T &as_single_element() const + { + BLI_assert(this->is_single_element()); + return (*this)[0]; + } + + Span<T> as_full_array() const + { + BLI_assert(this->is_full_array()); + if (this->virtual_size_ == 0) { + return Span<T>(); + } + const T *data = &(*this)[0]; + return Span<T>(data, this->virtual_size_); + } }; /** @@ -310,7 +340,7 @@ class GVSpan : public VSpanBase<void> { this->type_ = &values.type(); this->virtual_size_ = values.size(); this->category_ = VSpanCategory::FullArray; - this->data_.full_array.data = values.buffer(); + this->data_.full_array.data = values.data(); } GVSpan(GMutableSpan values) : GVSpan(GSpan(values)) @@ -394,6 +424,16 @@ class GVSpan : public VSpanBase<void> { return (*this)[0]; } + GSpan as_full_array() const + { + BLI_assert(this->is_full_array()); + if (this->virtual_size_ == 0) { + return GSpan(*this->type_); + } + const void *data = (*this)[0]; + return GSpan(*this->type_, data, this->virtual_size_); + } + void materialize_to_uninitialized(void *dst) const { this->materialize_to_uninitialized(IndexRange(virtual_size_), dst); diff --git a/source/blender/functions/intern/attributes_ref.cc b/source/blender/functions/intern/attributes_ref.cc index 7bfcc69671a..8f7f41be079 100644 --- a/source/blender/functions/intern/attributes_ref.cc +++ b/source/blender/functions/intern/attributes_ref.cc @@ -25,8 +25,12 @@ AttributesInfoBuilder::~AttributesInfoBuilder() } } -void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value) +bool AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value) { + if (name.size() == 0) { + std::cout << "Warning: Tried to add an attribute with empty name.\n"; + return false; + } if (names_.add_as(name)) { types_.append(&type); @@ -36,10 +40,15 @@ void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void void *dst = allocator_.allocate(type.size(), type.alignment()); type.copy_to_uninitialized(default_value, dst); defaults_.append(dst); + return true; } else { - /* The same name can be added more than once as long as the type is always the same. */ - BLI_assert(types_[names_.index_of_as(name)] == &type); + const CPPType &stored_type = *types_[names_.index_of_as(name)]; + if (stored_type != type) { + std::cout << "Warning: Tried to add an attribute twice with different types (" << name + << ": " << stored_type.name() << ", " << type.name() << ").\n"; + } + return false; } } diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc index 06084247e66..c9e8b88ba03 100644 --- a/source/blender/functions/intern/multi_function_builder.cc +++ b/source/blender/functions/intern/multi_function_builder.cc @@ -34,7 +34,7 @@ void CustomMF_GenericConstant::call(IndexMask mask, MFContext UNUSED(context)) const { GMutableSpan output = params.uninitialized_single_output(0); - type_.fill_uninitialized_indices(value_, output.buffer(), mask); + type_.fill_uninitialized_indices(value_, output.data(), mask); } uint64_t CustomMF_GenericConstant::hash() const @@ -111,7 +111,7 @@ void CustomMF_DefaultOutput::call(IndexMask mask, MFParams params, MFContext UNU if (param_type.data_type().is_single()) { GMutableSpan span = params.uninitialized_single_output(param_index); const CPPType &type = span.type(); - type.fill_uninitialized_indices(type.default_value(), span.buffer(), mask); + type.fill_uninitialized_indices(type.default_value(), span.data(), mask); } } } diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index 58577e31c42..25e983d2eeb 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -390,7 +390,7 @@ BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs( case MFDataType::Single: { GVSpan values = storage.get_single_input__full(*socket); GMutableSpan output_values = params.uninitialized_single_output(param_index); - values.materialize_to_uninitialized(storage.mask(), output_values.buffer()); + values.materialize_to_uninitialized(storage.mask(), output_values.data()); break; } case MFDataType::Vector: { @@ -524,11 +524,11 @@ MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage() GMutableSpan span = value->span; const CPPType &type = span.type(); if (value->is_single_allocated) { - type.destruct(span.buffer()); + type.destruct(span.data()); } else { - type.destruct_indices(span.buffer(), mask_); - MEM_freeN(span.buffer()); + type.destruct_indices(span.data(), mask_); + MEM_freeN(span.data()); } } else if (any_value->type == ValueType::OwnVector) { @@ -634,11 +634,11 @@ void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket GMutableSpan span = value->span; const CPPType &type = span.type(); if (value->is_single_allocated) { - type.destruct(span.buffer()); + type.destruct(span.data()); } else { - type.destruct_indices(span.buffer(), mask_); - MEM_freeN(span.buffer()); + type.destruct_indices(span.data(), mask_); + MEM_freeN(span.data()); } value_per_output_id_[origin.id()] = nullptr; } @@ -791,7 +791,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS BLI_assert(to_any_value->type == ValueType::OutputSingle); GMutableSpan span = ((OutputSingleValue *)to_any_value)->span; GVSpan virtual_span = this->get_single_input__full(input); - virtual_span.materialize_to_uninitialized(mask_, span.buffer()); + virtual_span.materialize_to_uninitialized(mask_, span.data()); return span; } @@ -808,7 +808,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS GVSpan virtual_span = this->get_single_input__full(input); void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); GMutableSpan new_array_ref(type, new_buffer, min_array_size_); - virtual_span.materialize_to_uninitialized(mask_, new_array_ref.buffer()); + virtual_span.materialize_to_uninitialized(mask_, new_array_ref.data()); OwnSingleValue *new_value = allocator_.construct<OwnSingleValue>( new_array_ref, to.targets().size(), false); @@ -953,7 +953,7 @@ GVSpan MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &s if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = (OwnSingleValue *)any_value; if (value->is_single_allocated) { - return GVSpan::FromSingle(value->span.type(), value->span.buffer(), min_array_size_); + return GVSpan::FromSingle(value->span.type(), value->span.data(), min_array_size_); } else { return value->span; diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc index f1e047f01a1..af1e77aa355 100644 --- a/source/blender/functions/intern/multi_function_network_optimization.cc +++ b/source/blender/functions/intern/multi_function_network_optimization.cc @@ -28,6 +28,7 @@ #include "BLI_disjoint_set.hh" #include "BLI_ghash.h" #include "BLI_map.hh" +#include "BLI_multi_value_map.hh" #include "BLI_rand.h" #include "BLI_stack.hh" @@ -265,7 +266,7 @@ static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction & case MFDataType::Single: { const CPPType &cpp_type = data_type.single_type(); GMutableSpan array = params.computed_array(param_index); - void *buffer = array.buffer(); + void *buffer = array.data(); resources.add(buffer, array.type().destruct_cb(), AT); constant_fn = &resources.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer); @@ -403,15 +404,15 @@ static Array<uint64_t> compute_node_hashes(MFNetwork &network) return node_hashes; } -static Map<uint64_t, Vector<MFNode *, 1>> group_nodes_by_hash(MFNetwork &network, - Span<uint64_t> node_hashes) +static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network, + Span<uint64_t> node_hashes) { - Map<uint64_t, Vector<MFNode *, 1>> nodes_by_hash; + MultiValueMap<uint64_t, MFNode *> nodes_by_hash; for (int id : IndexRange(network.node_id_amount())) { MFNode *node = network.node_or_null_by_id(id); if (node != nullptr) { uint64_t node_hash = node_hashes[id]; - nodes_by_hash.lookup_or_add_default(node_hash).append(node); + nodes_by_hash.add(node_hash, node); } } return nodes_by_hash; @@ -456,7 +457,7 @@ static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const } static void relink_duplicate_nodes(MFNetwork &network, - Map<uint64_t, Vector<MFNode *, 1>> &nodes_by_hash) + MultiValueMap<uint64_t, MFNode *> &nodes_by_hash) { DisjointSet same_node_cache{network.node_id_amount()}; @@ -494,7 +495,7 @@ static void relink_duplicate_nodes(MFNetwork &network, void common_subnetwork_elimination(MFNetwork &network) { Array<uint64_t> node_hashes = compute_node_hashes(network); - Map<uint64_t, Vector<MFNode *, 1>> nodes_by_hash = group_nodes_by_hash(network, node_hashes); + MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes); relink_duplicate_nodes(network, nodes_by_hash); } diff --git a/tests/gtests/functions/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc index 6b7bb8429ff..9a632b58be8 100644 --- a/tests/gtests/functions/FN_array_spans_test.cc +++ b/source/blender/functions/tests/FN_array_spans_test.cc @@ -7,7 +7,7 @@ #include "BLI_array.hh" -namespace blender::fn { +namespace blender::fn::tests { TEST(virtual_array_span, EmptyConstructor) { @@ -129,4 +129,4 @@ TEST(generic_virtual_array_span, IsSingleArray2) EXPECT_FALSE(converted.is_single_array()); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_attributes_ref_test.cc b/source/blender/functions/tests/FN_attributes_ref_test.cc index 9c7cce83493..3a5e4743c1e 100644 --- a/tests/gtests/functions/FN_attributes_ref_test.cc +++ b/source/blender/functions/tests/FN_attributes_ref_test.cc @@ -5,7 +5,7 @@ #include "testing/testing.h" -namespace blender::fn { +namespace blender::fn::tests { TEST(attributes_info, BuildEmpty) { @@ -68,10 +68,10 @@ TEST(mutable_attributes_ref, ComplexTest) MutableAttributesRef attributes{info, buffers, IndexRange(1, 3)}; EXPECT_EQ(attributes.size(), 3); EXPECT_EQ(attributes.info().size(), 4); - EXPECT_EQ(attributes.get("Position").buffer(), positions.data() + 1); - EXPECT_EQ(attributes.get("ID").buffer(), ids.data() + 1); - EXPECT_EQ(attributes.get("Size").buffer(), sizes.data() + 1); - EXPECT_EQ(attributes.get("Name").buffer(), names.data() + 1); + EXPECT_EQ(attributes.get("Position").data(), positions.data() + 1); + EXPECT_EQ(attributes.get("ID").data(), ids.data() + 1); + EXPECT_EQ(attributes.get("Size").data(), sizes.data() + 1); + EXPECT_EQ(attributes.get("Name").data(), names.data() + 1); EXPECT_EQ(attributes.get("ID").size(), 3); EXPECT_EQ(attributes.get<uint>("ID").size(), 3); @@ -94,4 +94,4 @@ TEST(mutable_attributes_ref, ComplexTest) EXPECT_EQ(ids[2], 100); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_cpp_type_test.cc b/source/blender/functions/tests/FN_cpp_type_test.cc index 85fc1105c25..29368b251cc 100644 --- a/tests/gtests/functions/FN_cpp_type_test.cc +++ b/source/blender/functions/tests/FN_cpp_type_test.cc @@ -4,7 +4,7 @@ #include "FN_cpp_type.hh" -namespace blender::fn { +namespace blender::fn::tests { static const int default_constructed_value = 1; static const int copy_constructed_value = 2; @@ -62,7 +62,7 @@ struct TestType { return stream; } - friend bool operator==(const TestType &a, const TestType &b) + friend bool operator==(const TestType &UNUSED(a), const TestType &UNUSED(b)) { return false; } @@ -73,7 +73,11 @@ struct TestType { } }; -MAKE_CPP_TYPE(TestType, TestType) +} // namespace blender::fn::tests + +MAKE_CPP_TYPE(TestType, blender::fn::tests::TestType) + +namespace blender::fn::tests { const CPPType &CPPType_TestType = CPPType::get<TestType>(); @@ -318,4 +322,4 @@ TEST(cpp_type, DebugPrint) EXPECT_EQ(text, "42"); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_generic_vector_array_test.cc b/source/blender/functions/tests/FN_generic_vector_array_test.cc index cfca678ad27..77ec05f12dc 100644 --- a/tests/gtests/functions/FN_generic_vector_array_test.cc +++ b/source/blender/functions/tests/FN_generic_vector_array_test.cc @@ -4,7 +4,7 @@ #include "testing/testing.h" -namespace blender::fn { +namespace blender::fn::tests { TEST(generic_vector_array, Constructor) { @@ -98,4 +98,4 @@ TEST(generic_vector_array, Extend) EXPECT_EQ(ref[0][0], 3); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc index 9f16b71bb10..f226e0eac2e 100644 --- a/tests/gtests/functions/FN_multi_function_network_test.cc +++ b/source/blender/functions/tests/FN_multi_function_network_test.cc @@ -6,7 +6,8 @@ #include "FN_multi_function_network.hh" #include "FN_multi_function_network_evaluation.hh" -namespace blender::fn { +namespace blender::fn::tests { +namespace { TEST(multi_function_network, Test1) { @@ -250,4 +251,5 @@ TEST(multi_function_network, Test2) } } -} // namespace blender::fn +} // namespace +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 8b5fb060c36..cc023bce597 100644 --- a/tests/gtests/functions/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -5,7 +5,8 @@ #include "FN_multi_function.hh" #include "FN_multi_function_builder.hh" -namespace blender::fn { +namespace blender::fn::tests { +namespace { class AddFunction : public MultiFunction { public: @@ -125,7 +126,7 @@ TEST(multi_function, CreateRangeFunction) { CreateRangeFunction fn; - GVectorArray ranges(CPPType::get<int32_t>(), 5); + GVectorArray ranges(CPPType::get<uint>(), 5); GVectorArrayRef<uint> ranges_ref(ranges); Array<uint> sizes = {3, 0, 6, 1, 4}; @@ -382,4 +383,5 @@ TEST(multi_function, CustomMF_Convert) EXPECT_EQ(outputs[2], 9); } -} // namespace blender::fn +} // namespace +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_spans_test.cc b/source/blender/functions/tests/FN_spans_test.cc index 3172cdc7170..fbcf1fda71e 100644 --- a/tests/gtests/functions/FN_spans_test.cc +++ b/source/blender/functions/tests/FN_spans_test.cc @@ -4,7 +4,7 @@ #include "FN_spans.hh" -namespace blender::fn { +namespace blender::fn::tests { TEST(generic_span, TypeConstructor) { @@ -56,6 +56,7 @@ TEST(virtual_span, EmptyConstructor) EXPECT_EQ(span.size(), 0); EXPECT_TRUE(span.is_empty()); EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); GVSpan converted(span); EXPECT_EQ(converted.type(), CPPType::get<int>()); @@ -73,6 +74,7 @@ TEST(virtual_span, SpanConstructor) EXPECT_EQ(virtual_span[2], 8); EXPECT_EQ(virtual_span[3], 6); EXPECT_FALSE(virtual_span.is_single_element()); + EXPECT_TRUE(virtual_span.is_full_array()); GVSpan converted(span); EXPECT_EQ(converted.type(), CPPType::get<int>()); @@ -93,6 +95,7 @@ TEST(virtual_span, PointerSpanConstructor) EXPECT_EQ(span[2], 6); EXPECT_EQ(&span[1], &x2); EXPECT_FALSE(span.is_single_element()); + EXPECT_FALSE(span.is_full_array()); GVSpan converted(span); EXPECT_EQ(converted.type(), CPPType::get<int>()); @@ -115,6 +118,7 @@ TEST(virtual_span, SingleConstructor) EXPECT_EQ(&span[1], &value); EXPECT_EQ(&span[2], &value); EXPECT_TRUE(span.is_single_element()); + EXPECT_FALSE(span.is_full_array()); GVSpan converted(span); EXPECT_EQ(converted.type(), CPPType::get<int>()); @@ -130,6 +134,7 @@ TEST(generic_virtual_span, TypeConstructor) EXPECT_EQ(span.size(), 0); EXPECT_TRUE(span.is_empty()); EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); VSpan<int> converted = span.typed<int>(); EXPECT_EQ(converted.size(), 0); @@ -146,6 +151,7 @@ TEST(generic_virtual_span, GenericSpanConstructor) EXPECT_EQ(span[2], &values[2]); EXPECT_EQ(span[3], &values[3]); EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); int materialized[4] = {0}; span.materialize_to_uninitialized(materialized); @@ -172,6 +178,7 @@ TEST(generic_virtual_span, SpanConstructor) EXPECT_EQ(span[1], &values[1]); EXPECT_EQ(span[2], &values[2]); EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); int materialized[3] = {0}; span.materialize_to_uninitialized(materialized); @@ -197,6 +204,7 @@ TEST(generic_virtual_span, SingleConstructor) EXPECT_EQ(span[2], &value); EXPECT_TRUE(span.is_single_element()); EXPECT_EQ(span.as_single_element(), &value); + EXPECT_FALSE(span.is_full_array()); int materialized[3] = {0}; span.materialize_to_uninitialized({1, 2}, materialized); @@ -211,4 +219,4 @@ TEST(generic_virtual_span, SingleConstructor) EXPECT_EQ(converted[2], 5); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c index 03137a5cf23..21a8962b131 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c @@ -69,7 +69,7 @@ static void initData(GpencilModifierData *md) gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c index 4761dc878c0..10f775ee340 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c @@ -94,7 +94,7 @@ static void initData(GpencilModifierData *md) gpmd->falloff_type = eGPHook_Falloff_Smooth; gpmd->curfalloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curfalloff) { - BKE_curvemapping_initialize(gpmd->curfalloff); + BKE_curvemapping_init(gpmd->curfalloff); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index 0d8a5f7914e..67c26bf2584 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -79,7 +79,7 @@ static void initData(GpencilModifierData *md) if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; BKE_curvemap_reset(curve->cm, &curve->clipr, CURVE_PRESET_BELL, CURVEMAP_SLOPE_POSITIVE); - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c index 9cc3712e8f4..75f929e979e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -105,19 +105,23 @@ static void deformStroke(GpencilModifierData *md, bGPDspoint *pt = &gps->points[i]; MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; - /* verify vertex group */ + /* Verify vertex group. */ const float weight = get_modifier_point_weight( dvert, (mmd->flag & GP_OFFSET_INVERT_VGROUP) != 0, def_nr); if (weight < 0.0f) { continue; } - /* calculate matrix */ + /* Calculate matrix. */ mul_v3_v3fl(loc, mmd->loc, weight); mul_v3_v3fl(rot, mmd->rot, weight); mul_v3_v3fl(scale, mmd->scale, weight); add_v3_fl(scale, 1.0); loc_eul_size_to_mat4(mat, loc, rot, scale); + /* Apply scale to thickness. */ + float unit_scale = (scale[0] + scale[1] + scale[2]) / 3.0f; + pt->pressure *= unit_scale; + mul_m4_v3(mat, &pt->x); } /* Calc geometry data. */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index 34142709c18..efa58cc4ae0 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -70,7 +70,7 @@ static void initData(GpencilModifierData *md) gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c index 557a305d731..e3511d9645e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c @@ -66,7 +66,7 @@ static void initData(GpencilModifierData *md) gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index 4fa47a592ba..68547614776 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -65,7 +65,7 @@ static void initData(GpencilModifierData *md) gpmd->material = NULL; gpmd->curve_thickness = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_thickness) { - BKE_curvemapping_initialize(gpmd->curve_thickness); + BKE_curvemapping_init(gpmd->curve_thickness); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index da7d33839f1..9d10fcbe49b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -96,7 +96,7 @@ static void initData(GpencilModifierData *md) gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4f90482d16e..3ea18f72166 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -52,38 +52,37 @@ set(INC_SYS ) set(SRC - intern/gpu_attr_binding.c - intern/gpu_batch.c + intern/gpu_attr_binding.cc + intern/gpu_batch.cc intern/gpu_batch_presets.c intern/gpu_batch_utils.c intern/gpu_buffers.c intern/gpu_codegen.c - intern/gpu_context.cpp - intern/gpu_debug.c - intern/gpu_draw.c - intern/gpu_draw_smoke.c - intern/gpu_element.c - intern/gpu_extensions.c - intern/gpu_framebuffer.c - intern/gpu_immediate.c + intern/gpu_context.cc + intern/gpu_debug.cc + intern/gpu_element.cc + intern/gpu_extensions.cc + intern/gpu_framebuffer.cc + intern/gpu_immediate.cc intern/gpu_immediate_util.c intern/gpu_init_exit.c intern/gpu_material.c intern/gpu_material_library.c - intern/gpu_matrix.c + intern/gpu_matrix.cc intern/gpu_node_graph.c - intern/gpu_platform.c + intern/gpu_platform.cc intern/gpu_primitive.c intern/gpu_select.c intern/gpu_select_pick.c intern/gpu_select_sample_query.c - intern/gpu_shader.c - intern/gpu_shader_interface.c - intern/gpu_state.c - intern/gpu_texture.c - intern/gpu_uniformbuffer.c - intern/gpu_vertex_buffer.c - intern/gpu_vertex_format.c + intern/gpu_shader.cc + intern/gpu_shader_builtin.c + intern/gpu_shader_interface.cc + intern/gpu_state.cc + intern/gpu_texture.cc + intern/gpu_uniformbuffer.cc + intern/gpu_vertex_buffer.cc + intern/gpu_vertex_format.cc intern/gpu_viewport.c GPU_attr_binding.h @@ -94,7 +93,6 @@ set(SRC GPU_common.h GPU_context.h GPU_debug.h - GPU_draw.h GPU_element.h GPU_extensions.h GPU_framebuffer.h @@ -216,6 +214,8 @@ data_to_c_simple(shaders/gpu_shader_text_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_keyframe_diamond_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_keyframe_diamond_frag.glsl SRC) +data_to_c_simple(shaders/gpu_shader_codegen_lib.glsl SRC) + data_to_c_simple(shaders/gpu_shader_geometry.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_add_shader.glsl SRC) diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index ca6aaa90ddc..71a29d0b178 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -126,9 +126,8 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo); #define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false) -void GPU_batch_program_set_no_use(GPUBatch *, uint32_t program, const GPUShaderInterface *); -void GPU_batch_program_set(GPUBatch *, uint32_t program, const GPUShaderInterface *); -void GPU_batch_program_set_shader(GPUBatch *, GPUShader *shader); +void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader); +void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader); void GPU_batch_program_set_imm_shader(GPUBatch *batch); void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id); void GPU_batch_program_set_builtin_with_config(GPUBatch *batch, diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h index 282c2437640..be822056678 100644 --- a/source/blender/gpu/GPU_debug.h +++ b/source/blender/gpu/GPU_debug.h @@ -23,8 +23,6 @@ #pragma once -#include "GPU_glew.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h deleted file mode 100644 index c0458fec7c3..00000000000 --- a/source/blender/gpu/GPU_draw.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - */ - -#pragma once - -#include "BLI_utildefines.h" -#include "DNA_object_enums.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct FluidModifierData; -struct ImBuf; -struct Image; -struct ImageUser; -struct Main; - -/* OpenGL drawing functions related to shading. */ - -/* Mipmap settings - * - these will free textures on changes */ - -void GPU_set_mipmap(struct Main *bmain, bool mipmap); -bool GPU_get_mipmap(void); -void GPU_set_linear_mipmap(bool linear); -bool GPU_get_linear_mipmap(void); -void GPU_paint_set_mipmap(struct Main *bmain, bool mipmap); - -/* Anisotropic filtering settings - * - these will free textures on changes */ -void GPU_set_anisotropic(float value); -float GPU_get_anisotropic(void); - -/* Image updates and free - * - these deal with images bound as opengl textures */ - -void GPU_paint_update_image( - struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); -void GPU_create_gl_tex(unsigned int *bind, - unsigned int *rect, - float *frect, - int rectw, - int recth, - int textarget, - bool mipmap, - bool half_float, - bool use_srgb, - struct Image *ima); -void GPU_create_gl_tex_compressed(unsigned int *bind, - int textarget, - struct Image *ima, - struct ImBuf *ibuf); -bool GPU_upload_dxt_texture(struct ImBuf *ibuf, bool use_srgb); -void GPU_free_image(struct Image *ima); -void GPU_free_images(struct Main *bmain); -void GPU_free_images_anim(struct Main *bmain); -void GPU_free_images_old(struct Main *bmain); - -/* gpu_draw_smoke.c */ -void GPU_free_smoke(struct FluidModifierData *fmd); -void GPU_free_smoke_velocity(struct FluidModifierData *fmd); -void GPU_create_smoke(struct FluidModifierData *fmd, int highres); -void GPU_create_smoke_coba_field(struct FluidModifierData *fmd); -void GPU_create_smoke_velocity(struct FluidModifierData *fmd); - -/* Delayed free of OpenGL buffers by main thread */ -void GPU_free_unused_buffers(void); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index 6cb7a297d09..2ce6e458378 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -51,6 +51,8 @@ bool GPU_use_main_context_workaround(void); bool GPU_texture_copy_workaround(void); bool GPU_crappy_amd_driver(void); +int GPU_texture_size_with_limit(int res); + bool GPU_mem_stats_supported(void); void GPU_mem_stats_get(int *totalmem, int *freemem); diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 4958d1eaac8..9dc07fefd4e 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -31,7 +31,7 @@ extern "C" { typedef struct GPUAttachment { struct GPUTexture *tex; - int mip, layer; + int layer, mip; } GPUAttachment; typedef enum eGPUFrameBufferBits { @@ -119,35 +119,35 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *confi #define GPU_ATTACHMENT_NONE \ { \ - .tex = NULL, .layer = -1, .mip = 0, \ + NULL, -1, 0, \ } #define GPU_ATTACHMENT_LEAVE \ { \ - .tex = NULL, .layer = -1, .mip = -1, \ + NULL, -1, -1, \ } #define GPU_ATTACHMENT_TEXTURE(_tex) \ { \ - .tex = _tex, .layer = -1, .mip = 0, \ + _tex, -1, 0, \ } #define GPU_ATTACHMENT_TEXTURE_MIP(_tex, _mip) \ { \ - .tex = _tex, .layer = -1, .mip = _mip, \ + _tex, -1, _mip, \ } #define GPU_ATTACHMENT_TEXTURE_LAYER(_tex, _layer) \ { \ - .tex = _tex, .layer = _layer, .mip = 0, \ + _tex, _layer, 0, \ } #define GPU_ATTACHMENT_TEXTURE_LAYER_MIP(_tex, _layer, _mip) \ { \ - .tex = _tex, .layer = _layer, .mip = _mip, \ + _tex, _layer, _mip, \ } #define GPU_ATTACHMENT_TEXTURE_CUBEFACE(_tex, _face) \ { \ - .tex = _tex, .layer = _face, .mip = 0, \ + _tex, _face, 0, \ } #define GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(_tex, _face, _mip) \ { \ - .tex = _tex, .layer = _face, .mip = _mip, \ + _tex, _face, _mip, \ } /* Framebuffer operations */ @@ -211,7 +211,7 @@ GPUOffScreen *GPU_offscreen_create( void GPU_offscreen_free(GPUOffScreen *ofs); void GPU_offscreen_bind(GPUOffScreen *ofs, bool save); void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore); -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels); +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels); void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y); int GPU_offscreen_width(const GPUOffScreen *ofs); int GPU_offscreen_height(const GPUOffScreen *ofs); diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h index 08bfcb95942..41d4f5d28d3 100644 --- a/source/blender/gpu/GPU_immediate.h +++ b/source/blender/gpu/GPU_immediate.h @@ -41,7 +41,7 @@ extern "C" { GPUVertFormat *immVertexFormat(void); /** Every immBegin must have a program bound first. */ -void immBindProgram(uint32_t program, const GPUShaderInterface *); +void immBindShader(GPUShader *shader); /** Call after your last immEnd, or before binding another program. */ void immUnbindProgram(void); @@ -133,7 +133,7 @@ void immUniformColor3ubvAlpha(const unsigned char rgb[3], unsigned char a); void immUniformColor4ubv(const unsigned char rgba[4]); /** - * Extend #immBindProgram to use Blender’s library of built-in shader programs. + * Extend #immBindShader to use Blender’s library of built-in shader programs. * Use #immUnbindProgram() when done. */ void immBindBuiltinProgram(eGPUBuiltinShader shader_id); diff --git a/source/blender/gpu/GPU_init_exit.h b/source/blender/gpu/GPU_init_exit.h index bd4771e2357..42c56940c4c 100644 --- a/source/blender/gpu/GPU_init_exit.h +++ b/source/blender/gpu/GPU_init_exit.h @@ -31,7 +31,7 @@ extern "C" { void GPU_init(void); void GPU_exit(void); -bool GPU_is_initialized(void); +bool GPU_is_init(void); #ifdef __cplusplus } diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index b2352b3f3b0..b8957ff1819 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -109,6 +109,7 @@ typedef enum eGPUMatFlag { GPU_MATFLAG_GLOSSY = (1 << 1), GPU_MATFLAG_REFRACT = (1 << 2), GPU_MATFLAG_SSS = (1 << 3), + GPU_MATFLAG_BARYCENTRIC = (1 << 4), } eGPUMatFlag; typedef enum eGPUBlendMode { @@ -136,6 +137,13 @@ typedef enum eGPUMaterialStatus { GPU_MAT_SUCCESS, } eGPUMaterialStatus; +typedef void (*GPUMaterialEvalCallbackFn)(GPUMaterial *mat, + int options, + const char **vert_code, + const char **geom_code, + const char **frag_lib, + const char **defines); + GPUNodeLink *GPU_constant(const float *num); GPUNodeLink *GPU_uniform(const float *num); GPUNodeLink *GPU_attribute(GPUMaterial *mat, CustomDataType type, const char *name); @@ -189,7 +197,8 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene, const char *geom_code, const char *frag_lib, const char *defines, - const char *name); + const char *name, + GPUMaterialEvalCallbackFn callback); void GPU_material_compile(GPUMaterial *mat); void GPU_material_free(struct ListBase *gpumaterial); diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h index 0848252c788..a89298c0d01 100644 --- a/source/blender/gpu/GPU_platform.h +++ b/source/blender/gpu/GPU_platform.h @@ -24,10 +24,7 @@ #pragma once #include "BLI_sys_types.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "BLI_utildefines.h" /* GPU platform support */ @@ -42,6 +39,8 @@ typedef enum eGPUDeviceType { GPU_DEVICE_ANY = (0xff), } eGPUDeviceType; +ENUM_OPERATORS(eGPUDeviceType) + typedef enum eGPUOSType { GPU_OS_WIN = (1 << 8), GPU_OS_MAC = (1 << 9), @@ -62,6 +61,10 @@ typedef enum eGPUSupportLevel { GPU_SUPPORT_LEVEL_UNSUPPORTED, } eGPUSupportLevel; +#ifdef __cplusplus +extern "C" { +#endif + bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver); eGPUSupportLevel GPU_platform_support_level(void); const char *GPU_platform_support_level_key(void); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 1ec70c1106b..f782742ae53 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -86,8 +86,6 @@ void GPU_shader_transform_feedback_disable(GPUShader *shader); int GPU_shader_get_program(GPUShader *shader); -void *GPU_shader_get_interface(GPUShader *shader); - void GPU_shader_set_srgb_uniform(const struct GPUShaderInterface *interface); int GPU_shader_get_uniform(GPUShader *shader, const char *name); diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 4cf1d9844ae..4a2c90e241b 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -86,6 +86,7 @@ bool GPU_depth_mask_get(void); void GPU_stencil_mask(uint stencil); void GPU_unpack_row_length_set(uint len); void GPU_clip_distances(int enabled_len); +bool GPU_mipmap_enabled(void); void GPU_flush(void); void GPU_finish(void); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 7cbd4b1eee3..7ee7f8fcdec 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -24,6 +24,7 @@ #pragma once #include "BLI_utildefines.h" + #include "GPU_state.h" struct GPUVertBuf; @@ -42,7 +43,6 @@ typedef struct GPUTexture GPUTexture; * - Internally used by textures. * - All states are created at startup to avoid runtime costs. */ - typedef enum eGPUSamplerState { GPU_SAMPLER_FILTER = (1 << 0), GPU_SAMPLER_MIPMAP = (1 << 1), @@ -56,6 +56,8 @@ typedef enum eGPUSamplerState { GPU_SAMPLER_MAX = (1 << 8), } eGPUSamplerState; +ENUM_OPERATORS(eGPUSamplerState) + #ifdef __cplusplus extern "C" { #endif @@ -120,7 +122,6 @@ typedef enum eGPUTextureFormat { #if 0 GPU_RGB10_A2, GPU_RGB10_A2UI, - GPU_SRGB8_A8, #endif GPU_R11F_G11F_B10F, GPU_DEPTH32F_STENCIL8, @@ -149,7 +150,13 @@ typedef enum eGPUTextureFormat { GPU_R8_SNORM, #endif -/* Special formats texture only */ + /* Special formats texture only */ + GPU_SRGB8_A8_DXT1, + GPU_SRGB8_A8_DXT3, + GPU_SRGB8_A8_DXT5, + GPU_RGBA8_DXT1, + GPU_RGBA8_DXT3, + GPU_RGBA8_DXT5, #if 0 GPU_SRGB8, GPU_RGB9_E5, @@ -222,17 +229,10 @@ GPUTexture *GPU_texture_create_cube_array( GPUTexture *GPU_texture_create_from_vertbuf(struct GPUVertBuf *vert); GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint buffer); -GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode); -GPUTexture *GPU_texture_from_blender(struct Image *ima, - struct ImageUser *iuser, - struct ImBuf *ibuf, - int textarget); +GPUTexture *GPU_texture_create_compressed( + int w, int h, int miplen, eGPUTextureFormat format, const void *data); -/* movie clip drawing */ -GPUTexture *GPU_texture_from_movieclip(struct MovieClip *clip, - struct MovieClipUser *cuser, - int textarget); -void GPU_free_texture_movieclip(struct MovieClip *clip); +GPUTexture *GPU_texture_create_error(int dimension, bool array); void GPU_texture_add_mipmap(GPUTexture *tex, eGPUDataFormat gpu_data_format, @@ -268,6 +268,7 @@ void GPU_texture_unbind_all(void); void GPU_texture_copy(GPUTexture *dst, GPUTexture *src); void GPU_texture_generate_mipmap(GPUTexture *tex); +void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso); void GPU_texture_compare_mode(GPUTexture *tex, bool use_compare); void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter); void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter); diff --git a/source/blender/gpu/GPU_uniformbuffer.h b/source/blender/gpu/GPU_uniformbuffer.h index 56e258d8a48..e2b2a757fb9 100644 --- a/source/blender/gpu/GPU_uniformbuffer.h +++ b/source/blender/gpu/GPU_uniformbuffer.h @@ -41,8 +41,7 @@ void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_); void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number); void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo); - -int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo); +void GPU_uniformbuffer_unbind_all(void); bool GPU_uniformbuffer_is_empty(GPUUniformBuffer *ubo); bool GPU_uniformbuffer_is_dirty(GPUUniformBuffer *ubo); diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index 8f194ed2c36..757255496e0 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -58,10 +58,10 @@ typedef struct GPUVertBuf { /** 0 indicates not yet allocated. */ uint32_t vbo_id; /** Usage hint for GL optimisation. */ - uint usage : 2; + GPUUsageType usage; /** Data has been touched and need to be reuploaded to GPU. */ - uint dirty : 1; - unsigned char *data; /* NULL indicates data in VRAM (unmapped) */ + bool dirty; + uchar *data; /* NULL indicates data in VRAM (unmapped) */ } GPUVertBuf; GPUVertBuf *GPU_vertbuf_create(GPUUsageType); diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index 391eaf61876..59af912ed3d 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -41,7 +41,7 @@ extern "C" { #define GPU_MAX_SAFE_ATTR_NAME 12 typedef enum { - GPU_COMP_I8, + GPU_COMP_I8 = 0, GPU_COMP_U8, GPU_COMP_I16, GPU_COMP_U16, @@ -51,17 +51,21 @@ typedef enum { GPU_COMP_F32, GPU_COMP_I10, + /* Warning! adjust GPUVertAttr if changing. */ } GPUVertCompType; typedef enum { - GPU_FETCH_FLOAT, + GPU_FETCH_FLOAT = 0, GPU_FETCH_INT, GPU_FETCH_INT_TO_FLOAT_UNIT, /* 127 (ubyte) -> 0.5 (and so on for other int types) */ GPU_FETCH_INT_TO_FLOAT, /* 127 (any int type) -> 127.0 */ + /* Warning! adjust GPUVertAttr if changing. */ } GPUVertFetchMode; typedef struct GPUVertAttr { + /* GPUVertFetchMode */ uint fetch_mode : 2; + /* GPUVertCompType */ uint comp_type : 3; /* 1 to 4 or 8 or 12 or 16 */ uint comp_len : 5; @@ -71,8 +75,6 @@ typedef struct GPUVertAttr { uint offset : 11; /* up to GPU_VERT_ATTR_MAX_NAMES */ uint name_len : 3; - uint gl_comp_type; - /* -- 8 Bytes -- */ uchar names[GPU_VERT_ATTR_MAX_NAMES]; } GPUVertAttr; diff --git a/source/blender/gpu/intern/gpu_attr_binding.c b/source/blender/gpu/intern/gpu_attr_binding.cc index 6cb60884620..6cb60884620 100644 --- a/source/blender/gpu/intern/gpu_attr_binding.c +++ b/source/blender/gpu/intern/gpu_attr_binding.cc diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/intern/gpu_attr_binding_private.h index 4f18655ec62..4d359343c38 100644 --- a/source/blender/gpu/intern/gpu_attr_binding_private.h +++ b/source/blender/gpu/intern/gpu_attr_binding_private.h @@ -28,9 +28,18 @@ #include "GPU_shader_interface.h" #include "GPU_vertex_format.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO(fclem) remove, use shaderface directly. */ void AttrBinding_clear(GPUAttrBinding *binding); void get_attr_locations(const GPUVertFormat *format, GPUAttrBinding *binding, const GPUShaderInterface *shaderface); uint read_attr_location(const GPUAttrBinding *binding, uint a_idx); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_batch.c b/source/blender/gpu/intern/gpu_batch.cc index 5f77f13c135..9f9adcacfa6 100644 --- a/source/blender/gpu/intern/gpu_batch.c +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -37,6 +37,7 @@ #include "gpu_context_private.h" #include "gpu_primitive_private.h" #include "gpu_shader_private.h" +#include "gpu_vertex_format_private.h" #include <limits.h> #include <stdlib.h> @@ -89,7 +90,7 @@ GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type, GPUIndexBuf *elem, uint owns_flag) { - GPUBatch *batch = MEM_callocN(sizeof(GPUBatch), "GPUBatch"); + GPUBatch *batch = (GPUBatch *)MEM_callocN(sizeof(GPUBatch), "GPUBatch"); GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag); return batch; } @@ -327,10 +328,10 @@ static GLuint batch_vao_get(GPUBatch *batch) } /* Init dynamic arrays and let the branch below set the values. */ batch->dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = MEM_callocN( + batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN( batch->dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces"); - batch->dynamic_vaos.vao_ids = MEM_callocN(batch->dynamic_vaos.count * sizeof(GLuint), - "dyn vaos ids"); + batch->dynamic_vaos.vao_ids = (GLuint *)MEM_callocN( + batch->dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids"); } } @@ -346,11 +347,11 @@ static GLuint batch_vao_get(GPUBatch *batch) /* Not enough place, realloc the array. */ i = batch->dynamic_vaos.count; batch->dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = MEM_recallocN((void *)batch->dynamic_vaos.interfaces, - sizeof(GPUShaderInterface *) * - batch->dynamic_vaos.count); - batch->dynamic_vaos.vao_ids = MEM_recallocN(batch->dynamic_vaos.vao_ids, - sizeof(GLuint) * batch->dynamic_vaos.count); + batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN( + (void *)batch->dynamic_vaos.interfaces, + sizeof(GPUShaderInterface *) * batch->dynamic_vaos.count); + batch->dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN( + batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count); } batch->dynamic_vaos.interfaces[i] = batch->interface; batch->dynamic_vaos.vao_ids[i] = new_vao = GPU_vao_alloc(); @@ -370,22 +371,20 @@ static GLuint batch_vao_get(GPUBatch *batch) return new_vao; } -void GPU_batch_program_set_no_use(GPUBatch *batch, - uint32_t program, - const GPUShaderInterface *shaderface) +void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader) { #if TRUST_NO_ONE - assert(glIsProgram(program)); + assert(glIsProgram(shader->program)); assert(batch->program_in_use == 0); #endif - batch->interface = shaderface; - batch->program = program; + batch->interface = shader->interface; + batch->program = shader->program; batch->vao_id = batch_vao_get(batch); } -void GPU_batch_program_set(GPUBatch *batch, uint32_t program, const GPUShaderInterface *shaderface) +void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader) { - GPU_batch_program_set_no_use(batch, program, shaderface); + GPU_batch_set_shader_no_bind(batch, shader); GPU_batch_program_use_begin(batch); /* hack! to make Batch_Uniform* simpler */ } @@ -440,6 +439,7 @@ static void create_bindings(GPUVertBuf *verts, } const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride; + const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); @@ -452,19 +452,13 @@ static void create_bindings(GPUVertBuf *verts, *attr_mask &= ~(1 << input->location); if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) { -#if TRUST_NO_ONE - assert(a->fetch_mode == GPU_FETCH_FLOAT); - assert(a->gl_comp_type == GL_FLOAT); -#endif + BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT); + BLI_assert(a->comp_type == GPU_COMP_F32); for (int i = 0; i < a->comp_len / 4; i++) { glEnableVertexAttribArray(input->location + i); glVertexAttribDivisor(input->location + i, (use_instancing) ? 1 : 0); - glVertexAttribPointer(input->location + i, - 4, - a->gl_comp_type, - GL_FALSE, - stride, - (const GLubyte *)pointer + i * 16); + glVertexAttribPointer( + input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16); } } else { @@ -474,15 +468,13 @@ static void create_bindings(GPUVertBuf *verts, switch (a->fetch_mode) { case GPU_FETCH_FLOAT: case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer( - input->location, a->comp_len, a->gl_comp_type, GL_FALSE, stride, pointer); + glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer); break; case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer( - input->location, a->comp_len, a->gl_comp_type, GL_TRUE, stride, pointer); + glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer); break; case GPU_FETCH_INT: - glVertexAttribIPointer(input->location, a->comp_len, a->gl_comp_type, stride, pointer); + glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); break; } } @@ -839,7 +831,7 @@ struct GPUDrawList { GPUDrawList *GPU_draw_list_create(int length) { - GPUDrawList *list = MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); + GPUDrawList *list = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); /* Alloc the biggest possible command list which is indexed. */ list->buffer_size = sizeof(GPUDrawCommandIndexed) * length; if (USE_MULTI_DRAW_INDIRECT) { @@ -848,7 +840,7 @@ GPUDrawList *GPU_draw_list_create(int length) glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW); } else { - list->commands = MEM_mallocN(list->buffer_size, "GPUDrawList data"); + list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data"); } return list; } @@ -880,7 +872,7 @@ void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch) list->cmd_offset = 0; } GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; - list->commands = glMapBufferRange( + list->commands = (GPUDrawCommand *)glMapBufferRange( GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags); } } @@ -989,17 +981,12 @@ void GPU_draw_list_submit(GPUDrawList *list) /** \name Utilities * \{ */ -void GPU_batch_program_set_shader(GPUBatch *batch, GPUShader *shader) -{ - GPU_batch_program_set(batch, shader->program, shader->interface); -} - void GPU_batch_program_set_builtin_with_config(GPUBatch *batch, eGPUBuiltinShader shader_id, eGPUShaderConfig sh_cfg) { GPUShader *shader = GPU_shader_get_builtin_shader_with_config(shader_id, sh_cfg); - GPU_batch_program_set(batch, shader->program, shader->interface); + GPU_batch_set_shader(batch, shader); } void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id) @@ -1012,10 +999,7 @@ void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id) * DO NOT DRAW WITH THE BATCH BEFORE CALLING immUnbindProgram. */ void GPU_batch_program_set_imm_shader(GPUBatch *batch) { - GLuint program; - GPUShaderInterface *interface; - immGetProgram(&program, &interface); - GPU_batch_program_set(batch, program, interface); + GPU_batch_set_shader(batch, immGetShader()); } /** \} */ diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 155179205c5..8947365d666 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -41,7 +41,6 @@ #include "BKE_material.h" #include "GPU_extensions.h" -#include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_uniformbuffer.h" @@ -56,8 +55,8 @@ #include <stdarg.h> #include <string.h> +extern char datatoc_gpu_shader_codegen_lib_glsl[]; extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; /* -------------------- GPUPass Cache ------------------ */ /** @@ -282,18 +281,15 @@ static const char *gpu_builtin_name(eGPUBuiltin builtin) static void codegen_set_unique_ids(GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *input; - GPUOutput *output; int id = 1; - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { /* set id for unique names of uniform variables */ input->id = id++; } - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { /* set id for unique names of tmp variables storing output */ output->id = id++; } @@ -307,17 +303,10 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *input; const char *name; int builtins = 0; ListBase ubo_inputs = {NULL, NULL}; - /* Attributes */ - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - BLI_dynstr_appendf(ds, "in %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); - } - /* Textures */ LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) { if (tex->colorband) { @@ -339,8 +328,9 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, } /* Print other uniforms */ - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_BUILTIN) { /* only define each builtin uniform/varying once */ if (!(builtins & input->builtin)) { @@ -382,8 +372,8 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME); LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) { - input = link->data; - BLI_dynstr_appendf(ds, "\t%s unf%d;\n", gpu_data_type_to_string(input->type), input->id); + GPUInput *input = (GPUInput *)(link->data); + BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id); } BLI_dynstr_append(ds, "};\n"); BLI_freelistN(&ubo_inputs); @@ -396,34 +386,26 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph) { - GPUNode *node; - GPUOutput *output; - - for (node = graph->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { /* declare temporary variables for node output storage */ - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { if (output->type == GPU_CLOSURE) { - BLI_dynstr_appendf(ds, "\tClosure tmp%d;\n", output->id); + BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id); } else { - BLI_dynstr_appendf(ds, "\t%s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); + BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); } } } - BLI_dynstr_append(ds, "\n"); } static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *finaloutput) { - GPUNode *node; - GPUInput *input; - GPUOutput *output; - - for (node = graph->nodes.first; node; node = node->next) { - BLI_dynstr_appendf(ds, "\t%s(", node->name); + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + BLI_dynstr_appendf(ds, " %s(", node->name); - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_TEX) { BLI_dynstr_append(ds, input->texture->sampler_name); } @@ -504,7 +486,7 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f BLI_dynstr_append(ds, ", "); } - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { BLI_dynstr_appendf(ds, "tmp%d", output->id); if (output->next) { BLI_dynstr_append(ds, ", "); @@ -514,21 +496,24 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f BLI_dynstr_append(ds, ");\n"); } - BLI_dynstr_appendf(ds, "\n\treturn tmp%d", finaloutput->id); - BLI_dynstr_append(ds, ";\n"); + BLI_dynstr_appendf(ds, "\n return tmp%d;\n", finaloutput->id); } -static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) +static char *code_generate_fragment(GPUMaterial *material, + GPUNodeGraph *graph, + const char *interface_str) { DynStr *ds = BLI_dynstr_new(); char *code; int builtins; -#if 0 - BLI_dynstr_append(ds, FUNCTION_PROTOTYPES); -#endif - codegen_set_unique_ids(graph); + + /* Attributes, Shader stage interface. */ + if (interface_str) { + BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str); + } + builtins = codegen_process_uniforms_functions(material, ds, graph); if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) { @@ -536,73 +521,61 @@ static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) } if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "in vec2 barycentricTexCo;\n"); - } - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "flat in vec3 barycentricDist;\n"); + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); } BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n"); if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, - "\tvec2 barytexco = vec2((fract(barycentricTexCo.y) != 0.0)\n" - "\t ? barycentricTexCo.x\n" - "\t : 1.0 - barycentricTexCo.x,\n" - "\t 0.0);\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tvec2 barytexco = barycentricTexCo;\n"); - BLI_dynstr_append(ds, "#endif\n"); + BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n"); } /* TODO(fclem) get rid of that. */ if (builtins & GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define viewmat ViewMatrix\n"); + BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n"); } if (builtins & GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, "\t#define camtexfac CameraTexCoFactors\n"); + BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n"); } if (builtins & GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "\t#define objmat ModelMatrix\n"); + BLI_dynstr_append(ds, " #define objmat ModelMatrix\n"); } if (builtins & GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "\t#define objinv ModelMatrixInverse\n"); + BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n"); } if (builtins & GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define viewinv ViewMatrixInverse\n"); + BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n"); } if (builtins & GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define localtoviewmat (ViewMatrix * ModelMatrix)\n"); + BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n"); } if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) { BLI_dynstr_append(ds, - "\t#define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); + " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); } if (builtins & GPU_VIEW_NORMAL) { BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tvec3 n;\n"); - BLI_dynstr_append(ds, "\tworld_normals_get(n);\n"); - BLI_dynstr_append(ds, "\tvec3 facingnormal = transform_direction(ViewMatrix, n);\n"); + BLI_dynstr_append(ds, " vec3 n;\n"); + BLI_dynstr_append(ds, " world_normals_get(n);\n"); + BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n"); BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); + BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); BLI_dynstr_append(ds, "#endif\n"); } if (builtins & GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, "\tvec3 facingwnormal;\n"); + BLI_dynstr_append(ds, " vec3 facingwnormal;\n"); if (builtins & GPU_VIEW_NORMAL) { BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tfacingwnormal = n;\n"); + BLI_dynstr_append(ds, " facingwnormal = n;\n"); BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tworld_normals_get(facingwnormal);\n"); + BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); BLI_dynstr_append(ds, "#endif\n"); } else { - BLI_dynstr_append(ds, "\tworld_normals_get(facingwnormal);\n"); + BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); } } if (builtins & GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, "\t#define viewposition viewPosition\n"); + BLI_dynstr_append(ds, " #define viewposition viewPosition\n"); } codegen_declare_tmps(ds, graph); @@ -610,21 +583,6 @@ static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) BLI_dynstr_append(ds, "}\n"); - /* 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, " - "saturate(1.0 - avg(cl.transmittance)));\n"); - BLI_dynstr_append(ds, "}\n"); - BLI_dynstr_append(ds, "#endif\n\n"); - /* create shader */ code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -659,23 +617,48 @@ static const char *attr_prefix_get(CustomDataType type) } } -static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bool use_geom) +/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */ +static char *code_generate_interface(GPUNodeGraph *graph, int builtins) { + if (BLI_listbase_is_empty(&graph->attributes) && + (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) { + return NULL; + } + DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; - char *code; - int builtins = 0; - /* Hairs uv and col attributes are passed by bufferTextures. */ - BLI_dynstr_append(ds, - "#ifdef HAIR_SHADER\n" - "#define DEFINE_ATTR(type, attr) uniform samplerBuffer attr\n" - "#else\n" - "#define DEFINE_ATTR(type, attr) in type attr\n" - "#endif\n"); + BLI_dynstr_append(ds, "\n"); + + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { + BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + } + if (builtins & GPU_BARYCENTRIC_TEXCO) { + BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n"); + } + if (builtins & GPU_BARYCENTRIC_DIST) { + BLI_dynstr_append(ds, "vec3 barycentricDist;\n"); + } + + char *code = BLI_dynstr_get_cstring(ds); + + BLI_dynstr_free(ds); + return code; +} + +static char *code_generate_vertex(GPUNodeGraph *graph, + const char *interface_str, + const char *vert_code, + int builtins) +{ + DynStr *ds = BLI_dynstr_new(); + + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); + + /* Inputs */ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { + const char *type_str = gpu_data_type_to_string(attr->gputype); + const char *prefix = attr_prefix_get(attr->type); /* XXX FIXME : see notes in mesh_render_data_create() */ /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ if (attr->type == CD_ORCO) { @@ -684,188 +667,58 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } else if (attr->name[0] == '\0') { - BLI_dynstr_appendf(ds, - "DEFINE_ATTR(%s, %s);\n", - gpu_data_type_to_string(attr->gputype), - attr_prefix_get(attr->type)); - BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, attr_prefix_get(attr->type)); + BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix); + BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix); } else { char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - BLI_dynstr_appendf(ds, - "DEFINE_ATTR(%s, %s%s);\n", - gpu_data_type_to_string(attr->gputype), - attr_prefix_get(attr->type), - attr_safe_name); - BLI_dynstr_appendf( - ds, "#define att%d %s%s\n", attr->id, attr_prefix_get(attr->type), attr_safe_name); + BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name); + BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name); } - BLI_dynstr_appendf(ds, - "out %s var%d%s;\n", - gpu_data_type_to_string(attr->gputype), - attr->id, - use_geom ? "g" : ""); - } - - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_appendf(ds, "out vec2 barycentricTexCo%s;\n", use_geom ? "g" : ""); - BLI_dynstr_append(ds, "#endif\n"); } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "out vec3 barycentricPosg;\n"); + /* Outputs interface */ + if (interface_str) { + BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str); } - BLI_dynstr_append(ds, "\n#define USE_ATTR\n"); - - /* Prototype, defined later (this is because of matrices definition). */ - BLI_dynstr_append(ds, "void pass_attr(in vec3 position);\n"); - - BLI_dynstr_append(ds, "\n"); - - if (use_geom) { - /* XXX HACK: Eevee specific. */ - char *vert_new, *vert_new2; - vert_new = BLI_str_replaceN(vert_code, "worldPosition", "worldPositiong"); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "viewPosition", "viewPositiong"); - MEM_freeN(vert_new2); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "worldNormal", "worldNormalg"); - MEM_freeN(vert_new2); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "viewNormal", "viewNormalg"); - MEM_freeN(vert_new2); - - BLI_dynstr_append(ds, vert_new); - - MEM_freeN(vert_new); - } - else { - BLI_dynstr_append(ds, vert_code); - } + /* Prototype. Needed for hair functions. */ + BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n"); + BLI_dynstr_append(ds, "#define USE_ATTR\n\n"); + BLI_dynstr_append(ds, vert_code); BLI_dynstr_append(ds, "\n"); - BLI_dynstr_append(ds, use_geom ? "RESOURCE_ID_VARYING_GEOM\n" : "RESOURCE_ID_VARYING\n"); - - /* Prototype because defined later. */ - BLI_dynstr_append(ds, - "vec2 hair_get_customdata_vec2(const samplerBuffer);\n" - "vec3 hair_get_customdata_vec3(const samplerBuffer);\n" - "vec4 hair_get_customdata_vec4(const samplerBuffer);\n" - "vec3 hair_get_strand_pos(void);\n" - "int hair_get_base_id(void);\n" - "\n"); - - BLI_dynstr_append(ds, "void pass_attr(in vec3 position) {\n"); - - BLI_dynstr_append(ds, use_geom ? "\tPASS_RESOURCE_ID_GEOM\n" : "\tPASS_RESOURCE_ID\n"); - - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - /* To match cycles without breaking into individual segment we encode if we need to invert - * the first component into the second component. We invert if the barycentricTexCo.y - * is NOT 0.0 or 1.0. */ - BLI_dynstr_append(ds, "\tint _base_id = hair_get_base_id();\n"); - BLI_dynstr_appendf( - ds, "\tbarycentricTexCo%s.x = float((_base_id %% 2) == 1);\n", use_geom ? "g" : ""); - BLI_dynstr_appendf( - ds, "\tbarycentricTexCo%s.y = float(((_base_id %% 4) %% 3) > 0);\n", use_geom ? "g" : ""); - } - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "\tbarycentricPosg = position;\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - if (attr->type == CD_TANGENT) { - /* Not supported by hairs */ - BLI_dynstr_appendf(ds, "\tvar%d%s = vec4(0.0);\n", attr->id, use_geom ? "g" : ""); - } - else if (attr->type == CD_ORCO) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = OrcoTexCoFactors[0].xyz + (ModelMatrixInverse * " - "vec4(hair_get_strand_pos(), 1.0)).xyz * OrcoTexCoFactors[1].xyz;\n", - attr->id, - use_geom ? "g" : ""); - /* TODO: fix ORCO with modifiers. */ - } - else { - BLI_dynstr_appendf(ds, - "\tvar%d%s = hair_get_customdata_%s(att%d);\n", - attr->id, - use_geom ? "g" : "", - gpu_data_type_to_string(attr->gputype), - attr->id); - } - } - - BLI_dynstr_append(ds, "#else /* MESH_SHADER */\n"); + BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n"); /* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID * for MESH_SHADER because of indexed drawing. In this case a * geometry shader is needed. */ - + if (builtins & GPU_BARYCENTRIC_TEXCO) { + BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n"); + } if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "\tbarycentricPosg = (ModelMatrix * vec4(position, 1.0)).xyz;\n"); + BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n"); } LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { if (attr->type == CD_TANGENT) { /* silly exception */ - BLI_dynstr_appendf(ds, - "\tvar%d%s.xyz = transpose(mat3(ModelMatrixInverse)) * att%d.xyz;\n", - attr->id, - use_geom ? "g" : "", - attr->id); - BLI_dynstr_appendf(ds, "\tvar%d%s.w = att%d.w;\n", attr->id, use_geom ? "g" : "", attr->id); - /* Normalize only if vector is not null. */ - BLI_dynstr_appendf(ds, - "\tfloat lvar%d = dot(var%d%s.xyz, var%d%s.xyz);\n", - attr->id, - attr->id, - use_geom ? "g" : "", - attr->id, - use_geom ? "g" : ""); - BLI_dynstr_appendf(ds, - "\tvar%d%s.xyz *= (lvar%d > 0.0) ? inversesqrt(lvar%d) : 1.0;\n", - attr->id, - use_geom ? "g" : "", - attr->id, - attr->id); + BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id); } else if (attr->type == CD_ORCO) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = OrcoTexCoFactors[0].xyz + position *" - " OrcoTexCoFactors[1].xyz;\n", - attr->id, - use_geom ? "g" : ""); - /* See mesh_create_loop_orco() for explanation. */ - BLI_dynstr_appendf(ds, - "\tif (orco.w == 0.0) { var%d%s = orco.xyz * 0.5 + 0.5; }\n", - attr->id, - use_geom ? "g" : ""); + BLI_dynstr_appendf( + ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id); } else { - BLI_dynstr_appendf(ds, "\tvar%d%s = att%d;\n", attr->id, use_geom ? "g" : "", attr->id); + const char *type_str = gpu_data_type_to_string(attr->gputype); + BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id); } } - BLI_dynstr_append(ds, "#endif /* HAIR_SHADER */\n"); BLI_dynstr_append(ds, "}\n"); - code = BLI_dynstr_get_cstring(ds); + char *code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -879,146 +732,46 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo } static char *code_generate_geometry(GPUNodeGraph *graph, + const char *interface_str, const char *geom_code, - const char *defines) + int builtins) { - DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; - char *code; - int builtins = 0; - - /* XXX we should not make specific eevee cases here. */ - bool is_hair_shader = (strstr(defines, "HAIR_SHADER") != NULL); - - /* Create prototype because attributes cannot be declared before layout. */ - BLI_dynstr_append(ds, "void pass_attr(in int vert);\n"); - BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2);\n"); - BLI_dynstr_append(ds, "#define USE_ATTR\n"); - - /* Generate varying declarations. */ - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - BLI_dynstr_appendf(ds, "in %s var%dg[];\n", gpu_data_type_to_string(attr->gputype), attr->id); - BLI_dynstr_appendf(ds, "out %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + if (!geom_code) { + return NULL; } - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "in vec2 barycentricTexCog[];\n"); - BLI_dynstr_append(ds, "#endif\n"); - - BLI_dynstr_append(ds, "out vec2 barycentricTexCo;\n"); - } + DynStr *ds = BLI_dynstr_new(); - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "in vec3 barycentricPosg[];\n"); - BLI_dynstr_append(ds, "flat out vec3 barycentricDist;\n"); + /* Attributes, Shader interface; */ + if (interface_str) { + BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str); + BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str); } - if (geom_code == NULL) { - /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used. - * Note: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */ - if ((builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0 || is_hair_shader) { - /* Early out */ - BLI_dynstr_free(ds); - return NULL; - } - else { - /* Force geom shader usage */ - /* TODO put in external file. */ - BLI_dynstr_append(ds, "layout(triangles) in;\n"); - BLI_dynstr_append(ds, "layout(triangle_strip, max_vertices=3) out;\n"); - - BLI_dynstr_append(ds, "in vec3 worldPositiong[];\n"); - BLI_dynstr_append(ds, "in vec3 viewPositiong[];\n"); - BLI_dynstr_append(ds, "in vec3 worldNormalg[];\n"); - BLI_dynstr_append(ds, "in vec3 viewNormalg[];\n"); - - BLI_dynstr_append(ds, "out vec3 worldPosition;\n"); - BLI_dynstr_append(ds, "out vec3 viewPosition;\n"); - BLI_dynstr_append(ds, "out vec3 worldNormal;\n"); - BLI_dynstr_append(ds, "out vec3 viewNormal;\n"); - - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - - BLI_dynstr_append(ds, "void main(){\n"); - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, - "\tcalc_barycentric_distances(barycentricPosg[0], barycentricPosg[1], " - "barycentricPosg[2]);\n"); - } - - for (int i = 0; i < 3; i++) { - BLI_dynstr_appendf(ds, "\tgl_Position = gl_in[%d].gl_Position;\n", i); - BLI_dynstr_appendf(ds, "\tgl_ClipDistance[0] = gl_in[%d].gl_ClipDistance[0];\n", i); - BLI_dynstr_appendf(ds, "\tpass_attr(%d);\n", i); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); - } - BLI_dynstr_append(ds, "}\n"); - } - } - else { - BLI_dynstr_append(ds, geom_code); - } + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2) {\n"); - BLI_dynstr_append(ds, "\tvec3 edge21 = pos2 - pos1;\n"); - BLI_dynstr_append(ds, "\tvec3 edge10 = pos1 - pos0;\n"); - BLI_dynstr_append(ds, "\tvec3 edge02 = pos0 - pos2;\n"); - BLI_dynstr_append(ds, "\tvec3 d21 = normalize(edge21);\n"); - BLI_dynstr_append(ds, "\tvec3 d10 = normalize(edge10);\n"); - BLI_dynstr_append(ds, "\tvec3 d02 = normalize(edge02);\n"); - - BLI_dynstr_append(ds, "\tfloat d = dot(d21, edge02);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.x = sqrt(dot(edge02, edge02) - d * d);\n"); - BLI_dynstr_append(ds, "\td = dot(d02, edge10);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.y = sqrt(dot(edge10, edge10) - d * d);\n"); - BLI_dynstr_append(ds, "\td = dot(d10, edge21);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.z = sqrt(dot(edge21, edge21) - d * d);\n"); - BLI_dynstr_append(ds, "}\n"); + /* geom_code should do something with this, but may not. */ + BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n"); } - BLI_dynstr_append(ds, "RESOURCE_ID_VARYING\n"); - /* Generate varying assignments. */ - BLI_dynstr_append(ds, "void pass_attr(in int vert) {\n"); - - BLI_dynstr_append(ds, "\tPASS_RESOURCE_ID(vert)\n"); - - /* XXX HACK: Eevee specific. */ - if (geom_code == NULL) { - BLI_dynstr_append(ds, "\tworldPosition = worldPositiong[vert];\n"); - BLI_dynstr_append(ds, "\tviewPosition = viewPositiong[vert];\n"); - BLI_dynstr_append(ds, "\tworldNormal = worldNormalg[vert];\n"); - BLI_dynstr_append(ds, "\tviewNormal = viewNormalg[vert];\n"); - } + BLI_dynstr_append(ds, "#define USE_ATTR\n"); + BLI_dynstr_append(ds, "void pass_attr(const int vert) {\n"); if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo = barycentricTexCog[vert];\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo.x = float((vert % 3) == 0);\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo.y = float((vert % 3) == 1);\n"); - BLI_dynstr_append(ds, "#endif\n"); + BLI_dynstr_append(ds, " dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\n"); } LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { /* TODO let shader choose what to do depending on what the attribute is. */ - BLI_dynstr_appendf(ds, "\tvar%d = var%dg[vert];\n", attr->id, attr->id); + BLI_dynstr_appendf(ds, " dataAttrOut.var%d = dataAttrIn[vert].var%d;\n", attr->id, attr->id); } - BLI_dynstr_append(ds, "}\n"); + BLI_dynstr_append(ds, "}\n\n"); - code = BLI_dynstr_get_cstring(ds); + BLI_dynstr_append(ds, geom_code); + + char *code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); return code; @@ -1048,8 +801,17 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, * generated VBOs are ready to accept the future shader. */ gpu_node_graph_prune_unused(graph); + int builtins = 0; + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + if (input->source == GPU_SOURCE_BUILTIN) { + builtins |= input->builtin; + } + } + } /* generate code */ - char *fragmentgen = code_generate_fragment(material, graph); + char *interface_str = code_generate_interface(graph, builtins); + char *fragmentgen = code_generate_fragment(material, graph, interface_str); /* Cache lookup: Reuse shaders already compiled */ uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes); @@ -1057,6 +819,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) { /* No collision, just return the pass. */ + MEM_SAFE_FREE(interface_str); MEM_freeN(fragmentgen); if (!gpu_pass_is_valid(pass_hash)) { /* Shader has already been created but failed to compile. */ @@ -1071,10 +834,11 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, GSet *used_libraries = gpu_material_used_libraries(material); char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib); - char *geometrycode = code_generate_geometry(graph, geom_code, defines); - char *vertexcode = code_generate_vertex(graph, vert_code, (geometrycode != NULL)); + char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins); + char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins); char *fragmentcode = BLI_strdupcat(tmp, fragmentgen); + MEM_SAFE_FREE(interface_str); MEM_freeN(fragmentgen); MEM_freeN(tmp); diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 1454edeb1e0..5d130d75d2c 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -25,6 +25,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct GPUMaterial; struct GPUNodeGraph; struct GPUOutput; @@ -61,3 +65,7 @@ void GPU_pass_release(GPUPass *pass); void gpu_codegen_init(void); void gpu_codegen_exit(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_context.cpp b/source/blender/gpu/intern/gpu_context.cc index e6356580ea3..e6356580ea3 100644 --- a/source/blender/gpu/intern/gpu_context.cpp +++ b/source/blender/gpu/intern/gpu_context.cc diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.cc index f7d6236071d..f7d6236071d 100644 --- a/source/blender/gpu/intern/gpu_debug.c +++ b/source/blender/gpu/intern/gpu_debug.cc diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c deleted file mode 100644 index e5f49555265..00000000000 --- a/source/blender/gpu/intern/gpu_draw.c +++ /dev/null @@ -1,1469 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * Utility functions for dealing with OpenGL texture & material context, - * mipmap generation and light objects. - * - * These are some obscure rendering functions shared between the game engine (not anymore) - * and the blender, in this module to avoid duplication - * and abstract them away from the rest a bit. - */ - -#include <string.h> - -#include "BLI_blenlib.h" -#include "BLI_boxpack_2d.h" -#include "BLI_linklist.h" -#include "BLI_math.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" - -#include "DNA_image_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_userdef_types.h" - -#include "MEM_guardedalloc.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_main.h" -#include "BKE_movieclip.h" - -#include "GPU_draw.h" -#include "GPU_extensions.h" -#include "GPU_glew.h" -#include "GPU_matrix.h" -#include "GPU_platform.h" -#include "GPU_texture.h" - -#include "PIL_time.h" - -static void gpu_free_image(Image *ima, const bool immediate); -static void gpu_free_unused_buffers(void); - -//* Checking powers of two for images since OpenGL ES requires it */ -#ifdef WITH_DDS -static bool is_power_of_2_resolution(int w, int h) -{ - return is_power_of_2_i(w) && is_power_of_2_i(h); -} -#endif - -static bool is_over_resolution_limit(GLenum textarget, int w, int h) -{ - int size = (textarget == GL_TEXTURE_CUBE_MAP) ? GPU_max_cube_map_size() : GPU_max_texture_size(); - int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; - - return (w > reslimit || h > reslimit); -} - -static int smaller_power_of_2_limit(int num) -{ - int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, GPU_max_texture_size()) : - GPU_max_texture_size(); - /* take texture clamping into account */ - if (num > reslimit) { - return reslimit; - } - - return power_of_2_min_i(num); -} - -/* Current OpenGL state caching for GPU_set_tpage */ - -static struct GPUTextureState { - /* also controls min/mag filtering */ - bool domipmap; - /* only use when 'domipmap' is set */ - bool linearmipmap; - /* store this so that new images created while texture painting won't be set to mipmapped */ - bool texpaint; - - float anisotropic; -} GTS = {1, 0, 0, 1.0f}; - -/* Mipmap settings */ - -void GPU_set_mipmap(Main *bmain, bool mipmap) -{ - if (GTS.domipmap != mipmap) { - GPU_free_images(bmain); - GTS.domipmap = mipmap; - } -} - -void GPU_set_linear_mipmap(bool linear) -{ - if (GTS.linearmipmap != linear) { - GTS.linearmipmap = linear; - } -} - -bool GPU_get_mipmap(void) -{ - return GTS.domipmap && !GTS.texpaint; -} - -bool GPU_get_linear_mipmap(void) -{ - return GTS.linearmipmap; -} - -static GLenum gpu_get_mipmap_filter(bool mag) -{ - /* linearmipmap is off by default *when mipmapping is off, - * use unfiltered display */ - if (mag) { - if (GTS.domipmap) { - return GL_LINEAR; - } - else { - return GL_NEAREST; - } - } - else { - if (GTS.domipmap) { - if (GTS.linearmipmap) { - return GL_LINEAR_MIPMAP_LINEAR; - } - else { - return GL_LINEAR_MIPMAP_NEAREST; - } - } - else { - return GL_NEAREST; - } - } -} - -/* Anisotropic filtering settings */ -void GPU_set_anisotropic(float value) -{ - if (GTS.anisotropic != value) { - GPU_samplers_free(); - - /* Clamp value to the maximum value the graphics card supports */ - const float max = GPU_max_texture_anisotropy(); - if (value > max) { - value = max; - } - - GTS.anisotropic = value; - - GPU_samplers_init(); - } -} - -float GPU_get_anisotropic(void) -{ - return GTS.anisotropic; -} - -/* Set OpenGL state for an MTFace */ - -static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget, const int multiview_eye) -{ - if (textarget == GL_TEXTURE_2D) { - return &(ima->gputexture[TEXTARGET_TEXTURE_2D][multiview_eye]); - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &(ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP][multiview_eye]); - } - else if (textarget == GL_TEXTURE_2D_ARRAY) { - return &(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]); - } - else if (textarget == GL_TEXTURE_1D_ARRAY) { - return &(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][multiview_eye]); - } - - return NULL; -} - -static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) -{ - GPUTexture *tilearray = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]; - - if (tilearray == NULL) { - return 0; - } - - float array_w = GPU_texture_width(tilearray); - float array_h = GPU_texture_height(tilearray); - - ImageTile *last_tile = ima->tiles.last; - /* Tiles are sorted by number. */ - int max_tile = last_tile->tile_number - 1001; - - /* create image */ - int bindcode; - glGenTextures(1, (GLuint *)&bindcode); - glBindTexture(GL_TEXTURE_1D_ARRAY, bindcode); - - int width = max_tile + 1; - float *data = MEM_callocN(width * 8 * sizeof(float), __func__); - for (int i = 0; i < width; i++) { - data[4 * i] = -1.0f; - } - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int i = tile->tile_number - 1001; - data[4 * i] = tile->runtime.tilearray_layer; - - float *tile_info = &data[4 * width + 4 * i]; - tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; - tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; - tile_info[2] = tile->runtime.tilearray_size[0] / array_w; - tile_info[3] = tile->runtime.tilearray_size[1] / array_h; - } - - glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA32F, width, 2, 0, GL_RGBA, GL_FLOAT, data); - MEM_freeN(data); - - glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glBindTexture(GL_TEXTURE_1D_ARRAY, 0); - - return bindcode; -} - -typedef struct PackTile { - FixedSizeBoxPack boxpack; - ImageTile *tile; - float pack_score; -} PackTile; - -static int compare_packtile(const void *a, const void *b) -{ - const PackTile *tile_a = a; - const PackTile *tile_b = b; - - return tile_a->pack_score < tile_b->pack_score; -} - -static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) -{ - int arraywidth = 0, arrayheight = 0; - - ListBase boxes = {NULL}; - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - ImageUser iuser; - BKE_imageuser_default(&iuser); - iuser.tile = tile->tile_number; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - - if (ibuf) { - PackTile *packtile = MEM_callocN(sizeof(PackTile), __func__); - packtile->tile = tile; - packtile->boxpack.w = ibuf->x; - packtile->boxpack.h = ibuf->y; - - if (is_over_resolution_limit( - GL_TEXTURE_2D_ARRAY, packtile->boxpack.w, packtile->boxpack.h)) { - packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); - packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); - } - arraywidth = max_ii(arraywidth, packtile->boxpack.w); - arrayheight = max_ii(arrayheight, packtile->boxpack.h); - - /* We sort the tiles by decreasing size, with an additional penalty term - * for high aspect ratios. This improves packing efficiency. */ - float w = packtile->boxpack.w, h = packtile->boxpack.h; - packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h; - - BKE_image_release_ibuf(ima, ibuf, NULL); - BLI_addtail(&boxes, packtile); - } - } - - BLI_assert(arraywidth > 0 && arrayheight > 0); - - BLI_listbase_sort(&boxes, compare_packtile); - int arraylayers = 0; - /* Keep adding layers until all tiles are packed. */ - while (boxes.first != NULL) { - ListBase packed = {NULL}; - BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed); - BLI_assert(packed.first != NULL); - - LISTBASE_FOREACH (PackTile *, packtile, &packed) { - ImageTile *tile = packtile->tile; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - - tileoffset[0] = packtile->boxpack.x; - tileoffset[1] = packtile->boxpack.y; - tilesize[0] = packtile->boxpack.w; - tilesize[1] = packtile->boxpack.h; - tile->runtime.tilearray_layer = arraylayers; - } - - BLI_freelistN(&packed); - arraylayers++; - } - - /* create image */ - int bindcode; - glGenTextures(1, (GLuint *)&bindcode); - glBindTexture(GL_TEXTURE_2D_ARRAY, bindcode); - - GLenum data_type, internal_format; - if (main_ibuf->rect_float) { - data_type = GL_FLOAT; - internal_format = (!(main_ibuf->flags & IB_halffloat) && (ima->flag & IMA_HIGH_BITDEPTH)) ? - GL_RGBA32F : - GL_RGBA16F; - } - else { - data_type = GL_UNSIGNED_BYTE; - internal_format = GL_RGBA8; - if (!IMB_colormanagement_space_is_data(main_ibuf->rect_colorspace) && - !IMB_colormanagement_space_is_scene_linear(main_ibuf->rect_colorspace)) { - internal_format = GL_SRGB8_ALPHA8; - } - } - - glTexImage3D(GL_TEXTURE_2D_ARRAY, - 0, - internal_format, - arraywidth, - arrayheight, - arraylayers, - 0, - GL_RGBA, - data_type, - NULL); - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int tilelayer = tile->runtime.tilearray_layer; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - - if (tilesize[0] == 0 || tilesize[1] == 0) { - continue; - } - - ImageUser iuser; - BKE_imageuser_default(&iuser); - iuser.tile = tile->tile_number; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - - if (ibuf) { - bool needs_scale = (ibuf->x != tilesize[0] || ibuf->y != tilesize[1]); - - ImBuf *scale_ibuf = NULL; - if (ibuf->rect_float) { - float *rect_float = ibuf->rect_float; - - const bool store_premultiplied = ima->alpha_mode != IMA_ALPHA_STRAIGHT; - if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); - IMB_colormanagement_imbuf_to_float_texture( - rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); - } - - float *pixeldata = rect_float; - if (needs_scale) { - scale_ibuf = IMB_allocFromBuffer(NULL, rect_float, ibuf->x, ibuf->y, 4); - IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); - pixeldata = scale_ibuf->rect_float; - } - - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - tileoffset[0], - tileoffset[1], - tilelayer, - tilesize[0], - tilesize[1], - 1, - GL_RGBA, - GL_FLOAT, - pixeldata); - - if (rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - } - else { - unsigned int *rect = ibuf->rect; - - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); - IMB_colormanagement_imbuf_to_byte_texture((uchar *)rect, - 0, - 0, - ibuf->x, - ibuf->y, - ibuf, - internal_format == GL_SRGB8_ALPHA8, - ima->alpha_mode == IMA_ALPHA_PREMUL); - } - - unsigned int *pixeldata = rect; - if (needs_scale) { - scale_ibuf = IMB_allocFromBuffer(rect, NULL, ibuf->x, ibuf->y, 4); - IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); - pixeldata = scale_ibuf->rect; - } - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - tileoffset[0], - tileoffset[1], - tilelayer, - tilesize[0], - tilesize[1], - 1, - GL_RGBA, - GL_UNSIGNED_BYTE, - pixeldata); - - if (rect != ibuf->rect) { - MEM_freeN(rect); - } - } - if (scale_ibuf != NULL) { - IMB_freeImBuf(scale_ibuf); - } - } - - BKE_image_release_ibuf(ima, ibuf, NULL); - } - - if (GPU_get_mipmap()) { - glGenerateMipmap(GL_TEXTURE_2D_ARRAY); - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - - return bindcode; -} - -static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) -{ - uint bindcode = 0; - const bool mipmap = GPU_get_mipmap(); - const bool half_float = (ibuf->flags & IB_halffloat) != 0; - -#ifdef WITH_DDS - if (ibuf->ftype == IMB_FTYPE_DDS) { - /* DDS is loaded directly in compressed form. */ - GPU_create_gl_tex_compressed(&bindcode, textarget, ima, ibuf); - return bindcode; - } -#endif - - /* Regular uncompressed texture. */ - float *rect_float = ibuf->rect_float; - uchar *rect = (uchar *)ibuf->rect; - bool compress_as_srgb = false; - - if (rect_float == NULL) { - /* Byte image is in original colorspace from the file. If the file is sRGB - * scene linear, or non-color data no conversion is needed. Otherwise we - * compress as scene linear + sRGB transfer function to avoid precision loss - * in common cases. - * - * We must also convert to premultiplied for correct texture interpolation - * and consistency with float images. */ - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace); - - rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); - if (rect == NULL) { - return bindcode; - } - - /* Texture storage of images is defined by the alpha mode of the image. The - * downside of this is that there can be artifacts near alpha edges. However, - * this allows us to use sRGB texture formats and preserves color values in - * zero alpha areas, and appears generally closer to what game engines that we - * want to be compatible with do. */ - const bool store_premultiplied = ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true; - IMB_colormanagement_imbuf_to_byte_texture( - rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); - } - } - else { - /* Float image is already in scene linear colorspace or non-color data by - * convention, no colorspace conversion needed. But we do require 4 channels - * currently. */ - const bool store_premultiplied = ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false; - - if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); - if (rect_float == NULL) { - return bindcode; - } - IMB_colormanagement_imbuf_to_float_texture( - rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); - } - } - - /* Create OpenGL texture. */ - GPU_create_gl_tex(&bindcode, - (uint *)rect, - rect_float, - ibuf->x, - ibuf->y, - textarget, - mipmap, - half_float, - compress_as_srgb, - ima); - - /* Free buffers if needed. */ - if (rect && rect != (uchar *)ibuf->rect) { - MEM_freeN(rect); - } - if (rect_float && rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - - return bindcode; -} - -static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip, - MovieClipUser *cuser, - GLenum textarget) -{ - MovieClip_RuntimeGPUTexture *tex; - for (tex = clip->runtime.gputextures.first; tex; tex = tex->next) { - if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) { - break; - } - } - - if (tex == NULL) { - tex = MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), __func__); - - for (int i = 0; i < TEXTARGET_COUNT; i++) { - tex->gputexture[i] = NULL; - } - - memcpy(&tex->user, cuser, sizeof(MovieClipUser)); - BLI_addtail(&clip->runtime.gputextures, tex); - } - - if (textarget == GL_TEXTURE_2D) { - return &tex->gputexture[TEXTARGET_TEXTURE_2D]; - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &tex->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; - } - - return NULL; -} - -static ImBuf *update_do_scale(uchar *rect, - float *rect_float, - int *x, - int *y, - int *w, - int *h, - int limit_w, - int limit_h, - int full_w, - int full_h) -{ - /* Partial update with scaling. */ - float xratio = limit_w / (float)full_w; - float yratio = limit_h / (float)full_h; - - int part_w = *w, part_h = *h; - - /* Find sub coordinates in scaled image. Take ceiling because we will be - * losing 1 pixel due to rounding errors in x,y. */ - *x *= xratio; - *y *= yratio; - *w = (int)ceil(xratio * (*w)); - *h = (int)ceil(yratio * (*h)); - - /* ...but take back if we are over the limit! */ - if (*x + *w > limit_w) { - (*w)--; - } - if (*y + *h > limit_h) { - (*h)--; - } - - /* Scale pixels. */ - ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4); - IMB_scaleImBuf(ibuf, *w, *h); - - return ibuf; -} - -static void gpu_texture_update_scaled_array(uchar *rect, - float *rect_float, - int full_w, - int full_h, - int x, - int y, - int layer, - const int *tile_offset, - const int *tile_size, - int w, - int h) -{ - ImBuf *ibuf = update_do_scale( - rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h); - - /* Shift to account for tile packing. */ - x += tile_offset[0]; - y += tile_offset[1]; - - if (ibuf->rect_float) { - glTexSubImage3D( - GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_FLOAT, ibuf->rect_float); - } - else { - glTexSubImage3D( - GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); - } - - IMB_freeImBuf(ibuf); -} - -static void gpu_texture_update_scaled( - uchar *rect, float *rect_float, int full_w, int full_h, int x, int y, int w, int h) -{ - /* Partial update with scaling. */ - int limit_w = smaller_power_of_2_limit(full_w); - int limit_h = smaller_power_of_2_limit(full_h); - - ImBuf *ibuf = update_do_scale( - rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h); - - if (ibuf->rect_float) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, ibuf->rect_float); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); - } - - IMB_freeImBuf(ibuf); -} - -static void gpu_texture_update_unscaled(uchar *rect, - float *rect_float, - int x, - int y, - int layer, - int w, - int h, - GLint tex_stride, - GLint tex_offset) -{ - /* Partial update without scaling. Stride and offset are used to copy only a - * subset of a possible larger buffer than what we are updating. */ - GPU_unpack_row_length_set(tex_stride); - - if (layer >= 0) { - if (rect_float == NULL) { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - x, - y, - layer, - w, - h, - 1, - GL_RGBA, - GL_UNSIGNED_BYTE, - rect + tex_offset); - } - else { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - x, - y, - layer, - w, - h, - 1, - GL_RGBA, - GL_FLOAT, - rect_float + tex_offset); - } - } - else { - if (rect_float == NULL) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset); - } - } - - /* Restore default. */ - GPU_unpack_row_length_set(0); -} - -static void gpu_texture_update_from_ibuf( - GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) -{ - /* Partial update of texture for texture painting. This is often much - * quicker than fully updating the texture for high resolution images. */ - GPU_texture_bind(tex, 0); - - bool scaled; - if (tile != NULL) { - int *tilesize = tile->runtime.tilearray_size; - scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); - } - else { - scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y); - } - - if (scaled) { - /* Extra padding to account for bleed from neighboring pixels. */ - const int padding = 4; - const int xmax = min_ii(x + w + padding, ibuf->x); - const int ymax = min_ii(y + h + padding, ibuf->y); - x = max_ii(x - padding, 0); - y = max_ii(y - padding, 0); - w = xmax - x; - h = ymax - y; - } - - /* Get texture data pointers. */ - float *rect_float = ibuf->rect_float; - uchar *rect = (uchar *)ibuf->rect; - GLint tex_stride = ibuf->x; - GLint tex_offset = ibuf->channels * (y * ibuf->x + x); - - if (rect_float == NULL) { - /* Byte pixels. */ - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear( - ibuf->rect_colorspace); - - rect = MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__); - if (rect == NULL) { - return; - } - - tex_stride = w; - tex_offset = 0; - - /* Convert to scene linear with sRGB compression, and premultiplied for - * correct texture interpolation. */ - const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL); - IMB_colormanagement_imbuf_to_byte_texture( - rect, x, y, w, h, ibuf, compress_as_srgb, store_premultiplied); - } - } - else { - /* Float pixels. */ - const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT); - - if (ibuf->channels != 4 || scaled || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * w * h, __func__); - if (rect_float == NULL) { - return; - } - - tex_stride = w; - tex_offset = 0; - - IMB_colormanagement_imbuf_to_float_texture( - rect_float, x, y, w, h, ibuf, store_premultiplied); - } - } - - if (scaled) { - /* Slower update where we first have to scale the input pixels. */ - if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - int tilelayer = tile->runtime.tilearray_layer; - gpu_texture_update_scaled_array( - rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); - } - else { - gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h); - } - } - else { - /* Fast update at same resolution. */ - if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int tilelayer = tile->runtime.tilearray_layer; - gpu_texture_update_unscaled(rect, - rect_float, - x + tileoffset[0], - y + tileoffset[1], - tilelayer, - w, - h, - tex_stride, - tex_offset); - } - else { - gpu_texture_update_unscaled(rect, rect_float, x, y, -1, w, h, tex_stride, tex_offset); - } - } - - /* Free buffers if needed. */ - if (rect && rect != (uchar *)ibuf->rect) { - MEM_freeN(rect); - } - if (rect_float && rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - - if (GPU_get_mipmap()) { - glGenerateMipmap((tile != NULL) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D); - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - - GPU_texture_unbind(tex); -} - -/* Get the GPUTexture for a given `Image`. - * - * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already - * available. It is also required when requesting the GPUTexture for a render result. */ -GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, int textarget) -{ -#ifndef GPU_STANDALONE - if (ima == NULL) { - return NULL; - } - - /* Free any unused GPU textures, since we know we are in a thread with OpenGL - * context and might as well ensure we have as much space free as possible. */ - gpu_free_unused_buffers(); - - /* currently, gpu refresh tagging is used by ima sequences */ - if (ima->gpuflag & IMA_GPU_REFRESH) { - gpu_free_image(ima, true); - ima->gpuflag &= ~IMA_GPU_REFRESH; - } - - /* Tag as in active use for garbage collector. */ - BKE_image_tag_time(ima); - - /* Test if we already have a texture. */ - GPUTexture **tex = gpu_get_image_gputexture(ima, textarget, iuser ? iuser->multiview_eye : 0); - if (*tex) { - return *tex; - } - - /* Check if we have a valid image. If not, we return a dummy - * texture with zero bindcode so we don't keep trying. */ - uint bindcode = 0; - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (tile == NULL || tile->ok == 0) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - - /* check if we have a valid image buffer */ - ImBuf *ibuf_intern = ibuf; - if (ibuf_intern == NULL) { - ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf_intern == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - } - - if (textarget == GL_TEXTURE_2D_ARRAY) { - bindcode = gpu_texture_create_tile_array(ima, ibuf_intern); - } - else if (textarget == GL_TEXTURE_1D_ARRAY) { - bindcode = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); - } - else { - bindcode = gpu_texture_create_from_ibuf(ima, ibuf_intern, textarget); - } - - /* if `ibuf` was given, we don't own the `ibuf_intern` */ - if (ibuf == NULL) { - BKE_image_release_ibuf(ima, ibuf_intern, NULL); - } - - *tex = GPU_texture_from_bindcode(textarget, bindcode); - - GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); - - if (textarget == GL_TEXTURE_1D_ARRAY) { - /* Special for tile mapping. */ - GPU_texture_mipmap_mode(*tex, false, false); - } - - return *tex; -#endif - return NULL; -} - -GPUTexture *GPU_texture_from_movieclip(MovieClip *clip, MovieClipUser *cuser, int textarget) -{ -#ifndef GPU_STANDALONE - if (clip == NULL) { - return NULL; - } - - GPUTexture **tex = gpu_get_movieclip_gputexture(clip, cuser, textarget); - if (*tex) { - return *tex; - } - - /* check if we have a valid image buffer */ - uint bindcode = 0; - ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser); - if (ibuf == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - - bindcode = gpu_texture_create_from_ibuf(NULL, ibuf, textarget); - IMB_freeImBuf(ibuf); - - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; -#else - return NULL; -#endif -} - -void GPU_free_texture_movieclip(struct MovieClip *clip) -{ - /* number of gpu textures to keep around as cache - * We don't want to keep too many GPU textures for - * movie clips around, as they can be large.*/ - const int MOVIECLIP_NUM_GPUTEXTURES = 1; - - while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) { - MovieClip_RuntimeGPUTexture *tex = BLI_pophead(&clip->runtime.gputextures); - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* free glsl image binding */ - if (tex->gputexture[i]) { - GPU_texture_free(tex->gputexture[i]); - tex->gputexture[i] = NULL; - } - } - MEM_freeN(tex); - } -} - -static void **gpu_gen_cube_map(uint *rect, float *frect, int rectw, int recth) -{ - size_t block_size = frect ? sizeof(float[4]) : sizeof(uchar[4]); - void **sides = NULL; - int h = recth / 2; - int w = rectw / 3; - - if (w != h) { - return sides; - } - - /* PosX, NegX, PosY, NegY, PosZ, NegZ */ - sides = MEM_mallocN(sizeof(void *) * 6, ""); - for (int i = 0; i < 6; i++) { - sides[i] = MEM_mallocN(block_size * w * h, ""); - } - - /* divide image into six parts */ - /* ______________________ - * | | | | - * | NegX | NegY | PosX | - * |______|______|______| - * | | | | - * | NegZ | PosZ | PosY | - * |______|______|______| - */ - if (frect) { - float(*frectb)[4] = (float(*)[4])frect; - float(**fsides)[4] = (float(**)[4])sides; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - memcpy(&fsides[0][x * h + y], &frectb[(recth - y - 1) * rectw + 2 * w + x], block_size); - memcpy(&fsides[1][x * h + y], &frectb[(y + h) * rectw + w - 1 - x], block_size); - memcpy( - &fsides[3][y * w + x], &frectb[(recth - y - 1) * rectw + 2 * w - 1 - x], block_size); - memcpy(&fsides[5][y * w + x], &frectb[(h - y - 1) * rectw + w - 1 - x], block_size); - } - memcpy(&fsides[2][y * w], frectb[y * rectw + 2 * w], block_size * w); - memcpy(&fsides[4][y * w], frectb[y * rectw + w], block_size * w); - } - } - else { - uint **isides = (uint **)sides; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - isides[0][x * h + y] = rect[(recth - y - 1) * rectw + 2 * w + x]; - isides[1][x * h + y] = rect[(y + h) * rectw + w - 1 - x]; - isides[3][y * w + x] = rect[(recth - y - 1) * rectw + 2 * w - 1 - x]; - isides[5][y * w + x] = rect[(h - y - 1) * rectw + w - 1 - x]; - } - memcpy(&isides[2][y * w], &rect[y * rectw + 2 * w], block_size * w); - memcpy(&isides[4][y * w], &rect[y * rectw + w], block_size * w); - } - } - - return sides; -} - -static void gpu_del_cube_map(void **cube_map) -{ - int i; - if (cube_map == NULL) { - return; - } - for (i = 0; i < 6; i++) { - MEM_freeN(cube_map[i]); - } - MEM_freeN(cube_map); -} - -/* Image *ima can be NULL */ -void GPU_create_gl_tex(uint *bind, - uint *rect, - float *frect, - int rectw, - int recth, - int textarget, - bool mipmap, - bool half_float, - bool use_srgb, - Image *ima) -{ - ImBuf *ibuf = NULL; - - if (textarget == GL_TEXTURE_2D && is_over_resolution_limit(textarget, rectw, recth)) { - int tpx = rectw; - int tpy = recth; - rectw = smaller_power_of_2_limit(rectw); - recth = smaller_power_of_2_limit(recth); - - if (frect) { - ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy, 4); - IMB_scaleImBuf(ibuf, rectw, recth); - - frect = ibuf->rect_float; - } - else { - ibuf = IMB_allocFromBuffer(rect, NULL, tpx, tpy, 4); - IMB_scaleImBuf(ibuf, rectw, recth); - - rect = ibuf->rect; - } - } - - /* create image */ - glGenTextures(1, (GLuint *)bind); - glBindTexture(textarget, *bind); - - GLenum float_format = (!half_float && (ima && (ima->flag & IMA_HIGH_BITDEPTH))) ? GL_RGBA32F : - GL_RGBA16F; - GLenum internal_format = (frect) ? float_format : (use_srgb) ? GL_SRGB8_ALPHA8 : GL_RGBA8; - - if (textarget == GL_TEXTURE_2D) { - if (frect) { - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect); - } - else { - glTexImage2D( - GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect); - } - - if (GPU_get_mipmap() && mipmap) { - glGenerateMipmap(GL_TEXTURE_2D); - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - int w = rectw / 3, h = recth / 2; - - if (h == w && is_power_of_2_i(h) && !is_over_resolution_limit(textarget, h, w)) { - void **cube_map = gpu_gen_cube_map(rect, frect, rectw, recth); - GLenum type = frect ? GL_FLOAT : GL_UNSIGNED_BYTE; - - if (cube_map) { - for (int i = 0; i < 6; i++) { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, - 0, - internal_format, - w, - h, - 0, - GL_RGBA, - type, - cube_map[i]); - } - } - - if (GPU_get_mipmap() && mipmap) { - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - - gpu_del_cube_map(cube_map); - } - else { - printf("Incorrect envmap size\n"); - } - } - - glBindTexture(textarget, 0); - - if (ibuf) { - IMB_freeImBuf(ibuf); - } -} - -/** - * GPU_upload_dxt_texture() assumes that the texture is already bound and ready to go. - * This is so the viewport and the BGE can share some code. - * Returns false if the provided ImBuf doesn't have a supported DXT compression format - */ -bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb) -{ -#ifdef WITH_DDS - GLint format = 0; - int blocksize, height, width, i, size, offset = 0; - - width = ibuf->x; - height = ibuf->y; - - if (GLEW_EXT_texture_compression_s3tc) { - if (ibuf->dds_data.fourcc == FOURCC_DXT1) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - } - else if (ibuf->dds_data.fourcc == FOURCC_DXT3) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - } - else if (ibuf->dds_data.fourcc == FOURCC_DXT5) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - } - } - - if (format == 0) { - fprintf(stderr, "Unable to find a suitable DXT compression, falling back to uncompressed\n"); - return false; - } - - if (!is_power_of_2_resolution(width, height)) { - fprintf( - stderr, - "Unable to load non-power-of-two DXT image resolution, falling back to uncompressed\n"); - return false; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - - /* Reset to opengl Defaults. (Untested, might not be needed) */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - - blocksize = (ibuf->dds_data.fourcc == FOURCC_DXT1) ? 8 : 16; - for (i = 0; i < ibuf->dds_data.nummipmaps && (width || height); i++) { - if (width == 0) { - width = 1; - } - if (height == 0) { - height = 1; - } - - size = ((width + 3) / 4) * ((height + 3) / 4) * blocksize; - - glCompressedTexImage2D( - GL_TEXTURE_2D, i, format, width, height, 0, size, ibuf->dds_data.data + offset); - - offset += size; - width >>= 1; - height >>= 1; - } - /* Restore Blender default. */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - /* set number of mipmap levels we have, needed in case they don't go down to 1x1 */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1); - - return true; -#else - UNUSED_VARS(ibuf, use_srgb); - return false; -#endif -} - -void GPU_create_gl_tex_compressed(unsigned int *bind, int textarget, Image *ima, ImBuf *ibuf) -{ - /* For DDS we only support data, scene linear and sRGB. Converting to - * different colorspace would break the compression. */ - const bool use_srgb = !(IMB_colormanagement_space_is_data(ibuf->rect_colorspace) || - IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); - const bool mipmap = GPU_get_mipmap(); - const bool half_float = (ibuf->flags & IB_halffloat) != 0; - -#ifndef WITH_DDS - (void)ibuf; - /* Fall back to uncompressed if DDS isn't enabled */ - GPU_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); -#else - glGenTextures(1, (GLuint *)bind); - glBindTexture(textarget, *bind); - - if (textarget == GL_TEXTURE_2D && GPU_upload_dxt_texture(ibuf, use_srgb) == 0) { - glDeleteTextures(1, (GLuint *)bind); - GPU_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); - } - - glBindTexture(textarget, 0); -#endif -} - -/* these two functions are called on entering and exiting texture paint mode, - * temporary disabling/enabling mipmapping on all images for quick texture - * updates with glTexSubImage2D. images that didn't change don't have to be - * re-uploaded to OpenGL */ -void GPU_paint_set_mipmap(Main *bmain, bool mipmap) -{ -#ifndef GPU_STANDALONE - if (!GTS.domipmap) { - return; - } - - GTS.texpaint = !mipmap; - - if (mipmap) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_has_opengl_texture(ima)) { - if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); - } - } - } - } - } - else { - GPU_free_image(ima); - } - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - } - } - else { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_has_opengl_texture(ima)) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); - } - } - } - } - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - } - } -#endif /* GPU_STANDALONE */ -} - -void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h) -{ -#ifndef GPU_STANDALONE - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - - if ((ibuf == NULL) || (w == 0) || (h == 0)) { - /* Full reload of texture. */ - GPU_free_image(ima); - } - - GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0]; - /* Check if we need to update the main gputexture. */ - if (tex != NULL && tile == ima->tiles.first) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); - } - - /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][0]; - if (tex != NULL) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); - } - - BKE_image_release_ibuf(ima, ibuf, NULL); -#endif -} - -/* Delayed GPU texture free. Image datablocks can be deleted by any thread, - * but there may not be any active OpenGL context. In that case we push them - * into a queue and free the buffers later. */ -static LinkNode *gpu_texture_free_queue = NULL; -static ThreadMutex gpu_texture_queue_mutex = BLI_MUTEX_INITIALIZER; - -static void gpu_free_unused_buffers() -{ - if (gpu_texture_free_queue == NULL) { - return; - } - - BLI_mutex_lock(&gpu_texture_queue_mutex); - - if (gpu_texture_free_queue != NULL) { - for (LinkNode *node = gpu_texture_free_queue; node; node = node->next) { - GPUTexture *tex = node->link; - GPU_texture_free(tex); - } - - BLI_linklist_free(gpu_texture_free_queue, NULL); - gpu_texture_free_queue = NULL; - } - - BLI_mutex_unlock(&gpu_texture_queue_mutex); -} - -static void gpu_free_image(Image *ima, const bool immediate) -{ - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye] != NULL) { - if (immediate) { - GPU_texture_free(ima->gputexture[i][eye]); - } - else { - BLI_mutex_lock(&gpu_texture_queue_mutex); - BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]); - BLI_mutex_unlock(&gpu_texture_queue_mutex); - } - - ima->gputexture[i][eye] = NULL; - } - } - } - - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; -} - -void GPU_free_unused_buffers() -{ - if (BLI_thread_is_main()) { - gpu_free_unused_buffers(); - } -} - -void GPU_free_image(Image *ima) -{ - gpu_free_image(ima, BLI_thread_is_main()); -} - -void GPU_free_images(Main *bmain) -{ - if (bmain) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - GPU_free_image(ima); - } - } -} - -/* same as above but only free animated images */ -void GPU_free_images_anim(Main *bmain) -{ - if (bmain) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_is_animated(ima)) { - GPU_free_image(ima); - } - } - } -} - -void GPU_free_images_old(Main *bmain) -{ - static int lasttime = 0; - int ctime = (int)PIL_check_seconds_timer(); - - /* - * Run garbage collector once for every collecting period of time - * if textimeout is 0, that's the option to NOT run the collector - */ - if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) { - return; - } - - /* of course not! */ - if (G.is_rendering) { - return; - } - - lasttime = ctime; - - Image *ima = bmain->images.first; - while (ima) { - if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) { - /* If it's in GL memory, deallocate and set time tag to current time - * This gives textures a "second chance" to be used before dying. */ - if (BKE_image_has_opengl_texture(ima)) { - GPU_free_image(ima); - ima->lastused = ctime; - } - /* Otherwise, just kill the buffers */ - else { - BKE_image_free_buffers(ima); - } - } - ima = ima->id.next; - } -} diff --git a/source/blender/gpu/intern/gpu_element.c b/source/blender/gpu/intern/gpu_element.cc index 036588b4a48..9f104ab3fec 100644 --- a/source/blender/gpu/intern/gpu_element.c +++ b/source/blender/gpu/intern/gpu_element.cc @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "GPU_element.h" +#include "GPU_glew.h" #include "gpu_context_private.h" @@ -37,21 +38,18 @@ static GLenum convert_index_type_to_gl(GPUIndexBufType type) { - static const GLenum table[] = { - [GPU_INDEX_U16] = GL_UNSIGNED_SHORT, - [GPU_INDEX_U32] = GL_UNSIGNED_INT, - }; - return table[type]; +#if GPU_TRACK_INDEX_RANGE + return (type == GPU_INDEX_U32) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; +#else + return GL_UNSIGNED_INT; +#endif } uint GPU_indexbuf_size_get(const GPUIndexBuf *elem) { #if GPU_TRACK_INDEX_RANGE - static const uint table[] = { - [GPU_INDEX_U16] = sizeof(GLushort), - [GPU_INDEX_U32] = sizeof(GLuint), - }; - return elem->index_len * table[elem->index_type]; + return elem->index_len * + ((elem->index_type == GPU_INDEX_U32) ? sizeof(GLuint) : sizeof(GLshort)); #else return elem->index_len * sizeof(GLuint); #endif @@ -86,7 +84,7 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder, builder->max_index_len = index_len; builder->index_len = 0; // start empty builder->prim_type = prim_type; - builder->data = MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); + builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); } void GPU_indexbuf_init(GPUIndexBufBuilder *builder, @@ -241,7 +239,7 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem) GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length) { - GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); GPU_indexbuf_create_subrange_in_place(elem, elem_src, start, length); return elem; } @@ -331,7 +329,7 @@ static void squeeze_indices_short(GPUIndexBufBuilder *builder, GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder) { - GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); GPU_indexbuf_build_in_place(builder, elem); return elem; } diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.cc index 9c37cc32e1d..35b70a18363 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.cc @@ -31,6 +31,8 @@ #include "BKE_global.h" #include "MEM_guardedalloc.h" +#include "DNA_userdef_types.h" + #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_glew.h" @@ -104,7 +106,8 @@ static struct GPUGlobal { static void gpu_detect_mip_render_workaround(void) { int cube_size = 2; - float *source_pix = MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, __func__); + float *source_pix = (float *)MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, + __func__); float clear_color[4] = {1.0f, 0.5f, 0.0f, 0.0f}; GPUTexture *tex = GPU_texture_create_cube(cube_size, GPU_RGBA16F, source_pix, NULL); @@ -123,7 +126,7 @@ static void gpu_detect_mip_render_workaround(void) GPU_framebuffer_restore(); GPU_framebuffer_free(fb); - float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 1); + float *data = (float *)GPU_texture_read(tex, GPU_DATA_FLOAT, 1); GG.mip_render_workaround = !equals_v4v4(clear_color, data); MEM_freeN(data); @@ -238,6 +241,13 @@ bool GPU_crappy_amd_driver(void) return GG.broken_amd_driver; } +int GPU_texture_size_with_limit(int res) +{ + int size = GPU_max_texture_size(); + int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; + return min_ii(reslimit, res); +} + void gpu_extensions_init(void) { /* during 2.8 development each platform has its own OpenGL minimum requirements @@ -311,7 +321,7 @@ void gpu_extensions_init(void) if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL)) { /* Limit this fix to older hardware with GL < 4.5. This means Broadwell GPUs are * covered since they only support GL 4.4 on windows. - * This fixes some issues with workbench antialiasing on Win + Intel GPU. (see T76273) */ + * This fixes some issues with workbench anti-aliasing on Win + Intel GPU. (see T76273) */ if (!GLEW_VERSION_4_5) { GG.texture_copy_workaround = true; } diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.cc index 77abb786117..056a449ac6d 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -28,7 +28,6 @@ #include "BLI_utildefines.h" #include "GPU_batch.h" -#include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_shader.h" @@ -53,6 +52,10 @@ typedef enum { GPU_FB_MAX_ATTACHEMENT, } GPUAttachmentType; +#define FOREACH_ATTACHMENT_RANGE(att, _start, _end) \ + for (GPUAttachmentType att = static_cast<GPUAttachmentType>(_start); att < _end; \ + att = static_cast<GPUAttachmentType>(att + 1)) + #define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0) #define GPU_FB_DIRTY_DRAWBUFFER (1 << 15) @@ -74,17 +77,25 @@ struct GPUFrameBuffer { static GLenum convert_attachment_type_to_gl(GPUAttachmentType type) { - static const GLenum table[] = { - [GPU_FB_DEPTH_ATTACHMENT] = GL_DEPTH_ATTACHMENT, - [GPU_FB_DEPTH_STENCIL_ATTACHMENT] = GL_DEPTH_STENCIL_ATTACHMENT, - [GPU_FB_COLOR_ATTACHMENT0] = GL_COLOR_ATTACHMENT0, - [GPU_FB_COLOR_ATTACHMENT1] = GL_COLOR_ATTACHMENT1, - [GPU_FB_COLOR_ATTACHMENT2] = GL_COLOR_ATTACHMENT2, - [GPU_FB_COLOR_ATTACHMENT3] = GL_COLOR_ATTACHMENT3, - [GPU_FB_COLOR_ATTACHMENT4] = GL_COLOR_ATTACHMENT4, - [GPU_FB_COLOR_ATTACHMENT5] = GL_COLOR_ATTACHMENT5, - }; - return table[type]; +#define ATTACHMENT(type) \ + case GPU_FB_##type: { \ + return GL_##type; \ + } \ + ((void)0) + + switch (type) { + ATTACHMENT(DEPTH_ATTACHMENT); + ATTACHMENT(DEPTH_STENCIL_ATTACHMENT); + ATTACHMENT(COLOR_ATTACHMENT0); + ATTACHMENT(COLOR_ATTACHMENT1); + ATTACHMENT(COLOR_ATTACHMENT2); + ATTACHMENT(COLOR_ATTACHMENT3); + ATTACHMENT(COLOR_ATTACHMENT4); + ATTACHMENT(COLOR_ATTACHMENT5); + default: + BLI_assert(0); + return GL_COLOR_ATTACHMENT0; + } } static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) @@ -98,7 +109,7 @@ static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) case GPU_DEPTH32F_STENCIL8: return GPU_FB_DEPTH_STENCIL_ATTACHMENT; default: - return GPU_FB_COLOR_ATTACHMENT0 + slot; + return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); } } @@ -198,7 +209,7 @@ GPUFrameBuffer *GPU_framebuffer_create(void) { /* We generate the FB object later at first use in order to * create the framebuffer in the right opengl context. */ - return MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); + return (GPUFrameBuffer *)MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); } static void gpu_framebuffer_init(GPUFrameBuffer *fb) @@ -210,7 +221,8 @@ static void gpu_framebuffer_init(GPUFrameBuffer *fb) void GPU_framebuffer_free(GPUFrameBuffer *fb) { - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex); } @@ -304,7 +316,7 @@ void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, in void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex) { - GPUAttachmentType type = GPU_texture_detach_framebuffer(tex, fb); + GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, fb); GPU_framebuffer_texture_detach_slot(fb, tex, type); } @@ -387,8 +399,8 @@ static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb) BLI_assert(GPU_framebuffer_active_get() == fb); /* Update attachments */ - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { - + FOREACH_ATTACHMENT_RANGE(type, 0, GPU_FB_MAX_ATTACHEMENT) + { if (type >= GPU_FB_COLOR_ATTACHMENT0) { if (fb->attachments[type].tex) { gl_attachments[numslots] = convert_attachment_type_to_gl(type); @@ -438,7 +450,8 @@ static void gpu_framebuffer_update_attachments_and_fill_empty_slots(GPUFrameBuff BLI_assert(GPU_framebuffer_active_get() == fb); /* Update attachments */ - for (GPUAttachmentType type = GPU_FB_MAX_ATTACHEMENT; type--;) { + for (int i_type = GPU_FB_MAX_ATTACHEMENT - 1; i_type >= 0; --i_type) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); GPUTexture *tex = fb->attachments[type].tex; if (type >= GPU_FB_COLOR_ATTACHMENT0) { @@ -623,8 +636,9 @@ void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0; - for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) { + int i_type = GPU_FB_COLOR_ATTACHMENT0; + for (int i = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i++, i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { glClearBufferfv(GL_COLOR, i, clear_cols[i]); } @@ -702,7 +716,8 @@ void GPU_framebuffer_read_color(GPUFrameBuffer *fb, void *data) { CHECK_FRAMEBUFFER_IS_BOUND(fb); - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, data); + gpu_framebuffer_read_color_ex( + x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data); } /* read_slot and write_slot are only used for color buffers. */ @@ -815,7 +830,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, current_dim[0] = max_ii(current_dim[0] / 2, 1); current_dim[1] = max_ii(current_dim[1] / 2, 1); - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { /* Some Intel HDXXX have issue with rendering to a mipmap that is below * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case @@ -844,7 +860,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, } } - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { /* reset mipmap level range */ GPUTexture *tex = fb->attachments[type].tex; @@ -885,9 +902,11 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) for (int i = 0; i < MAX_CTX_FB_LEN; i++) { if (ofs->framebuffers[i].fb == NULL) { ofs->framebuffers[i].ctx = ctx; - GPU_framebuffer_ensure_config( - &ofs->framebuffers[i].fb, - {GPU_ATTACHMENT_TEXTURE(ofs->depth), GPU_ATTACHMENT_TEXTURE(ofs->color)}); + GPU_framebuffer_ensure_config(&ofs->framebuffers[i].fb, + { + GPU_ATTACHMENT_TEXTURE(ofs->depth), + GPU_ATTACHMENT_TEXTURE(ofs->color), + }); } if (ofs->framebuffers[i].ctx == ctx) { @@ -916,9 +935,7 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) GPUOffScreen *GPU_offscreen_create( int width, int height, bool depth, bool high_bitdepth, char err_out[256]) { - GPUOffScreen *ofs; - - ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); + GPUOffScreen *ofs = (GPUOffScreen *)MEM_callocN(sizeof(GPUOffScreen), __func__); /* Sometimes areas can have 0 height or width and this will * create a 1D texture which we don't want. */ @@ -975,7 +992,7 @@ void GPU_offscreen_free(GPUOffScreen *ofs) void GPU_offscreen_bind(GPUOffScreen *ofs, bool save) { if (save) { - gpuPushAttr(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT); + gpuPushAttr((eGPUAttrMask)(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT)); GPUFrameBuffer *fb = GPU_framebuffer_active_get(); gpuPushFrameBuffer(fb); } @@ -1023,14 +1040,15 @@ void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default()); } -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels) { const int w = GPU_texture_width(ofs->color); const int h = GPU_texture_height(ofs->color); - BLI_assert(type == GL_UNSIGNED_BYTE || type == GL_FLOAT); + BLI_assert(ELEM(type, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT)); + GLenum gl_type = (type == GPU_DATA_FLOAT) ? GL_FLOAT : GL_UNSIGNED_BYTE; - glReadPixels(0, 0, w, h, GL_RGBA, type, pixels); + glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels); } int GPU_offscreen_width(const GPUOffScreen *ofs) @@ -1077,8 +1095,7 @@ void GPU_clear(eGPUFrameBufferBits flags) void GPU_frontbuffer_read_pixels( int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) { - glReadBuffer(GL_FRONT); - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, data); + gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data); } /* For stereo rendering. */ diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.cc index 1e99371f9a1..7283f7c12aa 100644 --- a/source/blender/gpu/intern/gpu_immediate.c +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -29,6 +29,7 @@ #include "GPU_attr_binding.h" #include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_texture.h" #include "gpu_attr_binding_private.h" @@ -40,10 +41,6 @@ #include <stdlib.h> #include <string.h> -/* necessary functions from matrix API */ -extern void GPU_matrix_bind(const GPUShaderInterface *); -extern bool GPU_matrix_dirty_get(void); - typedef struct ImmediateDrawBuffer { GLuint vbo_id; GLubyte *buffer_data; @@ -75,7 +72,7 @@ typedef struct { GLuint vao_id; - GLuint bound_program; + GPUShader *bound_program; const GPUShaderInterface *shader_interface; GPUAttrBinding attr_binding; uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */ @@ -146,48 +143,47 @@ GPUVertFormat *immVertexFormat(void) return &imm.vertex_format; } -void immBindProgram(GLuint program, const GPUShaderInterface *shaderface) +void immBindShader(GPUShader *shader) { #if TRUST_NO_ONE - assert(imm.bound_program == 0); - assert(glIsProgram(program)); + assert(imm.bound_program == NULL); + assert(glIsProgram(shader->program)); #endif - imm.bound_program = program; - imm.shader_interface = shaderface; + imm.bound_program = shader; + imm.shader_interface = shader->interface; if (!imm.vertex_format.packed) { VertexFormat_pack(&imm.vertex_format); } - glUseProgram(program); - get_attr_locations(&imm.vertex_format, &imm.attr_binding, shaderface); - GPU_matrix_bind(shaderface); - GPU_shader_set_srgb_uniform(shaderface); + GPU_shader_bind(shader); + get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface); + GPU_matrix_bind(imm.shader_interface); + GPU_shader_set_srgb_uniform(imm.shader_interface); } void immBindBuiltinProgram(eGPUBuiltinShader shader_id) { GPUShader *shader = GPU_shader_get_builtin_shader(shader_id); - immBindProgram(shader->program, shader->interface); + immBindShader(shader); } void immUnbindProgram(void) { #if TRUST_NO_ONE - assert(imm.bound_program != 0); + assert(imm.bound_program != NULL); #endif #if PROGRAM_NO_OPTI glUseProgram(0); #endif - imm.bound_program = 0; + imm.bound_program = NULL; } /* XXX do not use it. Special hack to use OCIO with batch API. */ -void immGetProgram(GLuint *program, GPUShaderInterface **shaderface) +GPUShader *immGetShader(void) { - *program = imm.bound_program; - *shaderface = (GPUShaderInterface *)imm.shader_interface; + return imm.bound_program; } #if TRUST_NO_ONE @@ -281,7 +277,7 @@ void immBegin(GPUPrimType prim_type, uint vertex_len) } #endif - active_buffer->buffer_data = glMapBufferRange( + active_buffer->buffer_data = (GLubyte *)glMapBufferRange( GL_ARRAY_BUFFER, active_buffer->buffer_offset, bytes_needed, @@ -367,17 +363,18 @@ static void immDrawSetup(void) const GLvoid *pointer = (const GLubyte *)0 + offset; const uint loc = read_attr_location(&imm.attr_binding, a_idx); + const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); switch (a->fetch_mode) { case GPU_FETCH_FLOAT: case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_FALSE, stride, pointer); + glVertexAttribPointer(loc, a->comp_len, type, GL_FALSE, stride, pointer); break; case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_TRUE, stride, pointer); + glVertexAttribPointer(loc, a->comp_len, type, GL_TRUE, stride, pointer); break; case GPU_FETCH_INT: - glVertexAttribIPointer(loc, a->comp_len, a->gl_comp_type, stride, pointer); + glVertexAttribIPointer(loc, a->comp_len, type, stride, pointer); } } @@ -425,7 +422,7 @@ void immEnd(void) GPU_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_len); /* TODO: resize only if vertex count is much smaller */ } - GPU_batch_program_set(imm.batch, imm.bound_program, imm.shader_interface); + GPU_batch_set_shader(imm.batch, imm.bound_program); imm.batch->phase = GPU_BATCH_READY_TO_DRAW; imm.batch = NULL; /* don't free, batch belongs to caller */ } diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index 54ddb9351b9..c5061ec9ba3 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -95,7 +95,7 @@ void GPU_exit(void) initialized = false; } -bool GPU_is_initialized(void) +bool GPU_is_init(void) { return initialized; } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index c65c1046b8f..f3477b6f3a4 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -85,7 +85,7 @@ struct GPUMaterial { bool has_surface_output; /* Only used by Eevee to know which bsdf are used. */ - int flag; + eGPUMatFlag flag; /* Used by 2.8 pipeline */ GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */ @@ -659,7 +659,8 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, const char *geom_code, const char *frag_lib, const char *defines, - const char *name) + const char *name, + GPUMaterialEvalCallbackFn callback) { LinkData *link; bool has_volume_output, has_surface_output; @@ -696,6 +697,9 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, mat->has_volume_output = has_volume_output; if (mat->graph.outlink) { + if (callback) { + callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines); + } /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */ if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { defines = BLI_string_joinN(defines, diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.cc index 669bf56b726..c15bb1fba19 100644 --- a/source/blender/gpu/intern/gpu_matrix.c +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -37,8 +37,6 @@ #include "MEM_guardedalloc.h" -#define DEBUG_MATRIX_BIND 0 - #define MATRIX_STACK_DEPTH 32 typedef float Mat4[4][4]; @@ -79,7 +77,7 @@ GPUMatrixState *GPU_matrix_state_create(void) } \ } - GPUMatrixState *state = MEM_mallocN(sizeof(*state), __func__); + GPUMatrixState *state = (GPUMatrixState *)MEM_mallocN(sizeof(*state), __func__); const MatrixStack identity_stack = {{MATRIX_4X4_IDENTITY}, 0}; state->model_view_stack = state->projection_stack = identity_stack; @@ -662,51 +660,32 @@ void GPU_matrix_bind(const GPUShaderInterface *shaderface) int32_t MV_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW_INV); int32_t P_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION_INV); + /* XXX(fclem) this works but this assumes shader is unused inside GPU_shader_uniform_vector. */ + GPUShader *sh = NULL; if (MV != -1) { -#if DEBUG_MATRIX_BIND - puts("setting MV matrix"); -#endif - - glUniformMatrix4fv(MV, 1, GL_FALSE, (const float *)GPU_matrix_model_view_get(NULL)); + GPU_shader_uniform_vector(sh, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL)); } - if (P != -1) { -#if DEBUG_MATRIX_BIND - puts("setting P matrix"); -#endif - - glUniformMatrix4fv(P, 1, GL_FALSE, (const float *)GPU_matrix_projection_get(NULL)); + GPU_shader_uniform_vector(sh, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL)); } - if (MVP != -1) { -#if DEBUG_MATRIX_BIND - puts("setting MVP matrix"); -#endif - - glUniformMatrix4fv( - MVP, 1, GL_FALSE, (const float *)GPU_matrix_model_view_projection_get(NULL)); + GPU_shader_uniform_vector( + sh, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL)); } - if (N != -1) { -#if DEBUG_MATRIX_BIND - puts("setting normal matrix"); -#endif - - glUniformMatrix3fv(N, 1, GL_FALSE, (const float *)GPU_matrix_normal_get(NULL)); + GPU_shader_uniform_vector(sh, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL)); } - if (MV_inv != -1) { Mat4 m; GPU_matrix_model_view_get(m); invert_m4(m); - glUniformMatrix4fv(MV_inv, 1, GL_FALSE, (const float *)m); + GPU_shader_uniform_vector(sh, MV_inv, 16, 1, (const float *)m); } - if (P_inv != -1) { Mat4 m; GPU_matrix_projection_get(m); invert_m4(m); - glUniformMatrix4fv(P_inv, 1, GL_FALSE, (const float *)m); + GPU_shader_uniform_vector(sh, P_inv, 16, 1, (const float *)m); } gpu_matrix_state_active_set_dirty(false); diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 21bb139f610..7265abf4d65 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -28,7 +28,6 @@ #include "DNA_customdata_types.h" #include "DNA_listBase.h" -#include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" diff --git a/source/blender/gpu/intern/gpu_platform.c b/source/blender/gpu/intern/gpu_platform.cc index 5cabde61bc3..5cabde61bc3 100644 --- a/source/blender/gpu/intern/gpu_platform.c +++ b/source/blender/gpu/intern/gpu_platform.cc diff --git a/source/blender/gpu/intern/gpu_primitive.c b/source/blender/gpu/intern/gpu_primitive.c index 2285c1ab95b..3b11b38db87 100644 --- a/source/blender/gpu/intern/gpu_primitive.c +++ b/source/blender/gpu/intern/gpu_primitive.c @@ -26,35 +26,6 @@ #include "GPU_primitive.h" #include "gpu_primitive_private.h" -GPUPrimClass GPU_primtype_class(GPUPrimType prim_type) -{ - static const GPUPrimClass classes[] = { - [GPU_PRIM_POINTS] = GPU_PRIM_CLASS_POINT, - [GPU_PRIM_LINES] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_STRIP] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_LOOP] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_TRIS] = GPU_PRIM_CLASS_SURFACE, - [GPU_PRIM_TRI_STRIP] = GPU_PRIM_CLASS_SURFACE, - [GPU_PRIM_TRI_FAN] = GPU_PRIM_CLASS_SURFACE, - - [GPU_PRIM_LINES_ADJ] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_STRIP_ADJ] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_TRIS_ADJ] = GPU_PRIM_CLASS_SURFACE, - - [GPU_PRIM_NONE] = GPU_PRIM_CLASS_NONE, - }; - - return classes[prim_type]; -} - -bool GPU_primtype_belongs_to_class(GPUPrimType prim_type, GPUPrimClass prim_class) -{ - if (prim_class == GPU_PRIM_CLASS_NONE && prim_type == GPU_PRIM_NONE) { - return true; - } - return prim_class & GPU_primtype_class(prim_type); -} - GLenum convert_prim_type_to_gl(GPUPrimType prim_type) { #if TRUST_NO_ONE diff --git a/source/blender/gpu/intern/gpu_primitive_private.h b/source/blender/gpu/intern/gpu_primitive_private.h index b3b6bd7fc88..e91eec18786 100644 --- a/source/blender/gpu/intern/gpu_primitive_private.h +++ b/source/blender/gpu/intern/gpu_primitive_private.h @@ -25,4 +25,13 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO(fclem) move to OGL backend */ GLenum convert_prim_type_to_gl(GPUPrimType); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h index a5caa816ef4..ef96bedae4a 100644 --- a/source/blender/gpu/intern/gpu_private.h +++ b/source/blender/gpu/intern/gpu_private.h @@ -20,6 +20,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + /* call this before running any of the functions below */ void gpu_platform_init(void); void gpu_platform_exit(void); @@ -39,3 +43,7 @@ void gpu_framebuffer_module_exit(void); /* gpu_pbvh.c */ void gpu_pbvh_init(void); void gpu_pbvh_exit(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 5766a176a96..c069cbe012f 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -26,7 +26,6 @@ #include <stdlib.h> #include <string.h> -#include "GPU_glew.h" #include "GPU_select.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index c6d8545527c..3025b5d66da 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -27,7 +27,6 @@ #include <stdlib.h> #include <string.h> -#include "GPU_draw.h" #include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_select.h" diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc new file mode 100644 index 00000000000..2b54e733bf5 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -0,0 +1,839 @@ +/* + * 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 gpu + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math_base.h" +#include "BLI_math_vector.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "BKE_appdir.h" +#include "BKE_global.h" + +#include "DNA_space_types.h" + +#include "GPU_extensions.h" +#include "GPU_matrix.h" +#include "GPU_platform.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_uniformbuffer.h" + +#include "gpu_shader_private.h" + +extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[]; + +/* Adjust these constants as needed. */ +#define MAX_DEFINE_LENGTH 256 +#define MAX_EXT_DEFINE_LENGTH 512 + +#ifndef NDEBUG +static uint g_shaderid = 0; +#endif + +/* -------------------------------------------------------------------- */ +/** \name Convenience functions + * \{ */ + +static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) +{ + int line = 1; + + fprintf(stderr, "GPUShader: %s error:\n", task); + + for (int i = 0; i < totcode; i++) { + const char *c, *pos, *end = code[i] + strlen(code[i]); + + if (G.debug & G_DEBUG) { + fprintf(stderr, "===== shader string %d ====\n", i + 1); + + c = code[i]; + while ((c < end) && (pos = strchr(c, '\n'))) { + fprintf(stderr, "%2d ", line); + fwrite(c, (pos + 1) - c, 1, stderr); + c = pos + 1; + line++; + } + + fprintf(stderr, "%s", c); + } + } + + fprintf(stderr, "%s\n", log); +} + +static const char *gpu_shader_version(void) +{ + return "#version 330\n"; +} + +static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) +{ + /* enable extensions for features that are not part of our base GLSL version + * don't use an extension for something already available! + */ + + if (GLEW_ARB_texture_gather) { + /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather + * is reported to be supported but yield a compile error (see T55802). */ + if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) { + strcat(defines, "#extension GL_ARB_texture_gather: enable\n"); + + /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the + * shader so double check the preprocessor define (see T56544). */ + if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) { + strcat(defines, "#ifdef GL_ARB_texture_gather\n"); + strcat(defines, "# define GPU_ARB_texture_gather\n"); + strcat(defines, "#endif\n"); + } + else { + strcat(defines, "#define GPU_ARB_texture_gather\n"); + } + } + } + if (GLEW_ARB_texture_query_lod) { + /* a #version 400 feature, but we use #version 330 maximum so use extension */ + strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); + } + if (GLEW_ARB_shader_draw_parameters) { + strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n"); + } + if (GPU_arb_texture_cube_map_array_is_supported()) { + strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n"); + strcat(defines, "#define GPU_ARB_texture_cube_map_array\n"); + } +} + +static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) +{ + /* some useful defines to detect GPU type */ + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#define GPU_ATI\n"); + if (GPU_crappy_amd_driver()) { + strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n"); + } + } + else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#define GPU_NVIDIA\n"); + } + else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#define GPU_INTEL\n"); + } + + /* some useful defines to detect OS type */ + if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) { + strcat(defines, "#define OS_WIN\n"); + } + else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { + strcat(defines, "#define OS_MAC\n"); + } + else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) { + strcat(defines, "#define OS_UNIX\n"); + } + + float derivatives_factors[2]; + GPU_get_dfdy_factors(derivatives_factors); + if (derivatives_factors[0] == 1.0f) { + strcat(defines, "#define DFDX_SIGN 1.0\n"); + } + else { + strcat(defines, "#define DFDX_SIGN -1.0\n"); + } + + if (derivatives_factors[1] == 1.0f) { + strcat(defines, "#define DFDY_SIGN 1.0\n"); + } + else { + strcat(defines, "#define DFDY_SIGN -1.0\n"); + } +} + +#define DEBUG_SHADER_NONE "" +#define DEBUG_SHADER_VERTEX "vert" +#define DEBUG_SHADER_FRAGMENT "frag" +#define DEBUG_SHADER_GEOMETRY "geom" + +/** + * Dump GLSL shaders to disk + * + * This is used for profiling shader performance externally and debug if shader code is correct. + * If called with no code, it simply bumps the shader index, so different shaders for the same + * program share the same index. + */ +static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension) +{ + if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) { + return; + } + + /* We use the same shader index for shaders in the same program. + * So we call this function once before calling for the individual shaders. */ + static int shader_index = 0; + if (code == NULL) { + shader_index++; + BLI_assert(STREQ(DEBUG_SHADER_NONE, extension)); + return; + } + + /* Determine the full path of the new shader. */ + char shader_path[FILE_MAX]; + + char file_name[512] = {'\0'}; + sprintf(file_name, "%04d.%s", shader_index, extension); + + BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name); + + /* Write shader to disk. */ + FILE *f = fopen(shader_path, "w"); + if (f == NULL) { + printf("Error writing to file: %s\n", shader_path); + } + for (int j = 0; j < num_shaders; j++) { + fprintf(f, "%s", code[j]); + } + fclose(f); + printf("Shader file written to disk: %s\n", shader_path); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +GPUShader *GPU_shader_create(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines, + const char *shname) +{ + return GPU_shader_create_ex( + vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname); +} + +GPUShader *GPU_shader_create_from_python(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines) +{ + char *libcodecat = NULL; + + if (libcode == NULL) { + libcode = datatoc_gpu_shader_colorspace_lib_glsl; + } + else { + libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); + } + + GPUShader *sh = GPU_shader_create_ex( + vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL); + + MEM_SAFE_FREE(libcodecat); + return sh; +} + +GPUShader *GPU_shader_load_from_binary(const char *binary, + const int binary_format, + const int binary_len, + const char *shname) +{ + BLI_assert(GL_ARB_get_program_binary); + int success; + int program = glCreateProgram(); + + glProgramBinary(program, binary_format, binary, binary_len); + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success) { + glUseProgram(program); + + GPUShader *shader = (GPUShader *)MEM_callocN(sizeof(*shader), __func__); + shader->interface = GPU_shaderinterface_create(program); + shader->program = program; + +#ifndef NDEBUG + BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); +#else + UNUSED_VARS(shname); +#endif + + return shader; + } + + glDeleteProgram(program); + return NULL; +} + +GPUShader *GPU_shader_create_ex(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines, + const eGPUShaderTFBType tf_type, + const char **tf_names, + const int tf_count, + const char *shname) +{ + GLint status; + GLchar log[5000]; + GLsizei length = 0; + GPUShader *shader; + char standard_defines[MAX_DEFINE_LENGTH] = ""; + char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; + + shader = (GPUShader *)MEM_callocN(sizeof(GPUShader), "GPUShader"); + gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE); + +#ifndef NDEBUG + BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); +#else + UNUSED_VARS(shname); +#endif + + /* At least a vertex shader and a fragment shader are required. */ + BLI_assert((fragcode != NULL) && (vertexcode != NULL)); + + if (vertexcode) { + shader->vertex = glCreateShader(GL_VERTEX_SHADER); + } + if (fragcode) { + shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); + } + if (geocode) { + shader->geometry = glCreateShader(GL_GEOMETRY_SHADER); + } + + shader->program = glCreateProgram(); + + if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) || + (geocode && !shader->geometry)) { + fprintf(stderr, "GPUShader, object creation failed.\n"); + GPU_shader_free(shader); + return NULL; + } + + gpu_shader_standard_defines(standard_defines); + gpu_shader_standard_extensions(standard_extensions); + + if (vertexcode) { + const char *source[7]; + /* custom limit, may be too small, beware */ + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = + "#define GPU_VERTEX_SHADER\n" + "#define IN_OUT out\n"; + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (geocode) { + source[num_source++] = "#define USE_GEOMETRY_SHADER\n"; + } + if (defines) { + source[num_source++] = defines; + } + source[num_source++] = vertexcode; + + gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX); + + glAttachShader(shader->program, shader->vertex); + glShaderSource(shader->vertex, num_source, source, NULL); + + glCompileShader(shader->vertex); + glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + } + + if (fragcode) { + const char *source[8]; + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = + "#define GPU_FRAGMENT_SHADER\n" + "#define IN_OUT in\n"; + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (geocode) { + source[num_source++] = "#define USE_GEOMETRY_SHADER\n"; + } + if (defines) { + source[num_source++] = defines; + } + if (libcode) { + source[num_source++] = libcode; + } + source[num_source++] = fragcode; + + gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT); + + glAttachShader(shader->program, shader->fragment); + glShaderSource(shader->fragment, num_source, source, NULL); + + glCompileShader(shader->fragment); + glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + } + + if (geocode) { + const char *source[6]; + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = "#define GPU_GEOMETRY_SHADER\n"; + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (defines) { + source[num_source++] = defines; + } + source[num_source++] = geocode; + + gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY); + + glAttachShader(shader->program, shader->geometry); + glShaderSource(shader->geometry, num_source, source, NULL); + + glCompileShader(shader->geometry); + glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + } + + if (tf_names != NULL) { + glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); + /* Primitive type must be setup */ + BLI_assert(tf_type != GPU_SHADER_TFB_NONE); + shader->feedback_transform_type = tf_type; + } + + glLinkProgram(shader->program); + glGetProgramiv(shader->program, GL_LINK_STATUS, &status); + if (!status) { + glGetProgramInfoLog(shader->program, sizeof(log), &length, log); + /* print attached shaders in pipeline order */ + if (defines) { + shader_print_errors("linking", log, &defines, 1); + } + if (vertexcode) { + shader_print_errors("linking", log, &vertexcode, 1); + } + if (geocode) { + shader_print_errors("linking", log, &geocode, 1); + } + if (libcode) { + shader_print_errors("linking", log, &libcode, 1); + } + if (fragcode) { + shader_print_errors("linking", log, &fragcode, 1); + } + + GPU_shader_free(shader); + return NULL; + } + + glUseProgram(shader->program); + shader->interface = GPU_shaderinterface_create(shader->program); + + return shader; +} + +#undef DEBUG_SHADER_GEOMETRY +#undef DEBUG_SHADER_FRAGMENT +#undef DEBUG_SHADER_VERTEX +#undef DEBUG_SHADER_NONE + +void GPU_shader_free(GPUShader *shader) +{ +#if 0 /* Would be nice to have, but for now the Deferred compilation \ + * does not have a GPUContext. */ + BLI_assert(GPU_context_active_get() != NULL); +#endif + BLI_assert(shader); + + if (shader->vertex) { + glDeleteShader(shader->vertex); + } + if (shader->geometry) { + glDeleteShader(shader->geometry); + } + if (shader->fragment) { + glDeleteShader(shader->fragment); + } + if (shader->program) { + glDeleteProgram(shader->program); + } + + if (shader->interface) { + GPU_shaderinterface_discard(shader->interface); + } + + MEM_freeN(shader); +} + +static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc) +{ + bool is_alloc = false; + if (str_arr == NULL) { + *r_is_alloc = false; + return NULL; + } + /* Skip empty strings (avoid alloc if we can). */ + while (str_arr[0] && str_arr[0][0] == '\0') { + str_arr++; + } + int i; + for (i = 0; str_arr[i]; i++) { + if (i != 0 && str_arr[i][0] != '\0') { + is_alloc = true; + } + } + *r_is_alloc = is_alloc; + if (is_alloc) { + return BLI_string_join_arrayN(str_arr, i); + } + else { + return str_arr[0]; + } +} + +/** + * Use via #GPU_shader_create_from_arrays macro (avoids passing in param). + * + * Similar to #DRW_shader_create_with_lib with the ability to include libs for each type of shader. + * + * It has the advantage that each item can be conditionally included + * without having to build the string inline, then free it. + * + * \param params: NULL terminated arrays of strings. + * + * Example: + * \code{.c} + * sh = GPU_shader_create_from_arrays({ + * .vert = (const char *[]){shader_lib_glsl, shader_vert_glsl, NULL}, + * .geom = (const char *[]){shader_geom_glsl, NULL}, + * .frag = (const char *[]){shader_frag_glsl, NULL}, + * .defs = (const char *[]){"#define DEFINE\n", test ? "#define OTHER_DEFINE\n" : "", NULL}, + * }); + * \endcode + */ +struct GPUShader *GPU_shader_create_from_arrays_impl( + const struct GPU_ShaderCreateFromArray_Params *params) +{ + struct { + const char *str; + bool is_alloc; + } str_dst[4] = {{0}}; + const char **str_src[4] = {params->vert, params->frag, params->geom, params->defs}; + + for (int i = 0; i < ARRAY_SIZE(str_src); i++) { + str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc); + } + + GPUShader *sh = GPU_shader_create( + str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__); + + for (int i = 0; i < ARRAY_SIZE(str_dst); i++) { + if (str_dst[i].is_alloc) { + MEM_freeN((void *)str_dst[i].str); + } + } + return sh; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Binding + * \{ */ + +void GPU_shader_bind(GPUShader *shader) +{ + BLI_assert(shader && shader->program); + + glUseProgram(shader->program); + GPU_matrix_bind(shader->interface); + GPU_shader_set_srgb_uniform(shader->interface); +} + +void GPU_shader_unbind(void) +{ + glUseProgram(0); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform feedback + * \{ */ + +bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id) +{ + if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) { + return false; + } + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id); + + switch (shader->feedback_transform_type) { + case GPU_SHADER_TFB_POINTS: + glBeginTransformFeedback(GL_POINTS); + return true; + case GPU_SHADER_TFB_LINES: + glBeginTransformFeedback(GL_LINES); + return true; + case GPU_SHADER_TFB_TRIANGLES: + glBeginTransformFeedback(GL_TRIANGLES); + return true; + default: + return false; + } +} + +void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) +{ + glEndTransformFeedback(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms / Resource location + * \{ */ + +int GPU_shader_get_uniform(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name); + return uniform ? uniform->location : -1; +} + +int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin) +{ + BLI_assert(shader && shader->program); + return GPU_shaderinterface_uniform_builtin(shader->interface, + static_cast<GPUUniformBuiltin>(builtin)); +} + +int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) +{ + BLI_assert(shader && shader->program); + return GPU_shaderinterface_block_builtin(shader->interface, + static_cast<GPUUniformBlockBuiltin>(builtin)); +} + +int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + return ubo ? ubo->location : -1; +} + +int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + return ubo ? ubo->binding : -1; +} + +int GPU_shader_get_texture_binding(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name); + return tex ? tex->binding : -1; +} + +int GPU_shader_get_attribute(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name); + return attr ? attr->location : -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Getters + * \{ */ + +/* Clement : Temp */ +int GPU_shader_get_program(GPUShader *shader) +{ + return (int)shader->program; +} + +char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len) +{ + BLI_assert(GLEW_ARB_get_program_binary); + char *r_binary; + int binary_len = 0; + + glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len); + r_binary = (char *)MEM_mallocN(binary_len, __func__); + glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary); + + if (r_binary_len) { + *r_binary_len = binary_len; + } + + return r_binary; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms setters + * \{ */ + +void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) +{ + if (location == -1) { + return; + } + + glUniform1f(location, value); +} + +void GPU_shader_uniform_vector( + GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) +{ + if (location == -1 || value == NULL) { + return; + } + + switch (length) { + case 1: + glUniform1fv(location, arraysize, value); + break; + case 2: + glUniform2fv(location, arraysize, value); + break; + case 3: + glUniform3fv(location, arraysize, value); + break; + case 4: + glUniform4fv(location, arraysize, value); + break; + case 9: + glUniformMatrix3fv(location, arraysize, 0, value); + break; + case 16: + glUniformMatrix4fv(location, arraysize, 0, value); + break; + default: + BLI_assert(0); + break; + } +} + +void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) +{ + if (location == -1) { + return; + } + + glUniform1i(location, value); +} + +void GPU_shader_uniform_vector_int( + GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) +{ + if (location == -1) { + return; + } + + switch (length) { + case 1: + glUniform1iv(location, arraysize, value); + break; + case 2: + glUniform2iv(location, arraysize, value); + break; + case 3: + glUniform3iv(location, arraysize, value); + break; + case 4: + glUniform4iv(location, arraysize, value); + break; + default: + BLI_assert(0); + break; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name sRGB Rendering Workaround + * + * The viewport overlay frame-buffer is sRGB and will expect shaders to output display referred + * Linear colors. But other frame-buffers (i.e: the area frame-buffers) are not sRGB and require + * the shader output color to be in sRGB space + * (assumed display encoded color-space as the time of writing). + * For this reason we have a uniform to switch the transform on and off depending on the current + * frame-buffer color-space. + * \{ */ + +static int g_shader_builtin_srgb_transform = 0; + +void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface) +{ + int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM); + if (loc != -1) { + glUniform1i(loc, g_shader_builtin_srgb_transform); + } +} + +void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear) +{ + g_shader_builtin_srgb_transform = use_srgb_to_linear; +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 9ea798e5669..9c0692b76e2 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -153,11 +153,6 @@ const struct GPUShaderConfigData GPU_shader_cfg_data[GPU_SHADER_CFG_LEN] = { /* cache of built-in shaders (each is created on first use) */ static GPUShader *builtin_shaders[GPU_SHADER_CFG_LEN][GPU_SHADER_BUILTIN_LEN] = {{NULL}}; -static int g_shader_builtin_srgb_transform = 0; - -#ifndef NDEBUG -static uint g_shaderid = 0; -#endif typedef struct { const char *vert; @@ -168,727 +163,6 @@ typedef struct { const char *defs; } GPUShaderStages; -static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) -{ - int line = 1; - - fprintf(stderr, "GPUShader: %s error:\n", task); - - for (int i = 0; i < totcode; i++) { - const char *c, *pos, *end = code[i] + strlen(code[i]); - - if (G.debug & G_DEBUG) { - fprintf(stderr, "===== shader string %d ====\n", i + 1); - - c = code[i]; - while ((c < end) && (pos = strchr(c, '\n'))) { - fprintf(stderr, "%2d ", line); - fwrite(c, (pos + 1) - c, 1, stderr); - c = pos + 1; - line++; - } - - fprintf(stderr, "%s", c); - } - } - - fprintf(stderr, "%s\n", log); -} - -static const char *gpu_shader_version(void) -{ - return "#version 330\n"; -} - -static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) -{ - /* enable extensions for features that are not part of our base GLSL version - * don't use an extension for something already available! - */ - - if (GLEW_ARB_texture_gather) { - /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather - * is reported to be supported but yield a compile error (see T55802). */ - if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) { - strcat(defines, "#extension GL_ARB_texture_gather: enable\n"); - - /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the - * shader so double check the preprocessor define (see T56544). */ - if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) { - strcat(defines, "#ifdef GL_ARB_texture_gather\n"); - strcat(defines, "# define GPU_ARB_texture_gather\n"); - strcat(defines, "#endif\n"); - } - else { - strcat(defines, "#define GPU_ARB_texture_gather\n"); - } - } - } - if (GLEW_ARB_texture_query_lod) { - /* a #version 400 feature, but we use #version 330 maximum so use extension */ - strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); - } - if (GLEW_ARB_shader_draw_parameters) { - strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n"); - } - if (GPU_arb_texture_cube_map_array_is_supported()) { - strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n"); - strcat(defines, "#define GPU_ARB_texture_cube_map_array\n"); - } -} - -static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) -{ - /* some useful defines to detect GPU type */ - if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_ATI\n"); - if (GPU_crappy_amd_driver()) { - strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n"); - } - } - else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_NVIDIA\n"); - } - else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_INTEL\n"); - } - - /* some useful defines to detect OS type */ - if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_WIN\n"); - } - else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_MAC\n"); - } - else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_UNIX\n"); - } - - float derivatives_factors[2]; - GPU_get_dfdy_factors(derivatives_factors); - if (derivatives_factors[0] == 1.0f) { - strcat(defines, "#define DFDX_SIGN 1.0\n"); - } - else { - strcat(defines, "#define DFDX_SIGN -1.0\n"); - } - - if (derivatives_factors[1] == 1.0f) { - strcat(defines, "#define DFDY_SIGN 1.0\n"); - } - else { - strcat(defines, "#define DFDY_SIGN -1.0\n"); - } -} - -GPUShader *GPU_shader_create(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - const char *shname) -{ - return GPU_shader_create_ex( - vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname); -} - -GPUShader *GPU_shader_create_from_python(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines) -{ - char *libcodecat = NULL; - - if (libcode == NULL) { - libcode = datatoc_gpu_shader_colorspace_lib_glsl; - } - else { - libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); - } - - GPUShader *sh = GPU_shader_create_ex( - vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL); - - MEM_SAFE_FREE(libcodecat); - return sh; -} - -GPUShader *GPU_shader_load_from_binary(const char *binary, - const int binary_format, - const int binary_len, - const char *shname) -{ - BLI_assert(GL_ARB_get_program_binary); - int success; - int program = glCreateProgram(); - - glProgramBinary(program, binary_format, binary, binary_len); - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success) { - glUseProgram(program); - - GPUShader *shader = MEM_callocN(sizeof(*shader), __func__); - shader->interface = GPU_shaderinterface_create(program); - shader->program = program; - -#ifndef NDEBUG - BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); -#else - UNUSED_VARS(shname); -#endif - - return shader; - } - - glDeleteProgram(program); - return NULL; -} - -#define DEBUG_SHADER_NONE "" -#define DEBUG_SHADER_VERTEX "vert" -#define DEBUG_SHADER_FRAGMENT "frag" -#define DEBUG_SHADER_GEOMETRY "geom" - -/** - * Dump GLSL shaders to disk - * - * This is used for profiling shader performance externally and debug if shader code is correct. - * If called with no code, it simply bumps the shader index, so different shaders for the same - * program share the same index. - */ -static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension) -{ - if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) { - return; - } - - /* We use the same shader index for shaders in the same program. - * So we call this function once before calling for the individual shaders. */ - static int shader_index = 0; - if (code == NULL) { - shader_index++; - BLI_assert(STREQ(DEBUG_SHADER_NONE, extension)); - return; - } - - /* Determine the full path of the new shader. */ - char shader_path[FILE_MAX]; - - char file_name[512] = {'\0'}; - sprintf(file_name, "%04d.%s", shader_index, extension); - - BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name); - - /* Write shader to disk. */ - FILE *f = fopen(shader_path, "w"); - if (f == NULL) { - printf("Error writing to file: %s\n", shader_path); - } - for (int j = 0; j < num_shaders; j++) { - fprintf(f, "%s", code[j]); - } - fclose(f); - printf("Shader file written to disk: %s\n", shader_path); -} - -GPUShader *GPU_shader_create_ex(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - const eGPUShaderTFBType tf_type, - const char **tf_names, - const int tf_count, - const char *shname) -{ - GLint status; - GLchar log[5000]; - GLsizei length = 0; - GPUShader *shader; - char standard_defines[MAX_DEFINE_LENGTH] = ""; - char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; - - shader = MEM_callocN(sizeof(GPUShader), "GPUShader"); - gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE); - -#ifndef NDEBUG - BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); -#else - UNUSED_VARS(shname); -#endif - - /* At least a vertex shader and a fragment shader are required. */ - BLI_assert((fragcode != NULL) && (vertexcode != NULL)); - - if (vertexcode) { - shader->vertex = glCreateShader(GL_VERTEX_SHADER); - } - if (fragcode) { - shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); - } - if (geocode) { - shader->geometry = glCreateShader(GL_GEOMETRY_SHADER); - } - - shader->program = glCreateProgram(); - - if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) || - (geocode && !shader->geometry)) { - fprintf(stderr, "GPUShader, object creation failed.\n"); - GPU_shader_free(shader); - return NULL; - } - - gpu_shader_standard_defines(standard_defines); - gpu_shader_standard_extensions(standard_extensions); - - if (vertexcode) { - const char *source[6]; - /* custom limit, may be too small, beware */ - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = - "#define GPU_VERTEX_SHADER\n" - "#define IN_OUT out\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (defines) { - source[num_source++] = defines; - } - source[num_source++] = vertexcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX); - - glAttachShader(shader->program, shader->vertex); - glShaderSource(shader->vertex, num_source, source, NULL); - - glCompileShader(shader->vertex); - glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } - } - - if (fragcode) { - const char *source[7]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = - "#define GPU_FRAGMENT_SHADER\n" - "#define IN_OUT in\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (defines) { - source[num_source++] = defines; - } - if (libcode) { - source[num_source++] = libcode; - } - source[num_source++] = fragcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT); - - glAttachShader(shader->program, shader->fragment); - glShaderSource(shader->fragment, num_source, source, NULL); - - glCompileShader(shader->fragment); - glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } - } - - if (geocode) { - const char *source[6]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = "#define GPU_GEOMETRY_SHADER\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (defines) { - source[num_source++] = defines; - } - source[num_source++] = geocode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY); - - glAttachShader(shader->program, shader->geometry); - glShaderSource(shader->geometry, num_source, source, NULL); - - glCompileShader(shader->geometry); - glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } - } - - if (tf_names != NULL) { - glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); - /* Primitive type must be setup */ - BLI_assert(tf_type != GPU_SHADER_TFB_NONE); - shader->feedback_transform_type = tf_type; - } - - glLinkProgram(shader->program); - glGetProgramiv(shader->program, GL_LINK_STATUS, &status); - if (!status) { - glGetProgramInfoLog(shader->program, sizeof(log), &length, log); - /* print attached shaders in pipeline order */ - if (vertexcode) { - shader_print_errors("linking", log, &vertexcode, 1); - } - if (geocode) { - shader_print_errors("linking", log, &geocode, 1); - } - if (libcode) { - shader_print_errors("linking", log, &libcode, 1); - } - if (fragcode) { - shader_print_errors("linking", log, &fragcode, 1); - } - - GPU_shader_free(shader); - return NULL; - } - - glUseProgram(shader->program); - shader->interface = GPU_shaderinterface_create(shader->program); - - return shader; -} - -#undef DEBUG_SHADER_GEOMETRY -#undef DEBUG_SHADER_FRAGMENT -#undef DEBUG_SHADER_VERTEX -#undef DEBUG_SHADER_NONE - -static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc) -{ - bool is_alloc = false; - if (str_arr == NULL) { - *r_is_alloc = false; - return NULL; - } - /* Skip empty strings (avoid alloc if we can). */ - while (str_arr[0] && str_arr[0][0] == '\0') { - str_arr++; - } - int i; - for (i = 0; str_arr[i]; i++) { - if (i != 0 && str_arr[i][0] != '\0') { - is_alloc = true; - } - } - *r_is_alloc = is_alloc; - if (is_alloc) { - return BLI_string_join_arrayN(str_arr, i); - } - else { - return str_arr[0]; - } -} - -/** - * Use via #GPU_shader_create_from_arrays macro (avoids passing in param). - * - * Similar to #DRW_shader_create_with_lib with the ability to include libs for each type of shader. - * - * It has the advantage that each item can be conditionally included - * without having to build the string inline, then free it. - * - * \param params: NULL terminated arrays of strings. - * - * Example: - * \code{.c} - * sh = GPU_shader_create_from_arrays({ - * .vert = (const char *[]){shader_lib_glsl, shader_vert_glsl, NULL}, - * .geom = (const char *[]){shader_geom_glsl, NULL}, - * .frag = (const char *[]){shader_frag_glsl, NULL}, - * .defs = (const char *[]){"#define DEFINE\n", test ? "#define OTHER_DEFINE\n" : "", NULL}, - * }); - * \endcode - */ -struct GPUShader *GPU_shader_create_from_arrays_impl( - const struct GPU_ShaderCreateFromArray_Params *params) -{ - struct { - const char *str; - bool is_alloc; - } str_dst[4] = {{0}}; - const char **str_src[4] = {params->vert, params->frag, params->geom, params->defs}; - - for (int i = 0; i < ARRAY_SIZE(str_src); i++) { - str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc); - } - - GPUShader *sh = GPU_shader_create( - str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__); - - for (int i = 0; i < ARRAY_SIZE(str_dst); i++) { - if (str_dst[i].is_alloc) { - MEM_freeN((void *)str_dst[i].str); - } - } - return sh; -} - -void GPU_shader_bind(GPUShader *shader) -{ - BLI_assert(shader && shader->program); - - glUseProgram(shader->program); - GPU_matrix_bind(shader->interface); - GPU_shader_set_srgb_uniform(shader->interface); -} - -void GPU_shader_unbind(void) -{ - glUseProgram(0); -} - -bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id) -{ - if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) { - return false; - } - - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id); - - switch (shader->feedback_transform_type) { - case GPU_SHADER_TFB_POINTS: - glBeginTransformFeedback(GL_POINTS); - return true; - case GPU_SHADER_TFB_LINES: - glBeginTransformFeedback(GL_LINES); - return true; - case GPU_SHADER_TFB_TRIANGLES: - glBeginTransformFeedback(GL_TRIANGLES); - return true; - default: - return false; - } -} - -void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) -{ - glEndTransformFeedback(); -} - -void GPU_shader_free(GPUShader *shader) -{ -#if 0 /* Would be nice to have, but for now the Deferred compilation \ - * does not have a GPUContext. */ - BLI_assert(GPU_context_active_get() != NULL); -#endif - BLI_assert(shader); - - if (shader->vertex) { - glDeleteShader(shader->vertex); - } - if (shader->geometry) { - glDeleteShader(shader->geometry); - } - if (shader->fragment) { - glDeleteShader(shader->fragment); - } - if (shader->program) { - glDeleteProgram(shader->program); - } - - if (shader->interface) { - GPU_shaderinterface_discard(shader->interface); - } - - MEM_freeN(shader); -} - -int GPU_shader_get_uniform(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name); - return uniform ? uniform->location : -1; -} - -int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin) -{ - BLI_assert(shader && shader->program); - return GPU_shaderinterface_uniform_builtin(shader->interface, builtin); -} - -int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) -{ - BLI_assert(shader && shader->program); - return GPU_shaderinterface_block_builtin(shader->interface, builtin); -} - -int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); - return ubo ? ubo->location : -1; -} - -int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); - return ubo ? ubo->binding : -1; -} - -int GPU_shader_get_texture_binding(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name); - return tex ? tex->binding : -1; -} - -void *GPU_shader_get_interface(GPUShader *shader) -{ - return shader->interface; -} - -/* Clement : Temp */ -int GPU_shader_get_program(GPUShader *shader) -{ - return (int)shader->program; -} - -void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) -{ - if (location == -1) { - return; - } - - glUniform1f(location, value); -} - -void GPU_shader_uniform_vector( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) -{ - if (location == -1 || value == NULL) { - return; - } - - switch (length) { - case 1: - glUniform1fv(location, arraysize, value); - break; - case 2: - glUniform2fv(location, arraysize, value); - break; - case 3: - glUniform3fv(location, arraysize, value); - break; - case 4: - glUniform4fv(location, arraysize, value); - break; - case 9: - glUniformMatrix3fv(location, arraysize, 0, value); - break; - case 16: - glUniformMatrix4fv(location, arraysize, 0, value); - break; - default: - BLI_assert(0); - break; - } -} - -void GPU_shader_uniform_vector_int( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) -{ - if (location == -1) { - return; - } - - switch (length) { - case 1: - glUniform1iv(location, arraysize, value); - break; - case 2: - glUniform2iv(location, arraysize, value); - break; - case 3: - glUniform3iv(location, arraysize, value); - break; - case 4: - glUniform4iv(location, arraysize, value); - break; - default: - BLI_assert(0); - break; - } -} - -void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) -{ - if (location == -1) { - return; - } - - glUniform1i(location, value); -} - -void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface) -{ - int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM); - if (loc != -1) { - glUniform1i(loc, g_shader_builtin_srgb_transform); - } -} - -int GPU_shader_get_attribute(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name); - return attr ? attr->location : -1; -} - -char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len) -{ - BLI_assert(GLEW_ARB_get_program_binary); - char *r_binary; - int binary_len = 0; - - glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len); - r_binary = MEM_mallocN(binary_len, __func__); - glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary); - - if (r_binary_len) { - *r_binary_len = binary_len; - } - - return r_binary; -} - -void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear) -{ - g_shader_builtin_srgb_transform = use_srgb_to_linear; -} - static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { [GPU_SHADER_TEXT] = { @@ -1242,6 +516,7 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, return *sh_p; } + GPUShader *GPU_shader_get_builtin_shader(eGPUBuiltinShader shader) { return GPU_shader_get_builtin_shader_with_config(shader, GPU_SHADER_CFG_DEFAULT); diff --git a/source/blender/gpu/intern/gpu_shader_interface.c b/source/blender/gpu/intern/gpu_shader_interface.cc index 9d9f98c6bb0..4586b1ce814 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.c +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -47,47 +47,66 @@ static const char *BuiltinUniform_name(GPUUniformBuiltin u) { - static const char *names[] = { - [GPU_UNIFORM_MODEL] = "ModelMatrix", - [GPU_UNIFORM_VIEW] = "ViewMatrix", - [GPU_UNIFORM_MODELVIEW] = "ModelViewMatrix", - [GPU_UNIFORM_PROJECTION] = "ProjectionMatrix", - [GPU_UNIFORM_VIEWPROJECTION] = "ViewProjectionMatrix", - [GPU_UNIFORM_MVP] = "ModelViewProjectionMatrix", - - [GPU_UNIFORM_MODEL_INV] = "ModelMatrixInverse", - [GPU_UNIFORM_VIEW_INV] = "ViewMatrixInverse", - [GPU_UNIFORM_MODELVIEW_INV] = "ModelViewMatrixInverse", - [GPU_UNIFORM_PROJECTION_INV] = "ProjectionMatrixInverse", - [GPU_UNIFORM_VIEWPROJECTION_INV] = "ViewProjectionMatrixInverse", - - [GPU_UNIFORM_NORMAL] = "NormalMatrix", - [GPU_UNIFORM_ORCO] = "OrcoTexCoFactors", - [GPU_UNIFORM_CLIPPLANES] = "WorldClipPlanes", - - [GPU_UNIFORM_COLOR] = "color", - [GPU_UNIFORM_BASE_INSTANCE] = "baseInstance", - [GPU_UNIFORM_RESOURCE_CHUNK] = "resourceChunk", - [GPU_UNIFORM_RESOURCE_ID] = "resourceId", - [GPU_UNIFORM_SRGB_TRANSFORM] = "srgbTarget", - - [GPU_NUM_UNIFORMS] = NULL, - }; - - return names[u]; + switch (u) { + case GPU_UNIFORM_MODEL: + return "ModelMatrix"; + case GPU_UNIFORM_VIEW: + return "ViewMatrix"; + case GPU_UNIFORM_MODELVIEW: + return "ModelViewMatrix"; + case GPU_UNIFORM_PROJECTION: + return "ProjectionMatrix"; + case GPU_UNIFORM_VIEWPROJECTION: + return "ViewProjectionMatrix"; + case GPU_UNIFORM_MVP: + return "ModelViewProjectionMatrix"; + + case GPU_UNIFORM_MODEL_INV: + return "ModelMatrixInverse"; + case GPU_UNIFORM_VIEW_INV: + return "ViewMatrixInverse"; + case GPU_UNIFORM_MODELVIEW_INV: + return "ModelViewMatrixInverse"; + case GPU_UNIFORM_PROJECTION_INV: + return "ProjectionMatrixInverse"; + case GPU_UNIFORM_VIEWPROJECTION_INV: + return "ViewProjectionMatrixInverse"; + + case GPU_UNIFORM_NORMAL: + return "NormalMatrix"; + case GPU_UNIFORM_ORCO: + return "OrcoTexCoFactors"; + case GPU_UNIFORM_CLIPPLANES: + return "WorldClipPlanes"; + + case GPU_UNIFORM_COLOR: + return "color"; + case GPU_UNIFORM_BASE_INSTANCE: + return "baseInstance"; + case GPU_UNIFORM_RESOURCE_CHUNK: + return "resourceChunk"; + case GPU_UNIFORM_RESOURCE_ID: + return "resourceId"; + case GPU_UNIFORM_SRGB_TRANSFORM: + return "srgbTarget"; + + default: + return NULL; + } } static const char *BuiltinUniformBlock_name(GPUUniformBlockBuiltin u) { - static const char *names[] = { - [GPU_UNIFORM_BLOCK_VIEW] = "viewBlock", - [GPU_UNIFORM_BLOCK_MODEL] = "modelBlock", - [GPU_UNIFORM_BLOCK_INFO] = "infoBlock", - - [GPU_NUM_UNIFORM_BLOCKS] = NULL, - }; - - return names[u]; + switch (u) { + case GPU_UNIFORM_BLOCK_VIEW: + return "viewBlock"; + case GPU_UNIFORM_BLOCK_MODEL: + return "modelBlock"; + case GPU_UNIFORM_BLOCK_INFO: + return "infoBlock"; + default: + return NULL; + } } GPU_INLINE bool match(const char *a, const char *b) @@ -273,7 +292,7 @@ GPUShaderInterface *GPU_shaderinterface_create(int32_t program) /* Bit set to true if uniform comes from a uniform block. */ BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__); /* Set uniforms from block for exclusion. */ - GLint *ubo_uni_ids = MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); + GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); for (int i = 0; i < ubo_len; i++) { GLint ubo_uni_len; glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); @@ -291,16 +310,18 @@ GPUShaderInterface *GPU_shaderinterface_create(int32_t program) int input_tot_len = attr_len + ubo_len + uniform_len; size_t interface_size = sizeof(GPUShaderInterface) + sizeof(GPUShaderInput) * input_tot_len; - GPUShaderInterface *shaderface = MEM_callocN(interface_size, "GPUShaderInterface"); + GPUShaderInterface *shaderface = (GPUShaderInterface *)MEM_callocN(interface_size, + "GPUShaderInterface"); shaderface->attribute_len = attr_len; shaderface->ubo_len = ubo_len; shaderface->uniform_len = uniform_len; - shaderface->name_buffer = MEM_mallocN(name_buffer_len, "name_buffer"); + shaderface->name_buffer = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); GPUShaderInput *inputs = shaderface->inputs; /* Temp buffer. */ int input_tmp_len = max_iii(attr_len, ubo_len, uniform_len); - GPUShaderInput *inputs_tmp = MEM_mallocN(sizeof(GPUShaderInput) * input_tmp_len, "name_buffer"); + GPUShaderInput *inputs_tmp = (GPUShaderInput *)MEM_mallocN( + sizeof(GPUShaderInput) * input_tmp_len, "name_buffer"); /* Attributes */ shaderface->enabled_attr_mask = 0; @@ -366,27 +387,29 @@ GPUShaderInterface *GPU_shaderinterface_create(int32_t program) sort_input_list(inputs, inputs_tmp, shaderface->uniform_len); /* Builtin Uniforms */ - for (GPUUniformBuiltin u = 0; u < GPU_NUM_UNIFORMS; u++) { + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { + GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); shaderface->builtins[u] = glGetUniformLocation(program, BuiltinUniform_name(u)); } /* Builtin Uniforms Blocks */ - for (GPUUniformBlockBuiltin u = 0; u < GPU_NUM_UNIFORM_BLOCKS; u++) { + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) { + GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int); const GPUShaderInput *block = GPU_shaderinterface_ubo(shaderface, BuiltinUniformBlock_name(u)); shaderface->builtin_blocks[u] = (block != NULL) ? block->binding : -1; } /* Batches ref buffer */ shaderface->batches_len = GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = MEM_callocN(shaderface->batches_len * sizeof(GPUBatch *), - "GPUShaderInterface batches"); + shaderface->batches = (GPUBatch **)MEM_callocN(shaderface->batches_len * sizeof(GPUBatch *), + "GPUShaderInterface batches"); MEM_freeN(uniforms_from_blocks); MEM_freeN(inputs_tmp); /* Resize name buffer to save some memory. */ if (name_buffer_offset < name_buffer_len) { - shaderface->name_buffer = MEM_reallocN(shaderface->name_buffer, name_buffer_offset); + shaderface->name_buffer = (char *)MEM_reallocN(shaderface->name_buffer, name_buffer_offset); } #if DEBUG_SHADER_INTERFACE @@ -501,8 +524,8 @@ void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *shaderface, GPUBatch /* Not enough place, realloc the array. */ i = shaderface->batches_len; shaderface->batches_len += GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = MEM_recallocN(shaderface->batches, - sizeof(GPUBatch *) * shaderface->batches_len); + shaderface->batches = (GPUBatch **)MEM_recallocN(shaderface->batches, + sizeof(GPUBatch *) * shaderface->batches_len); } shaderface->batches[i] = batch; } diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/gpu/intern/gpu_shader_private.h index e04d8655421..0f89fbda737 100644 --- a/source/blender/gpu/intern/gpu_shader_private.h +++ b/source/blender/gpu/intern/gpu_shader_private.h @@ -20,9 +20,12 @@ #pragma once -#include "GPU_glew.h" #include "GPU_shader_interface.h" +#ifdef __cplusplus +extern "C" { +#endif + struct GPUShader { /** Handle for full program (links shader stages below). */ GLuint program; @@ -44,4 +47,8 @@ struct GPUShader { }; /* XXX do not use it. Special hack to use OCIO with batch API. */ -void immGetProgram(GLuint *program, GPUShaderInterface **shaderface); +GPUShader *immGetShader(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_state.c b/source/blender/gpu/intern/gpu_state.cc index bd7aff9772b..794c7a3eb97 100644 --- a/source/blender/gpu/intern/gpu_state.c +++ b/source/blender/gpu/intern/gpu_state.cc @@ -268,6 +268,12 @@ void GPU_clip_distances(int distances_new) distances_enabled = distances_new; } +bool GPU_mipmap_enabled(void) +{ + /* TODO(fclem) this used to be a userdef option. */ + return true; +} + /** \name GPU Push/Pop State * \{ */ @@ -276,33 +282,18 @@ void GPU_clip_distances(int distances_new) typedef struct { eGPUAttrMask mask; - /* GL_ENABLE_BIT */ + /* GL_BLEND_BIT */ uint is_blend : 1; - uint is_cull_face : 1; - uint is_depth_test : 1; - /* uint is_lighting : 1; */ /* UNUSED */ - uint is_line_smooth : 1; - uint is_color_logic_op : 1; - uint is_multisample : 1; - uint is_polygon_offset_line : 1; - uint is_polygon_offset_fill : 1; - uint is_polygon_smooth : 1; - uint is_sample_alpha_to_coverage : 1; - uint is_scissor_test : 1; - uint is_stencil_test : 1; - uint is_framebuffer_srgb : 1; - - bool is_clip_plane[6]; /* GL_DEPTH_BUFFER_BIT */ - /* uint is_depth_test : 1; */ + uint is_depth_test : 1; int depth_func; double depth_clear_value; bool depth_write_mask; /* GL_SCISSOR_BIT */ int scissor_box[4]; - /* uint is_scissor_test : 1; */ + uint is_scissor_test : 1; /* GL_VIEWPORT_BIT */ int viewport[4]; @@ -315,7 +306,8 @@ typedef struct { } GPUAttrStack; static GPUAttrStack state = { - .top = 0, + {}, + 0, }; #define AttrStack state @@ -338,26 +330,6 @@ void gpuPushAttr(eGPUAttrMask mask) glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attr.depth_write_mask); } - if ((mask & GPU_ENABLE_BIT) != 0) { - Attr.is_blend = glIsEnabled(GL_BLEND); - - for (int i = 0; i < 6; i++) { - Attr.is_clip_plane[i] = glIsEnabled(GL_CLIP_PLANE0 + i); - } - - Attr.is_cull_face = glIsEnabled(GL_CULL_FACE); - Attr.is_depth_test = glIsEnabled(GL_DEPTH_TEST); - Attr.is_line_smooth = glIsEnabled(GL_LINE_SMOOTH); - Attr.is_color_logic_op = glIsEnabled(GL_COLOR_LOGIC_OP); - Attr.is_multisample = glIsEnabled(GL_MULTISAMPLE); - Attr.is_polygon_offset_line = glIsEnabled(GL_POLYGON_OFFSET_LINE); - Attr.is_polygon_offset_fill = glIsEnabled(GL_POLYGON_OFFSET_FILL); - Attr.is_polygon_smooth = glIsEnabled(GL_POLYGON_SMOOTH); - Attr.is_sample_alpha_to_coverage = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE); - Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - Attr.is_stencil_test = glIsEnabled(GL_STENCIL_TEST); - } - if ((mask & GPU_SCISSOR_BIT) != 0) { Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attr.scissor_box); @@ -366,7 +338,6 @@ void gpuPushAttr(eGPUAttrMask mask) if ((mask & GPU_VIEWPORT_BIT) != 0) { glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attr.near_far); glGetIntegerv(GL_VIEWPORT, (GLint *)&Attr.viewport); - Attr.is_framebuffer_srgb = glIsEnabled(GL_FRAMEBUFFER_SRGB); } if ((mask & GPU_BLEND_BIT) != 0) { @@ -401,30 +372,9 @@ void gpuPopAttr(void) glDepthMask(Attr.depth_write_mask); } - if ((mask & GPU_ENABLE_BIT) != 0) { - restore_mask(GL_BLEND, Attr.is_blend); - - for (int i = 0; i < 6; i++) { - restore_mask(GL_CLIP_PLANE0 + i, Attr.is_clip_plane[i]); - } - - restore_mask(GL_CULL_FACE, Attr.is_cull_face); - restore_mask(GL_DEPTH_TEST, Attr.is_depth_test); - restore_mask(GL_LINE_SMOOTH, Attr.is_line_smooth); - restore_mask(GL_COLOR_LOGIC_OP, Attr.is_color_logic_op); - restore_mask(GL_MULTISAMPLE, Attr.is_multisample); - restore_mask(GL_POLYGON_OFFSET_LINE, Attr.is_polygon_offset_line); - restore_mask(GL_POLYGON_OFFSET_FILL, Attr.is_polygon_offset_fill); - restore_mask(GL_POLYGON_SMOOTH, Attr.is_polygon_smooth); - restore_mask(GL_SAMPLE_ALPHA_TO_COVERAGE, Attr.is_sample_alpha_to_coverage); - restore_mask(GL_SCISSOR_TEST, Attr.is_scissor_test); - restore_mask(GL_STENCIL_TEST, Attr.is_stencil_test); - } - if ((mask & GPU_VIEWPORT_BIT) != 0) { glViewport(Attr.viewport[0], Attr.viewport[1], Attr.viewport[2], Attr.viewport[3]); glDepthRange(Attr.near_far[0], Attr.near_far[1]); - restore_mask(GL_FRAMEBUFFER_SRGB, Attr.is_framebuffer_srgb); } if ((mask & GPU_SCISSOR_BIT) != 0) { diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.cc index 1e81f6e86a4..49812abe605 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "DNA_image_types.h" +#include "DNA_userdef_types.h" #include "BLI_blenlib.h" #include "BLI_math_base.h" @@ -36,7 +37,6 @@ #include "GPU_batch.h" #include "GPU_context.h" #include "GPU_debug.h" -#include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_glew.h" @@ -45,6 +45,16 @@ #include "gpu_context_private.h" +#define WARN_NOT_BOUND(_tex) \ + { \ + if (_tex->number == -1) { \ + fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ + BLI_assert(0); \ + return; \ + } \ + } \ + ((void)0) + static struct GPUTextureGlobal { /** Texture used in place of invalid textures (not loaded correctly, missing). */ GPUTexture *invalid_tex_1D; @@ -71,6 +81,8 @@ typedef enum eGPUTextureFormatFlag { GPU_FORMAT_ARRAY = (1 << 14), } eGPUTextureFormatFlag; +ENUM_OPERATORS(eGPUTextureFormatFlag) + /* GPUTexture */ struct GPUTexture { int w, h, d; /* width/height/depth */ @@ -103,7 +115,7 @@ static void gpu_texture_framebuffer_ensure(GPUTexture *tex); /* ------ Memory Management ------- */ /* Records every texture allocation / free * to estimate the Texture Pool Memory consumption */ -static uint memory_usage; +static uint memory_usage = 0; static uint gpu_texture_memory_footprint_compute(GPUTexture *tex) { @@ -161,54 +173,58 @@ uint GPU_texture_memory_usage_get(void) static const char *gl_enum_to_str(GLenum e) { -#define ENUM_TO_STRING(e) [GL_##e] = STRINGIFY_ARG(e) - static const char *enum_strings[] = { - ENUM_TO_STRING(TEXTURE_CUBE_MAP), - ENUM_TO_STRING(TEXTURE_CUBE_MAP_ARRAY), - ENUM_TO_STRING(TEXTURE_2D), - ENUM_TO_STRING(TEXTURE_2D_ARRAY), - ENUM_TO_STRING(TEXTURE_1D), - ENUM_TO_STRING(TEXTURE_1D_ARRAY), - ENUM_TO_STRING(TEXTURE_3D), - ENUM_TO_STRING(TEXTURE_2D_MULTISAMPLE), - ENUM_TO_STRING(RGBA32F), - ENUM_TO_STRING(RGBA16F), - ENUM_TO_STRING(RGBA16UI), - ENUM_TO_STRING(RGBA16I), - ENUM_TO_STRING(RGBA16), - ENUM_TO_STRING(RGBA8UI), - ENUM_TO_STRING(RGBA8I), - ENUM_TO_STRING(RGBA8), - ENUM_TO_STRING(RGB16F), - ENUM_TO_STRING(RG32F), - ENUM_TO_STRING(RG16F), - ENUM_TO_STRING(RG16UI), - ENUM_TO_STRING(RG16I), - ENUM_TO_STRING(RG16), - ENUM_TO_STRING(RG8UI), - ENUM_TO_STRING(RG8I), - ENUM_TO_STRING(RG8), - ENUM_TO_STRING(R8UI), - ENUM_TO_STRING(R8I), - ENUM_TO_STRING(R8), - ENUM_TO_STRING(R32F), - ENUM_TO_STRING(R32UI), - ENUM_TO_STRING(R32I), - ENUM_TO_STRING(R16F), - ENUM_TO_STRING(R16UI), - ENUM_TO_STRING(R16I), - ENUM_TO_STRING(R16), - ENUM_TO_STRING(R11F_G11F_B10F), - ENUM_TO_STRING(SRGB8_ALPHA8), - ENUM_TO_STRING(DEPTH24_STENCIL8), - ENUM_TO_STRING(DEPTH32F_STENCIL8), - ENUM_TO_STRING(DEPTH_COMPONENT32F), - ENUM_TO_STRING(DEPTH_COMPONENT24), - ENUM_TO_STRING(DEPTH_COMPONENT16), +#define ENUM_TO_STRING(e) \ + case GL_##e: { \ + return STRINGIFY_ARG(e); \ + } + + switch (e) { + ENUM_TO_STRING(TEXTURE_CUBE_MAP); + ENUM_TO_STRING(TEXTURE_CUBE_MAP_ARRAY); + ENUM_TO_STRING(TEXTURE_2D); + ENUM_TO_STRING(TEXTURE_2D_ARRAY); + ENUM_TO_STRING(TEXTURE_1D); + ENUM_TO_STRING(TEXTURE_1D_ARRAY); + ENUM_TO_STRING(TEXTURE_3D); + ENUM_TO_STRING(TEXTURE_2D_MULTISAMPLE); + ENUM_TO_STRING(RGBA32F); + ENUM_TO_STRING(RGBA16F); + ENUM_TO_STRING(RGBA16UI); + ENUM_TO_STRING(RGBA16I); + ENUM_TO_STRING(RGBA16); + ENUM_TO_STRING(RGBA8UI); + ENUM_TO_STRING(RGBA8I); + ENUM_TO_STRING(RGBA8); + ENUM_TO_STRING(RGB16F); + ENUM_TO_STRING(RG32F); + ENUM_TO_STRING(RG16F); + ENUM_TO_STRING(RG16UI); + ENUM_TO_STRING(RG16I); + ENUM_TO_STRING(RG16); + ENUM_TO_STRING(RG8UI); + ENUM_TO_STRING(RG8I); + ENUM_TO_STRING(RG8); + ENUM_TO_STRING(R8UI); + ENUM_TO_STRING(R8I); + ENUM_TO_STRING(R8); + ENUM_TO_STRING(R32F); + ENUM_TO_STRING(R32UI); + ENUM_TO_STRING(R32I); + ENUM_TO_STRING(R16F); + ENUM_TO_STRING(R16UI); + ENUM_TO_STRING(R16I); + ENUM_TO_STRING(R16); + ENUM_TO_STRING(R11F_G11F_B10F); + ENUM_TO_STRING(SRGB8_ALPHA8); + ENUM_TO_STRING(DEPTH24_STENCIL8); + ENUM_TO_STRING(DEPTH32F_STENCIL8); + ENUM_TO_STRING(DEPTH_COMPONENT32F); + ENUM_TO_STRING(DEPTH_COMPONENT24); + ENUM_TO_STRING(DEPTH_COMPONENT16); + default: + return "Unkown enum"; }; #undef ENUM_TO_STRING - - return enum_strings[e]; } static int gpu_get_component_count(eGPUTextureFormat format) @@ -423,6 +439,13 @@ static uint gpu_get_bytesize(eGPUTextureFormat data_type) case GPU_R8: case GPU_R8UI: return 1; + case GPU_SRGB8_A8_DXT1: + case GPU_SRGB8_A8_DXT3: + case GPU_SRGB8_A8_DXT5: + case GPU_RGBA8_DXT1: + case GPU_RGBA8_DXT3: + case GPU_RGBA8_DXT5: + return 1; /* Incorrect but actual size is fractional. */ default: BLI_assert(!"Texture format incorrect or unsupported\n"); return 0; @@ -508,7 +531,18 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format) case GPU_RGB16F: return GL_RGB16F; /* Special formats texture only */ - /* ** Add Format here */ + case GPU_SRGB8_A8_DXT1: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + case GPU_SRGB8_A8_DXT3: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + case GPU_SRGB8_A8_DXT5: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case GPU_RGBA8_DXT1: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case GPU_RGBA8_DXT3: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case GPU_RGBA8_DXT5: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; /* Depth Formats */ case GPU_DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT32F; @@ -522,99 +556,6 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format) } } -static eGPUTextureFormat gl_internalformat_to_gpu_format(const GLint glformat) -{ - /* You can add any of the available type to this list - * For available types see GPU_texture.h */ - switch (glformat) { - /* Formats texture & renderbuffer */ - case GL_RGBA8UI: - return GPU_RGBA8UI; - case GL_RGBA8I: - return GPU_RGBA8I; - case GL_RGBA8: - return GPU_RGBA8; - case GL_RGBA32UI: - return GPU_RGBA32UI; - case GL_RGBA32I: - return GPU_RGBA32I; - case GL_RGBA32F: - return GPU_RGBA32F; - case GL_RGBA16UI: - return GPU_RGBA16UI; - case GL_RGBA16I: - return GPU_RGBA16I; - case GL_RGBA16F: - return GPU_RGBA16F; - case GL_RGBA16: - return GPU_RGBA16; - case GL_RG8UI: - return GPU_RG8UI; - case GL_RG8I: - return GPU_RG8I; - case GL_RG8: - return GPU_RG8; - case GL_RG32UI: - return GPU_RG32UI; - case GL_RG32I: - return GPU_RG32I; - case GL_RG32F: - return GPU_RG32F; - case GL_RG16UI: - return GPU_RG16UI; - case GL_RG16I: - return GPU_RG16I; - case GL_RG16F: - return GPU_RGBA32F; - case GL_RG16: - return GPU_RG16; - case GL_R8UI: - return GPU_R8UI; - case GL_R8I: - return GPU_R8I; - case GL_R8: - return GPU_R8; - case GL_R32UI: - return GPU_R32UI; - case GL_R32I: - return GPU_R32I; - case GL_R32F: - return GPU_R32F; - case GL_R16UI: - return GPU_R16UI; - case GL_R16I: - return GPU_R16I; - case GL_R16F: - return GPU_R16F; - case GL_R16: - return GPU_R16; - /* Special formats texture & renderbuffer */ - case GL_R11F_G11F_B10F: - return GPU_R11F_G11F_B10F; - case GL_DEPTH32F_STENCIL8: - return GPU_DEPTH32F_STENCIL8; - case GL_DEPTH24_STENCIL8: - return GPU_DEPTH24_STENCIL8; - case GL_SRGB8_ALPHA8: - return GPU_SRGB8_A8; - /* Texture only format */ - case GL_RGB16F: - return GPU_RGB16F; - /* Special formats texture only */ - /* ** Add Format here */ - /* Depth Formats */ - case GL_DEPTH_COMPONENT32F: - return GPU_DEPTH_COMPONENT32F; - case GL_DEPTH_COMPONENT24: - return GPU_DEPTH_COMPONENT24; - case GL_DEPTH_COMPONENT16: - return GPU_DEPTH_COMPONENT16; - default: - BLI_assert(!"Internal format incorrect or unsupported\n"); - } - return -1; -} - static GLenum gpu_get_gl_datatype(eGPUDataFormat format) { switch (format) { @@ -640,8 +581,8 @@ static float *GPU_texture_rescale_3d( GPUTexture *tex, int w, int h, int d, int channels, const float *fpixels) { const uint xf = w / tex->w, yf = h / tex->h, zf = d / tex->d; - float *nfpixels = MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d, - "GPUTexture Rescaled 3Dtex"); + float *nfpixels = (float *)MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d, + "GPUTexture Rescaled 3Dtex"); if (nfpixels) { GPU_print_error_debug("You need to scale a 3D texture, feel the pain!"); @@ -836,7 +777,7 @@ GPUTexture *GPU_texture_create_nD(int w, tex_format = GPU_DEPTH32F_STENCIL8; } - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->w = w; tex->h = h; tex->d = d; @@ -845,7 +786,7 @@ GPUTexture *GPU_texture_create_nD(int w, tex->format = tex_format; tex->components = gpu_get_component_count(tex_format); tex->mipmaps = 0; - tex->format_flag = 0; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); tex->number = -1; if (n == 2) { @@ -929,7 +870,7 @@ GPUTexture *GPU_texture_create_nD(int w, data_type, tex->components, can_rescale, - pixels, + (float *)pixels, &rescaled_pixels); if (G.debug & G_DEBUG_GPU || !valid) { @@ -962,7 +903,7 @@ GPUTexture *GPU_texture_create_nD(int w, gpu_texture_memory_footprint_add(tex); /* Upload Texture */ - const float *pix = (rescaled_pixels) ? rescaled_pixels : pixels; + const void *pix = (rescaled_pixels) ? rescaled_pixels : pixels; if (tex->target == GL_TEXTURE_2D || tex->target == GL_TEXTURE_2D_MULTISAMPLE || tex->target == GL_TEXTURE_1D_ARRAY) { @@ -1012,7 +953,7 @@ GPUTexture *GPU_texture_cube_create(int w, eGPUDataFormat gpu_data_format, char err_out[256]) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->w = w; tex->h = w; tex->d = d; @@ -1161,11 +1102,11 @@ GPUTexture *GPU_texture_cube_create(int w, /* Special buffer textures. tex_format must be compatible with the buffer content. */ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint buffer) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->refcount = 1; tex->format = tex_format; tex->components = gpu_get_component_count(tex_format); - tex->format_flag = 0; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); tex->target_base = tex->target = GL_TEXTURE_BUFFER; tex->mipmaps = 0; tex->number = -1; @@ -1211,44 +1152,87 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint return tex; } -GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) +static GLenum convert_target_to_gl(int dimension, bool is_array) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->bindcode = bindcode; + switch (dimension) { + case 1: + return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY; + case 2: + return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY; + case 3: + return GL_TEXTURE_3D; + default: + BLI_assert(0); + return GL_TEXTURE_2D; + } +} + +/* Create an error texture that will bind an invalid texture (pink) at draw time. */ +GPUTexture *GPU_texture_create_error(int dimension, bool is_array) +{ + GLenum textarget = convert_target_to_gl(dimension, is_array); + + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); + tex->bindcode = 0; tex->refcount = 1; tex->target = textarget; tex->target_base = textarget; tex->samples = 0; - tex->sampler_state = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO; - if (GPU_get_mipmap()) { - tex->sampler_state |= (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER); - } + tex->sampler_state = GPU_SAMPLER_DEFAULT; tex->number = -1; - if (!glIsTexture(tex->bindcode)) { - GPU_print_error_debug("Blender Texture Not Loaded"); + GPU_print_error_debug("Blender Texture Not Loaded"); + return tex; +} + +/* DDS texture loading. Return NULL if support is not available. */ +GPUTexture *GPU_texture_create_compressed( + int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data) +{ + if (!GLEW_EXT_texture_compression_s3tc) { + return NULL; } - else { - GLint w, h, gl_format; - GLenum gettarget; - gettarget = (textarget == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : textarget; - - glBindTexture(textarget, tex->bindcode); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_HEIGHT, &h); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_INTERNAL_FORMAT, &gl_format); - tex->w = w; - tex->h = h; - tex->format = gl_internalformat_to_gpu_format(gl_format); - tex->components = gpu_get_component_count(tex->format); - glBindTexture(textarget, 0); - - /* Depending on how this bindcode was obtained, the memory used here could - * already have been computed. - * But that is not the case currently. */ - gpu_texture_memory_footprint_add(tex); + + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); + tex->w = w; + tex->h = h; + tex->refcount = 1; + tex->target = tex->target_base = GL_TEXTURE_2D; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); + tex->components = gpu_get_component_count(tex_format); + tex->mipmaps = miplen - 1; + tex->sampler_state = GPU_SAMPLER_DEFAULT; + tex->number = -1; + + GLenum internalformat = gpu_format_to_gl_internalformat(tex_format); + + glGenTextures(1, &tex->bindcode); + glBindTexture(tex->target, tex->bindcode); + + /* Reset to opengl Defaults. (Untested, might not be needed) */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int blocksize = (ELEM(tex_format, GPU_RGBA8_DXT1, GPU_SRGB8_A8_DXT1)) ? 8 : 16; + + size_t ofs = 0; + for (int mip = 0; mip < miplen && (w || h); mip++, w >>= 1, h >>= 1) { + w = max_ii(1, w); + h = max_ii(1, h); + size_t size = ((w + 3) / 4) * ((h + 3) / 4) * blocksize; + + glCompressedTexImage2D(tex->target, mip, internalformat, w, h, 0, size, (uchar *)data + ofs); + + ofs += size; } + /* Restore Blender default. */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, tex->mipmaps); + glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glBindTexture(tex->target, 0); + return tex; } @@ -1503,7 +1487,8 @@ void GPU_texture_update_sub(GPUTexture *tex, GLenum data_format = gpu_get_gl_dataformat(tex->format, &tex->format_flag); GLenum data_type = gpu_get_gl_datatype(gpu_data_format); - glBindTexture(tex->target, tex->bindcode); + WARN_NOT_BOUND(tex); + switch (tex->target) { case GL_TEXTURE_1D: glTexSubImage1D(tex->target, 0, offset_x, width, data_format, data_type, pixels); @@ -1531,8 +1516,6 @@ void GPU_texture_update_sub(GPUTexture *tex, default: BLI_assert(!"tex->target mode not supported"); } - - glBindTexture(tex->target, 0); } void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat gpu_data_format, int miplvl) @@ -1826,16 +1809,6 @@ void GPU_texture_unbind_all(void) glActiveTexture(GL_TEXTURE0); } -#define WARN_NOT_BOUND(_tex) \ - { \ - if (_tex->number == -1) { \ - fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ - BLI_assert(0); \ - return; \ - } \ - } \ - ((void)0) - void GPU_texture_generate_mipmap(GPUTexture *tex) { WARN_NOT_BOUND(tex); @@ -1979,6 +1952,14 @@ void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter) SET_FLAG_FROM_TEST(tex->sampler_state, use_filter, GPU_SAMPLER_FILTER); } +void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso) +{ + /* Stencil and integer format does not support filtering. */ + BLI_assert(!(use_aniso) || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex))); + + SET_FLAG_FROM_TEST(tex->sampler_state, use_aniso, GPU_SAMPLER_ANISO); +} + void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp) { SET_FLAG_FROM_TEST(tex->sampler_state, use_repeat, GPU_SAMPLER_REPEAT); @@ -2193,7 +2174,7 @@ void GPU_samplers_init(void) { glGenSamplers(GPU_SAMPLER_MAX, GG.samplers); for (int i = 0; i < GPU_SAMPLER_MAX; i++) { - eGPUSamplerState state = i; + eGPUSamplerState state = static_cast<eGPUSamplerState>(i); GLenum clamp_type = (state & GPU_SAMPLER_CLAMP_BORDER) ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE; GLenum wrap_s = (state & GPU_SAMPLER_REPEAT_S) ? GL_REPEAT : clamp_type; GLenum wrap_t = (state & GPU_SAMPLER_REPEAT_T) ? GL_REPEAT : clamp_type; @@ -2203,8 +2184,9 @@ void GPU_samplers_init(void) ((state & GPU_SAMPLER_MIPMAP) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) : ((state & GPU_SAMPLER_MIPMAP) ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST); GLenum compare_mode = (state & GPU_SAMPLER_COMPARE) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE; + /* TODO(fclem) Anisotropic level should be a render engine parameter. */ float aniso_filter = ((state & GPU_SAMPLER_MIPMAP) && (state & GPU_SAMPLER_ANISO)) ? - GPU_get_anisotropic() : + U.anisotropic_filter : 1.0f; glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s); diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.c b/source/blender/gpu/intern/gpu_uniformbuffer.cc index 130e8fe7da1..846ab6c8560 100644 --- a/source/blender/gpu/intern/gpu_uniformbuffer.c +++ b/source/blender/gpu/intern/gpu_uniformbuffer.cc @@ -25,6 +25,7 @@ #include <string.h> #include "BLI_blenlib.h" +#include "BLI_math_base.h" #include "gpu_context_private.h" #include "gpu_node_graph.h" @@ -34,217 +35,63 @@ #include "GPU_material.h" #include "GPU_uniformbuffer.h" -typedef enum eGPUUniformBufferFlag { - GPU_UBO_FLAG_INITIALIZED = (1 << 0), - GPU_UBO_FLAG_DIRTY = (1 << 1), -} eGPUUniformBufferFlag; - -typedef enum eGPUUniformBufferType { - GPU_UBO_STATIC = 0, - GPU_UBO_DYNAMIC = 1, -} eGPUUniformBufferType; - -struct GPUUniformBuffer { - int size; /* in bytes */ - GLuint bindcode; /* opengl identifier for UBO */ - int bindpoint; /* current binding point */ - eGPUUniformBufferType type; -}; - -#define GPUUniformBufferStatic GPUUniformBuffer - -typedef struct GPUUniformBufferDynamic { - GPUUniformBuffer buffer; - void *data; /* Continuous memory block to copy to GPU. */ - char flag; -} GPUUniformBufferDynamic; - -/* Prototypes */ -static eGPUType get_padded_gpu_type(struct LinkData *link); -static void gpu_uniformbuffer_inputs_sort(struct ListBase *inputs); - -/* Only support up to this type, if you want to extend it, make sure the - * padding logic is correct for the new types. */ -#define MAX_UBO_GPU_TYPE GPU_MAT4 - -static void gpu_uniformbuffer_initialize(GPUUniformBuffer *ubo, const void *data) -{ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferData(GL_UNIFORM_BUFFER, ubo->size, data, GL_DYNAMIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); -} +typedef struct GPUUniformBuffer { + /** Data size in bytes. */ + int size; + /** GL handle for UBO. */ + GLuint bindcode; + /** Current binding point. */ + int bindpoint; + /** Continuous memory block to copy to GPU. Is own by the GPUUniformBuffer. */ + void *data; +} GPUUniformBuffer; GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]) { /* Make sure that UBO is padded to size of vec4 */ BLI_assert((size % 16) == 0); - GPUUniformBuffer *ubo = MEM_callocN(sizeof(GPUUniformBufferStatic), "GPUUniformBufferStatic"); - ubo->size = size; - ubo->bindpoint = -1; - - /* Generate Buffer object */ - ubo->bindcode = GPU_buf_alloc(); - - if (!ubo->bindcode) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO create failed", 256); - } - GPU_uniformbuffer_free(ubo); - return NULL; - } - - if (ubo->size > GPU_max_ubo_size()) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); - } - GPU_uniformbuffer_free(ubo); - return NULL; - } - - gpu_uniformbuffer_initialize(ubo, data); - return ubo; -} - -/** - * Create dynamic UBO from parameters - * Return NULL if failed to create or if \param inputs: is empty. - * - * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). - */ -GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256]) -{ - /* There is no point on creating an UBO if there is no arguments. */ - if (BLI_listbase_is_empty(inputs)) { - return NULL; - } - - GPUUniformBufferDynamic *ubo = MEM_callocN(sizeof(GPUUniformBufferDynamic), - "GPUUniformBufferDynamic"); - ubo->buffer.type = GPU_UBO_DYNAMIC; - ubo->buffer.bindpoint = -1; - ubo->flag = GPU_UBO_FLAG_DIRTY; - - /* Generate Buffer object. */ - ubo->buffer.bindcode = GPU_buf_alloc(); - - if (!ubo->buffer.bindcode) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO create failed", 256); - } - GPU_uniformbuffer_free(&ubo->buffer); - return NULL; - } - - if (ubo->buffer.size > GPU_max_ubo_size()) { + if (size > GPU_max_ubo_size()) { if (err_out) { BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); } - GPU_uniformbuffer_free(&ubo->buffer); return NULL; } - /* Make sure we comply to the ubo alignment requirements. */ - gpu_uniformbuffer_inputs_sort(inputs); - - LISTBASE_FOREACH (LinkData *, link, inputs) { - const eGPUType gputype = get_padded_gpu_type(link); - ubo->buffer.size += gputype * sizeof(float); - } - - /* Round up to size of vec4 */ - ubo->buffer.size = ((ubo->buffer.size + 15) / 16) * 16; - - /* Allocate the data. */ - ubo->data = MEM_mallocN(ubo->buffer.size, __func__); + GPUUniformBuffer *ubo = (GPUUniformBuffer *)MEM_mallocN(sizeof(GPUUniformBuffer), __func__); + ubo->size = size; + ubo->data = NULL; + ubo->bindcode = 0; + ubo->bindpoint = -1; - /* Now that we know the total ubo size we can start populating it. */ - float *offset = ubo->data; - LISTBASE_FOREACH (LinkData *, link, inputs) { - GPUInput *input = link->data; - memcpy(offset, input->vec, input->type * sizeof(float)); - offset += get_padded_gpu_type(link); + /* Direct init. */ + if (data != NULL) { + GPU_uniformbuffer_update(ubo, data); } - /* Note since we may create the UBOs in the CPU in a different thread than the main drawing one, - * we don't create the UBO in the GPU here. This will happen when we first bind the UBO. - */ - - return &ubo->buffer; -} - -/** - * Free the data - */ -static void gpu_uniformbuffer_dynamic_free(GPUUniformBuffer *ubo_) -{ - BLI_assert(ubo_->type == GPU_UBO_DYNAMIC); - GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_; - - ubo->buffer.size = 0; - if (ubo->data) { - MEM_freeN(ubo->data); - } + return ubo; } void GPU_uniformbuffer_free(GPUUniformBuffer *ubo) { - if (ubo->type == GPU_UBO_DYNAMIC) { - gpu_uniformbuffer_dynamic_free(ubo); - } - + MEM_SAFE_FREE(ubo->data); GPU_buf_free(ubo->bindcode); MEM_freeN(ubo); } -static void gpu_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) -{ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); - glBindBuffer(GL_UNIFORM_BUFFER, 0); -} - -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) -{ - BLI_assert(ubo->type == GPU_UBO_STATIC); - gpu_uniformbuffer_update(ubo, data); -} - -/** - * We need to recalculate the internal data, and re-generate it - * from its populated items. - */ -void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_) -{ - BLI_assert(ubo_->type == GPU_UBO_DYNAMIC); - GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_; - - if (ubo->flag & GPU_UBO_FLAG_INITIALIZED) { - gpu_uniformbuffer_update(ubo_, ubo->data); - } - else { - ubo->flag |= GPU_UBO_FLAG_INITIALIZED; - gpu_uniformbuffer_initialize(ubo_, ubo->data); - } - - ubo->flag &= ~GPU_UBO_FLAG_DIRTY; -} - /** * We need to pad some data types (vec3) on the C side * To match the GPU expected memory block alignment. */ static eGPUType get_padded_gpu_type(LinkData *link) { - GPUInput *input = link->data; + GPUInput *input = (GPUInput *)link->data; eGPUType gputype = input->type; - /* Unless the vec3 is followed by a float we need to treat it as a vec4. */ if (gputype == GPU_VEC3 && (link->next != NULL) && (((GPUInput *)link->next->data)->type != GPU_FLOAT)) { gputype = GPU_VEC4; } - return gputype; } @@ -254,8 +101,9 @@ static eGPUType get_padded_gpu_type(LinkData *link) */ static int inputs_cmp(const void *a, const void *b) { - const LinkData *link_a = a, *link_b = b; - const GPUInput *input_a = link_a->data, *input_b = link_b->data; + const LinkData *link_a = (const LinkData *)a, *link_b = (const LinkData *)b; + const GPUInput *input_a = (const GPUInput *)link_a->data; + const GPUInput *input_b = (const GPUInput *)link_b->data; return input_a->type < input_b->type ? 1 : 0; } @@ -265,15 +113,19 @@ static int inputs_cmp(const void *a, const void *b) */ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) { +/* Only support up to this type, if you want to extend it, make sure the + * padding logic is correct for the new types. */ +#define MAX_UBO_GPU_TYPE GPU_MAT4 + /* Order them as mat4, vec4, vec3, vec2, float. */ BLI_listbase_sort(inputs, inputs_cmp); /* Creates a lookup table for the different types; */ LinkData *inputs_lookup[MAX_UBO_GPU_TYPE + 1] = {NULL}; - eGPUType cur_type = MAX_UBO_GPU_TYPE + 1; + eGPUType cur_type = static_cast<eGPUType>(MAX_UBO_GPU_TYPE + 1); LISTBASE_FOREACH (LinkData *, link, inputs) { - GPUInput *input = link->data; + GPUInput *input = (GPUInput *)link->data; if (input->type == GPU_MAT3) { /* Alignment for mat3 is not handled currently, so not supported */ @@ -319,6 +171,74 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) link = link_next; } +#undef MAX_UBO_GPU_TYPE +} + +/** + * Create dynamic UBO from parameters + * Return NULL if failed to create or if \param inputs: is empty. + * + * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). + */ +GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256]) +{ + /* There is no point on creating an UBO if there is no arguments. */ + if (BLI_listbase_is_empty(inputs)) { + return NULL; + } + /* Make sure we comply to the ubo alignment requirements. */ + gpu_uniformbuffer_inputs_sort(inputs); + + size_t buffer_size = 0; + + LISTBASE_FOREACH (LinkData *, link, inputs) { + const eGPUType gputype = get_padded_gpu_type(link); + buffer_size += gputype * sizeof(float); + } + /* Round up to size of vec4. (Opengl Requirement) */ + size_t alignment = sizeof(float[4]); + buffer_size = divide_ceil_u(buffer_size, alignment) * alignment; + void *data = MEM_mallocN(buffer_size, __func__); + + /* Now that we know the total ubo size we can start populating it. */ + float *offset = (float *)data; + LISTBASE_FOREACH (LinkData *, link, inputs) { + GPUInput *input = (GPUInput *)link->data; + memcpy(offset, input->vec, input->type * sizeof(float)); + offset += get_padded_gpu_type(link); + } + + /* Pass data as NULL for late init. */ + GPUUniformBuffer *ubo = GPU_uniformbuffer_create(buffer_size, NULL, err_out); + /* Data will be update just before binding. */ + ubo->data = data; + return ubo; +} + +static void gpu_uniformbuffer_init(GPUUniformBuffer *ubo) +{ + BLI_assert(ubo->bindcode == 0); + ubo->bindcode = GPU_buf_alloc(); + + if (ubo->bindcode == 0) { + fprintf(stderr, "GPUUniformBuffer: UBO create failed"); + BLI_assert(0); + return; + } + + glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); + glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW); +} + +void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) +{ + if (ubo->bindcode == 0) { + gpu_uniformbuffer_init(ubo); + } + + glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); + glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); } void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) @@ -328,28 +248,30 @@ void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) return; } - if (ubo->type == GPU_UBO_DYNAMIC) { - GPUUniformBufferDynamic *ubo_dynamic = (GPUUniformBufferDynamic *)ubo; - if (ubo_dynamic->flag & GPU_UBO_FLAG_DIRTY) { - GPU_uniformbuffer_dynamic_update(ubo); - } + if (ubo->bindcode == 0) { + gpu_uniformbuffer_init(ubo); } - if (ubo->bindcode != 0) { - glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); + if (ubo->data != NULL) { + GPU_uniformbuffer_update(ubo, ubo->data); + MEM_SAFE_FREE(ubo->data); } + glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); ubo->bindpoint = number; } void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo) { - ubo->bindpoint = -1; +#ifndef NDEBUG + glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0); +#endif + ubo->bindpoint = 0; } -int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo) +void GPU_uniformbuffer_unbind_all(void) { - return ubo->bindpoint; + for (int i = 0; i < GPU_max_ubo_binds(); i++) { + glBindBufferBase(GL_UNIFORM_BUFFER, i, 0); + } } - -#undef MAX_UBO_GPU_TYPE diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.c b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 3b4d469542c..eda6d1c7300 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.c +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -39,17 +39,22 @@ static uint vbo_memory_usage; static GLenum convert_usage_type_to_gl(GPUUsageType type) { - static const GLenum table[] = { - [GPU_USAGE_STREAM] = GL_STREAM_DRAW, - [GPU_USAGE_STATIC] = GL_STATIC_DRAW, - [GPU_USAGE_DYNAMIC] = GL_DYNAMIC_DRAW, - }; - return table[type]; + switch (type) { + case GPU_USAGE_STREAM: + return GL_STREAM_DRAW; + case GPU_USAGE_DYNAMIC: + return GL_DYNAMIC_DRAW; + case GPU_USAGE_STATIC: + return GL_STATIC_DRAW; + default: + BLI_assert(0); + return GL_STATIC_DRAW; + } } GPUVertBuf *GPU_vertbuf_create(GPUUsageType usage) { - GPUVertBuf *verts = MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf"); + GPUVertBuf *verts = (GPUVertBuf *)MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf"); GPU_vertbuf_init(verts, usage); return verts; } @@ -109,7 +114,7 @@ GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts) } if (verts->data) { - verts_dst->data = MEM_dupallocN(verts->data); + verts_dst->data = (uchar *)MEM_dupallocN(verts->data); } return verts_dst; } @@ -161,7 +166,7 @@ void GPU_vertbuf_data_alloc(GPUVertBuf *verts, uint v_len) #endif verts->dirty = true; verts->vertex_len = verts->vertex_alloc = v_len; - verts->data = MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), "GPUVertBuf data"); + verts->data = (uchar *)MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), __func__); } /* resize buffer keeping existing data */ @@ -178,7 +183,7 @@ void GPU_vertbuf_data_resize(GPUVertBuf *verts, uint v_len) #endif verts->dirty = true; verts->vertex_len = verts->vertex_alloc = v_len; - verts->data = MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts)); + verts->data = (uchar *)MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts)); } /* Set vertex count but does not change allocation. diff --git a/source/blender/gpu/intern/gpu_vertex_format.c b/source/blender/gpu/intern/gpu_vertex_format.cc index 585a22277b2..a59a6a468ce 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.c +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -63,21 +63,29 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src) memcpy(dest, src, sizeof(GPUVertFormat)); } -static GLenum convert_comp_type_to_gl(GPUVertCompType type) +GLenum convert_comp_type_to_gl(GPUVertCompType type) { - static const GLenum table[] = { - [GPU_COMP_I8] = GL_BYTE, - [GPU_COMP_U8] = GL_UNSIGNED_BYTE, - [GPU_COMP_I16] = GL_SHORT, - [GPU_COMP_U16] = GL_UNSIGNED_SHORT, - [GPU_COMP_I32] = GL_INT, - [GPU_COMP_U32] = GL_UNSIGNED_INT, - - [GPU_COMP_F32] = GL_FLOAT, - - [GPU_COMP_I10] = GL_INT_2_10_10_10_REV, - }; - return table[type]; + switch (type) { + case GPU_COMP_I8: + return GL_BYTE; + case GPU_COMP_U8: + return GL_UNSIGNED_BYTE; + case GPU_COMP_I16: + return GL_SHORT; + case GPU_COMP_U16: + return GL_UNSIGNED_SHORT; + case GPU_COMP_I32: + return GL_INT; + case GPU_COMP_U32: + return GL_UNSIGNED_INT; + case GPU_COMP_F32: + return GL_FLOAT; + case GPU_COMP_I10: + return GL_INT_2_10_10_10_REV; + default: + BLI_assert(0); + return GL_FLOAT; + } } static uint comp_sz(GPUVertCompType type) @@ -94,7 +102,7 @@ static uint attr_sz(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - return a->comp_len * comp_sz(a->comp_type); + return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type)); } static uint attr_align(const GPUVertAttr *a) @@ -102,7 +110,7 @@ static uint attr_align(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - uint c = comp_sz(a->comp_type); + uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type)); if (a->comp_len == 3 && c <= 2) { return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */ } @@ -185,7 +193,6 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, attr->names[attr->name_len++] = copy_attr_name(format, name); attr->comp_type = comp_type; - attr->gl_comp_type = convert_comp_type_to_gl(comp_type); attr->comp_len = (comp_type == GPU_COMP_I10) ? 4 : comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */ @@ -279,7 +286,7 @@ void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr_id, const char * /* Encode 8 original bytes into 11 safe bytes. */ static void safe_bytes(char out[11], const char data[8]) { - char safe_chars[63] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + char safe_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; uint64_t in = *(uint64_t *)data; for (int i = 0; i < 11; i++) { @@ -368,14 +375,6 @@ static void show_pack(uint a_idx, uint sz, uint pad) void VertexFormat_pack(GPUVertFormat *format) { - /* For now, attributes are packed in the order they were added, - * making sure each attribute is naturally aligned (add padding where necessary) - * Later we can implement more efficient packing w/ reordering - * (keep attribute ID order, adjust their offsets to reorder in buffer). */ - - /* TODO: realloc just enough to hold the final combo string. And just enough to - * hold used attributes, not all 16. */ - GPUVertAttr *a0 = &format->attrs[0]; a0->offset = 0; uint offset = a0->sz; @@ -512,7 +511,6 @@ void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader) attr->sz = attr->comp_len * 4; attr->fetch_mode = fetch_mode; attr->comp_type = comp_type; - attr->gl_comp_type = convert_comp_type_to_gl(comp_type); attr += 1; } } diff --git a/source/blender/gpu/intern/gpu_vertex_format_private.h b/source/blender/gpu/intern/gpu_vertex_format_private.h index a80c085b44a..45523c0e956 100644 --- a/source/blender/gpu/intern/gpu_vertex_format_private.h +++ b/source/blender/gpu/intern/gpu_vertex_format_private.h @@ -25,6 +25,15 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + void VertexFormat_pack(GPUVertFormat *format); uint padding(uint offset, uint alignment); uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len); +GLenum convert_comp_type_to_gl(GPUVertCompType type); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 7f133ca626d..81315ec1f92 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -38,7 +38,6 @@ #include "DNA_vec_types.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl new file mode 100644 index 00000000000..f7bf3d33361 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -0,0 +1,87 @@ + +vec3 calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2) +{ + vec3 edge21 = pos2 - pos1; + vec3 edge10 = pos1 - pos0; + vec3 edge02 = pos0 - pos2; + vec3 d21 = normalize(edge21); + vec3 d10 = normalize(edge10); + vec3 d02 = normalize(edge02); + + vec3 dists; + float d = dot(d21, edge02); + dists.x = sqrt(dot(edge02, edge02) - d * d); + d = dot(d02, edge10); + dists.y = sqrt(dot(edge10, edge10) - d * d); + d = dot(d10, edge21); + dists.z = sqrt(dot(edge21, edge21) - d * d); + return dists; +} + +vec2 calc_barycentric_co(int vertid) +{ + vec2 bary; + bary.x = float((vertid % 3) == 0); + bary.y = float((vertid % 3) == 1); + return bary; +} + +#ifdef HAIR_SHADER + +/* Hairs uv and col attributes are passed by bufferTextures. */ +# define DEFINE_ATTR(type, attr) uniform samplerBuffer attr +# define GET_ATTR(type, attr) hair_get_customdata_##type(attr) + +# define barycentric_get() hair_get_barycentric() +# define barycentric_resolve(bary) hair_resolve_barycentric(bary) + +vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const samplerBuffer orco_samp) +{ + /* TODO: fix ORCO with modifiers. */ + vec3 orco = (modelmatinv * vec4(local_pos, 1.0)).xyz; + return orco_madd[0].xyz + orco * orco_madd[1].xyz; +} + +vec4 tangent_get(const samplerBuffer attr, mat3 normalmat) +{ + /* Unsupported */ + return vec4(0.0); +} + +#else /* MESH_SHADER */ + +# define DEFINE_ATTR(type, attr) in type attr +# define GET_ATTR(type, attr) attr + +/* Calculated in geom shader later with calc_barycentric_co. */ +# define barycentric_get() vec2(0) +# define barycentric_resolve(bary) bary + +vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco) +{ + /* If the object does not have any deformation, the orco layer calculation is done on the fly + * using the orco_madd factors. + * We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex + * attrib (which is [0,0,0,1]). */ + if (orco.w == 0.0) { + return orco.xyz * 0.5 + 0.5; + } + else { + return orco_madd[0].xyz + local_pos * orco_madd[1].xyz; + } +} + +vec4 tangent_get(vec4 attr, mat3 normalmat) +{ + vec4 tangent; + tangent.xyz = normalmat * attr.xyz; + tangent.w = attr.w; + float len_sqr = dot(tangent.xyz, tangent.xyz); + /* Normalize only if vector is not null. */ + if (len_sqr > 0.0) { + tangent.xyz *= inversesqrt(len_sqr); + } + return tangent; +} + +#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl index d6d6fbab971..eea8d19efce 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl @@ -3,7 +3,7 @@ void node_ambient_occlusion( vec4 color, float distance, vec3 normal, out vec4 result_color, out float result_ao) { vec3 bent_normal; - vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0); + vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); result_ao = occlusion_compute(normalize(normal), viewPosition, 1.0, rand, bent_normal); result_color = result_ao * color; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 3b23ac976ae..6330daa4391 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -1,3 +1,15 @@ + +float wang_hash_noise(uint s) +{ + s = (s ^ 61u) ^ (s >> 16u); + s *= 9u; + s = s ^ (s >> 4u); + s *= 0x27d4eb2du; + s = s ^ (s >> 15u); + + return fract(float(s) / 4294967296.0); +} + void node_hair_info(out float is_strand, out float intercept, out float thickness, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl index f9691beee6f..d33465fa846 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl @@ -6,7 +6,7 @@ void world_normals_get(out vec3 N) vec3 B = normalize(cross(worldNormal, hairTangent)); float cos_theta; if (hairThicknessRes == 1) { - vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0); + vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); /* Random cosine normal distribution on the hair surface. */ cos_theta = rand.x * 2.0 - 1.0; } diff --git a/source/blender/ikplugin/BIK_api.h b/source/blender/ikplugin/BIK_api.h index fa7d7ff555c..674b384adf2 100644 --- a/source/blender/ikplugin/BIK_api.h +++ b/source/blender/ikplugin/BIK_api.h @@ -35,10 +35,10 @@ struct bConstraint; struct bPose; struct bPoseChannel; -void BIK_initialize_tree(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - float ctime); +void BIK_init_tree(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + float ctime); void BIK_execute_tree(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, diff --git a/source/blender/ikplugin/intern/ikplugin_api.c b/source/blender/ikplugin/intern/ikplugin_api.c index 5e683d36408..233150a77aa 100644 --- a/source/blender/ikplugin/intern/ikplugin_api.c +++ b/source/blender/ikplugin/intern/ikplugin_api.c @@ -80,7 +80,7 @@ static IKPlugin *get_plugin(bPose *pose) /*----------------------------------------*/ /* Plugin API */ -void BIK_initialize_tree(struct Depsgraph *depsgraph, Scene *scene, Object *ob, float ctime) +void BIK_init_tree(struct Depsgraph *depsgraph, Scene *scene, Object *ob, float ctime) { IKPlugin *plugin = get_plugin(ob->pose); diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index cad0be659ec..cf637a06405 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ../blenkernel ../blenlib ../blenloader + ../gpu ../makesdna ../makesrna ../../../intern/guardedalloc @@ -63,6 +64,7 @@ set(SRC intern/thumbs_blend.c intern/thumbs_font.c intern/util.c + intern/util_gpu.c intern/writeimage.c IMB_colormanagement.h diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 0f3d121ff96..37046521dd8 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -89,6 +89,12 @@ struct Stereo3dFormat; /** * + * \attention defined in GPU_texture.h + */ +struct GPUTexture; + +/** + * * \attention Defined in allocimbuf.c */ void IMB_init(void); @@ -728,6 +734,25 @@ const char *IMB_ffmpeg_last_error(void); /** * + * \attention defined in util_gpu.c + */ +struct GPUTexture *IMB_create_gpu_texture(struct ImBuf *ibuf, + bool use_high_bitdepth, + bool use_premult); +struct GPUTexture *IMB_touch_gpu_texture( + struct ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth); +void IMB_update_gpu_texture_sub(struct GPUTexture *tex, + struct ImBuf *ibuf, + int x, + int y, + int z, + int w, + int h, + bool use_high_bitdepth, + bool use_premult); + +/** + * * \attention defined in stereoimbuf.c */ void IMB_stereo3d_write_dimensions(const char mode, diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index c9b3db39976..136d191c6a0 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -3958,7 +3958,7 @@ static void curve_mapping_to_ocio_settings(CurveMapping *curve_mapping, { int i; - BKE_curvemapping_initialize(curve_mapping); + BKE_curvemapping_init(curve_mapping); BKE_curvemapping_premultiply(curve_mapping, false); BKE_curvemapping_table_RGBA( curve_mapping, &curve_mapping_settings->lut, &curve_mapping_settings->lut_size); diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index 5154f50c7e8..f81c005bf06 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -651,7 +651,7 @@ BLI_INLINE int DOWNSAMPLE_FLOAT_TO_16BIT(const float _val) #define COMP_24_CS 1041666 /*Maximum size per color component for 2K & 4K @ 24fps*/ #define COMP_48_CS 520833 /*Maximum size per color component for 2K @ 48fps*/ -static int initialise_4K_poc(opj_poc_t *POC, int numres) +static int init_4K_poc(opj_poc_t *POC, int numres) { POC[0].tile = 1; POC[0].resno0 = 0; @@ -750,7 +750,7 @@ static void cinema_setup_encoder(opj_cparameters_t *parameters, else { parameters->cp_rsiz = DCP_CINEMA2K; } - parameters->numpocs = initialise_4K_poc(parameters->POC, parameters->numresolution); + parameters->numpocs = init_4K_poc(parameters->POC, parameters->numresolution); break; case OPJ_OFF: /* do nothing */ diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index 5569e119b95..247122065de 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -669,17 +669,17 @@ static void imb_stereo3d_squeeze_rect( /*************************** preparing to call the write functions **************************/ -static void imb_stereo3d_data_initialize(Stereo3DData *s3d_data, - const bool is_float, - const size_t x, - const size_t y, - const size_t channels, - int *rect_left, - int *rect_right, - int *rect_stereo, - float *rectf_left, - float *rectf_right, - float *rectf_stereo) +static void imb_stereo3d_data_init(Stereo3DData *s3d_data, + const bool is_float, + const size_t x, + const size_t y, + const size_t channels, + int *rect_left, + int *rect_right, + int *rect_stereo, + float *rectf_left, + float *rectf_right, + float *rectf_stereo) { s3d_data->is_float = is_float; s3d_data->x = x; @@ -709,7 +709,7 @@ int *IMB_stereo3d_from_rect(ImageFormatData *im_format, im_format->stereo3d_format.display_mode, false, x, y, &width, &height); r_rect = MEM_mallocN(channels * sizeof(int) * width * height, __func__); - imb_stereo3d_data_initialize( + imb_stereo3d_data_init( &s3d_data, is_float, x, y, channels, rect_left, rect_right, r_rect, NULL, NULL, NULL); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_rect(r_rect, &im_format->stereo3d_format, x, y, channels); @@ -733,7 +733,7 @@ float *IMB_stereo3d_from_rectf(ImageFormatData *im_format, im_format->stereo3d_format.display_mode, false, x, y, &width, &height); r_rectf = MEM_mallocN(channels * sizeof(float) * width * height, __func__); - imb_stereo3d_data_initialize( + imb_stereo3d_data_init( &s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, r_rectf); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_rectf(r_rectf, &im_format->stereo3d_format, x, y, channels); @@ -759,17 +759,17 @@ ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *i ibuf_stereo->flags = ibuf_left->flags; - imb_stereo3d_data_initialize(&s3d_data, - is_float, - ibuf_left->x, - ibuf_left->y, - 4, - (int *)ibuf_left->rect, - (int *)ibuf_right->rect, - (int *)ibuf_stereo->rect, - ibuf_left->rect_float, - ibuf_right->rect_float, - ibuf_stereo->rect_float); + imb_stereo3d_data_init(&s3d_data, + is_float, + ibuf_left->x, + ibuf_left->y, + 4, + (int *)ibuf_left->rect, + (int *)ibuf_right->rect, + (int *)ibuf_stereo->rect, + ibuf_left->rect_float, + ibuf_right->rect_float, + ibuf_stereo->rect_float); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_ImBuf(ibuf_stereo, &im_format->stereo3d_format, ibuf_left->x, ibuf_left->y); @@ -1286,17 +1286,17 @@ void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d, &height); imb_stereo3d_unsqueeze_ImBuf(ibuf_stereo3d, s3d, width, height); - imb_stereo3d_data_initialize(&s3d_data, - is_float, - ibuf_left->x, - ibuf_left->y, - 4, - (int *)ibuf_left->rect, - (int *)ibuf_right->rect, - (int *)ibuf_stereo3d->rect, - ibuf_left->rect_float, - ibuf_right->rect_float, - ibuf_stereo3d->rect_float); + imb_stereo3d_data_init(&s3d_data, + is_float, + ibuf_left->x, + ibuf_left->y, + 4, + (int *)ibuf_left->rect, + (int *)ibuf_right->rect, + (int *)ibuf_stereo3d->rect, + ibuf_left->rect_float, + ibuf_right->rect_float, + ibuf_stereo3d->rect_float); imb_stereo3d_read_doit(&s3d_data, s3d); @@ -1310,17 +1310,17 @@ void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d, addzbufImBuf(ibuf_right); } - imb_stereo3d_data_initialize(&s3d_data, - is_float, - ibuf_left->x, - ibuf_left->y, - 1, - (int *)ibuf_left->zbuf, - (int *)ibuf_right->zbuf, - (int *)ibuf_stereo3d->zbuf, - ibuf_left->zbuf_float, - ibuf_right->zbuf_float, - ibuf_stereo3d->zbuf_float); + imb_stereo3d_data_init(&s3d_data, + is_float, + ibuf_left->x, + ibuf_left->y, + 1, + (int *)ibuf_left->zbuf, + (int *)ibuf_right->zbuf, + (int *)ibuf_stereo3d->zbuf, + ibuf_left->zbuf_float, + ibuf_right->zbuf_float, + ibuf_stereo3d->zbuf_float); imb_stereo3d_read_doit(&s3d_data, s3d); } diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c new file mode 100644 index 00000000000..52628f5aaad --- /dev/null +++ b/source/blender/imbuf/intern/util_gpu.c @@ -0,0 +1,261 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * util.c + */ + +/** \file + * \ingroup imbuf + */ + +#include "imbuf.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "MEM_guardedalloc.h" + +#include "BKE_global.h" + +#include "GPU_extensions.h" +#include "GPU_texture.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +/* gpu ibuf utils */ + +static void imb_gpu_get_format(const ImBuf *ibuf, + bool high_bitdepth, + eGPUDataFormat *r_data_format, + eGPUTextureFormat *r_texture_format) +{ + const bool float_rect = (ibuf->rect_float != NULL); + const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); + high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth); + + *r_data_format = (float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + + if (float_rect) { + *r_texture_format = high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F; + } + else { + *r_texture_format = use_srgb ? GPU_SRGB8_A8 : GPU_RGBA8; + } +} + +/* Return false if no suitable format was found. */ +#ifdef WITH_DDS +static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format) +{ + /* For DDS we only support data, scene linear and sRGB. Converting to + * different colorspace would break the compression. */ + const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); + + if (ibuf->dds_data.fourcc == FOURCC_DXT1) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1; + } + else if (ibuf->dds_data.fourcc == FOURCC_DXT3) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3; + } + else if (ibuf->dds_data.fourcc == FOURCC_DXT5) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5; + } + else { + return false; + } + return true; +} +#endif + +/** + * Apply colormanagement and scale buffer if needed. + * *r_freedata is set to true if the returned buffer need to be manually freed. + **/ +static void *imb_gpu_get_data(const ImBuf *ibuf, + const bool do_rescale, + const int rescale_size[2], + const bool compress_as_srgb, + const bool store_premultiplied, + bool *r_freedata) +{ + const bool is_float_rect = (ibuf->rect_float != NULL); + void *data_rect = (is_float_rect) ? (void *)ibuf->rect_float : (void *)ibuf->rect; + + if (is_float_rect) { + /* Float image is already in scene linear colorspace or non-color data by + * convention, no colorspace conversion needed. But we do require 4 channels + * currently. */ + if (ibuf->channels != 4 || !store_premultiplied) { + data_rect = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); + *r_freedata = true; + + if (data_rect == NULL) { + return NULL; + } + + IMB_colormanagement_imbuf_to_float_texture( + (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); + } + } + else { + /* Byte image is in original colorspace from the file. If the file is sRGB + * scene linear, or non-color data no conversion is needed. Otherwise we + * compress as scene linear + sRGB transfer function to avoid precision loss + * in common cases. + * + * We must also convert to premultiplied for correct texture interpolation + * and consistency with float images. */ + if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + data_rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); + *r_freedata = true; + + if (data_rect == NULL) { + return NULL; + } + + /* Texture storage of images is defined by the alpha mode of the image. The + * downside of this is that there can be artifacts near alpha edges. However, + * this allows us to use sRGB texture formats and preserves color values in + * zero alpha areas, and appears generally closer to what game engines that we + * want to be compatible with do. */ + IMB_colormanagement_imbuf_to_byte_texture( + (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); + } + } + + if (do_rescale) { + uint *rect = (is_float_rect) ? NULL : (uint *)data_rect; + float *rect_float = (is_float_rect) ? (float *)data_rect : NULL; + + ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4); + IMB_scaleImBuf(scale_ibuf, UNPACK2(rescale_size)); + + data_rect = (is_float_rect) ? (void *)scale_ibuf->rect_float : (void *)scale_ibuf->rect; + *r_freedata = true; + /* Steal the rescaled buffer to avoid double free. */ + scale_ibuf->rect_float = NULL; + scale_ibuf->rect = NULL; + IMB_freeImBuf(scale_ibuf); + } + return data_rect; +} + +/* The ibuf is only here to detect the storage type. The produced texture will have undefined + * content. It will need to be populated by using IMB_update_gpu_texture_sub(). */ +GPUTexture *IMB_touch_gpu_texture(ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth) +{ + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); + + GPUTexture *tex = GPU_texture_create_nD( + w, h, layers, 2, NULL, tex_format, data_format, 0, false, NULL); + + GPU_texture_anisotropic_filter(tex, true); + return tex; +} + +/* Will update a GPUTexture using the content of the ImBuf. Only one layer will be updated. + * Will resize the ibuf if needed. + * z is the layer to update. Unused if the texture is 2D. */ +void IMB_update_gpu_texture_sub(GPUTexture *tex, + ImBuf *ibuf, + int x, + int y, + int z, + int w, + int h, + bool use_high_bitdepth, + bool use_premult) +{ + const bool do_rescale = (ibuf->x != w || ibuf->y != h); + int size[2] = {w, h}; + + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); + + const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); + bool freebuf = false; + + void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf); + + /* Update Texture. */ + GPU_texture_update_sub(tex, data_format, data, x, y, z, w, h, 1); + + if (freebuf) { + MEM_freeN(data); + } +} + +GPUTexture *IMB_create_gpu_texture(ImBuf *ibuf, bool use_high_bitdepth, bool use_premult) +{ + GPUTexture *tex = NULL; + int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)}; + bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]); + +#ifdef WITH_DDS + if (ibuf->ftype == IMB_FTYPE_DDS) { + eGPUTextureFormat compressed_format; + if (!IMB_gpu_get_compressed_format(ibuf, &compressed_format)) { + fprintf(stderr, "Unable to find a suitable DXT compression,"); + } + else if (do_rescale) { + fprintf(stderr, "Unable to load DXT image resolution,"); + } + else if (!is_power_of_2_i(ibuf->x) || !is_power_of_2_i(ibuf->y)) { + fprintf(stderr, "Unable to load non-power-of-two DXT image resolution,"); + } + else { + tex = GPU_texture_create_compressed( + ibuf->x, ibuf->y, ibuf->dds_data.nummipmaps, compressed_format, ibuf->dds_data.data); + + if (tex != NULL) { + return tex; + } + else { + fprintf(stderr, "ST3C support not found,"); + } + } + /* Fallback to uncompressed texture. */ + fprintf(stderr, " falling back to uncompressed.\n"); + } +#endif + + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); + + const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); + bool freebuf = false; + + void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf); + + /* Create Texture. */ + tex = GPU_texture_create_nD(UNPACK2(size), 0, 2, data, tex_format, data_format, 0, false, NULL); + + GPU_texture_anisotropic_filter(tex, true); + + if (freebuf) { + MEM_freeN(data); + } + + return tex; +} diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index ddf75aa3258..67f8aeb0a67 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -128,6 +128,16 @@ struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *han struct Object *object, const char *object_path); +bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name); + +/* r_vertex_velocities should point to a preallocated array of num_vertices floats */ +int ABC_read_velocity_cache(struct CacheReader *reader, + const char *velocity_name, + float time, + float fps, + int num_vertices, + float *r_vertex_velocities); + #ifdef __cplusplus } #endif diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.cc b/source/blender/io/alembic/intern/abc_axis_conversion.cc index cebab1f2e41..396c8fdb28b 100644 --- a/source/blender/io/alembic/intern/abc_axis_conversion.cc +++ b/source/blender/io/alembic/intern/abc_axis_conversion.cc @@ -170,4 +170,4 @@ void create_transform_matrix(Object *obj, } // namespace alembic } // namespace io -} // namespace blender
\ No newline at end of file +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.h b/source/blender/io/alembic/intern/abc_axis_conversion.h index 9a19e9116be..645d9fc783b 100644 --- a/source/blender/io/alembic/intern/abc_axis_conversion.h +++ b/source/blender/io/alembic/intern/abc_axis_conversion.h @@ -100,4 +100,4 @@ void create_transform_matrix(Object *obj, } // namespace alembic } // namespace io -} // namespace blender
\ No newline at end of file +} // namespace blender diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index 7cde2d4fe73..eba7f64db02 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -22,6 +22,7 @@ #include <Alembic/AbcMaterial/IMaterial.h> +#include "abc_axis_conversion.h" #include "abc_reader_archive.h" #include "abc_reader_camera.h" #include "abc_reader_curves.h" @@ -47,18 +48,13 @@ #include "BKE_lib_id.h" #include "BKE_object.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" #include "ED_undo.h" -/* SpaceType struct has a member called 'new' which obviously conflicts with C++ - * so temporarily redefining the new keyword to make it compile. */ -#define new extern_new -#include "BKE_screen.h" -#undef new - #include "BLI_compiler_compat.h" #include "BLI_fileops.h" #include "BLI_ghash.h" @@ -70,7 +66,10 @@ #include "WM_api.h" #include "WM_types.h" +using Alembic::Abc::IV3fArrayProperty; using Alembic::Abc::ObjectHeader; +using Alembic::Abc::PropertyHeader; +using Alembic::Abc::V3fArraySamplePtr; using Alembic::AbcGeom::ICamera; using Alembic::AbcGeom::ICurves; using Alembic::AbcGeom::IFaceSet; @@ -79,9 +78,11 @@ using Alembic::AbcGeom::INuPatch; using Alembic::AbcGeom::IObject; using Alembic::AbcGeom::IPoints; using Alembic::AbcGeom::IPolyMesh; +using Alembic::AbcGeom::IPolyMeshSchema; using Alembic::AbcGeom::ISampleSelector; using Alembic::AbcGeom::ISubD; using Alembic::AbcGeom::IXform; +using Alembic::AbcGeom::kWrapExisting; using Alembic::AbcGeom::MetaData; using Alembic::AbcMaterial::IMaterial; @@ -859,3 +860,136 @@ CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, return reinterpret_cast<CacheReader *>(abc_reader); } + +/* ************************************************************************** */ + +static const PropertyHeader *get_property_header(const IPolyMeshSchema &schema, const char *name) +{ + const PropertyHeader *prop_header = schema.getPropertyHeader(name); + + if (prop_header) { + return prop_header; + } + + ICompoundProperty prop = schema.getArbGeomParams(); + + if (!has_property(prop, name)) { + return nullptr; + } + + return prop.getPropertyHeader(name); +} + +bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name) +{ + AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); + + if (!abc_reader) { + return false; + } + + IObject iobject = abc_reader->iobject(); + + if (!iobject.valid()) { + return false; + } + + const ObjectHeader &header = iobject.getHeader(); + + if (!IPolyMesh::matches(header)) { + return false; + } + + IPolyMesh mesh(iobject, kWrapExisting); + IPolyMeshSchema schema = mesh.getSchema(); + + const PropertyHeader *prop_header = get_property_header(schema, name); + + if (!prop_header) { + return false; + } + + return IV3fArrayProperty::matches(prop_header->getMetaData()); +} + +static V3fArraySamplePtr get_velocity_prop(const IPolyMeshSchema &schema, + const ISampleSelector &iss, + const std::string &name) +{ + const PropertyHeader *prop_header = schema.getPropertyHeader(name); + + if (prop_header) { + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0); + return velocity_prop.getValue(iss); + } + + ICompoundProperty prop = schema.getArbGeomParams(); + + if (!has_property(prop, name)) { + return V3fArraySamplePtr(); + } + + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0); + + if (velocity_prop) { + return velocity_prop.getValue(iss); + } + + return V3fArraySamplePtr(); +} + +int ABC_read_velocity_cache(CacheReader *reader, + const char *velocity_name, + const float time, + float velocity_scale, + int num_vertices, + float *r_vertex_velocities) +{ + AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); + + if (!abc_reader) { + return -1; + } + + IObject iobject = abc_reader->iobject(); + + if (!iobject.valid()) { + return -1; + } + + const ObjectHeader &header = iobject.getHeader(); + + if (!IPolyMesh::matches(header)) { + return -1; + } + + IPolyMesh mesh(iobject, kWrapExisting); + IPolyMeshSchema schema = mesh.getSchema(); + ISampleSelector sample_sel(time); + const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel); + + V3fArraySamplePtr velocities = get_velocity_prop(schema, sample_sel, velocity_name); + + if (!velocities) { + return -1; + } + + float vel[3]; + + int num_velocity_vectors = static_cast<int>(velocities->size()); + + if (num_velocity_vectors != num_vertices) { + return -1; + } + + for (size_t i = 0; i < velocities->size(); ++i) { + const Imath::V3f &vel_in = (*velocities)[i]; + copy_zup_from_yup(vel, vel_in.getValue()); + + mul_v3_fl(vel, velocity_scale); + + copy_v3_v3(r_vertex_velocities + i * 3, vel); + } + + return num_vertices; +} diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 10a7dbcd811..c79ae68678f 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -66,7 +66,9 @@ typedef struct BrushGpencilSettings { short draw_smoothlvl; /** Number of times to subdivide new strokes. */ short draw_subdivide; - char _pad[4]; + /** Layers used for fill. */ + short fill_layer_mode; + char _pad[2]; /** Factor for transparency. */ float fill_threshold; @@ -117,7 +119,8 @@ typedef struct BrushGpencilSettings { int sculpt_mode_flag; /** Preset type (used to reset brushes - internal). */ short preset_type; - char _pad3[2]; + /** Brush preselected mode (Active/Material/Vertexcolor). */ + short brush_draw_mode; /** Randomness for Hue. */ float random_hue; @@ -250,6 +253,16 @@ typedef enum eGP_FillDrawModes { GP_FILL_DMODE_CONTROL = 2, } eGP_FillDrawModes; +/* BrushGpencilSettings->fill_layer_mode */ +typedef enum eGP_FillLayerModes { + GP_FILL_GPLMODE_VISIBLE = 0, + GP_FILL_GPLMODE_ACTIVE = 1, + GP_FILL_GPLMODE_ALL_ABOVE = 2, + GP_FILL_GPLMODE_ALL_BELOW = 3, + GP_FILL_GPLMODE_ABOVE = 4, + GP_FILL_GPLMODE_BELOW = 5, +} eGP_FillLayerModes; + /* BrushGpencilSettings->gp_eraser_mode */ typedef enum eGP_BrushEraserMode { GP_BRUSH_ERASER_SOFT = 0, @@ -257,6 +270,13 @@ typedef enum eGP_BrushEraserMode { GP_BRUSH_ERASER_STROKE = 2, } eGP_BrushEraserMode; +/* BrushGpencilSettings->brush_draw_mode */ +typedef enum eGP_BrushMode { + GP_BRUSH_MODE_ACTIVE = 0, + GP_BRUSH_MODE_MATERIAL = 1, + GP_BRUSH_MODE_VERTEXCOLOR = 2, +} eGP_BrushMode; + /* BrushGpencilSettings default brush icons */ typedef enum eGP_BrushIcons { GP_BRUSH_ICON_PENCIL = 1, @@ -330,6 +350,11 @@ typedef enum eBrushClothForceFalloffType { BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1, } eBrushClothForceFalloffType; +typedef enum eBrushClothSimulationAreaType { + BRUSH_CLOTH_SIMULATION_AREA_LOCAL = 0, + BRUSH_CLOTH_SIMULATION_AREA_GLOBAL = 1, +} eBrushClothSimulationAreaType; + typedef enum eBrushPoseDeformType { BRUSH_POSE_DEFORM_ROTATE_TWIST = 0, BRUSH_POSE_DEFORM_SCALE_TRASLATE = 1, @@ -499,7 +524,7 @@ typedef struct Brush { /** Source for fill tool color gradient application. */ char gradient_fill_mode; - char _pad0[1]; + char _pad0[5]; /** Projection shape (sphere, circle). */ char falloff_shape; @@ -525,7 +550,7 @@ typedef struct Brush { char gpencil_sculpt_tool; /** Active grease pencil weight tool. */ char gpencil_weight_tool; - char _pad1[6]; + char _pad1[2]; float autosmooth_factor; @@ -564,6 +589,7 @@ typedef struct Brush { /* cloth */ int cloth_deform_type; int cloth_force_falloff_type; + int cloth_simulation_area_type; float cloth_mass; float cloth_damping; @@ -571,6 +597,8 @@ typedef struct Brush { float cloth_sim_limit; float cloth_sim_falloff; + float cloth_constraint_softbody_strength; + /* smooth */ int smooth_deform_type; float surface_smooth_shape_preservation; @@ -715,6 +743,9 @@ typedef enum eBrushFlags2 { BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW = (1 << 1), BRUSH_POSE_IK_ANCHORED = (1 << 2), BRUSH_USE_CONNECTED_ONLY = (1 << 3), + BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY = (1 << 4), + BRUSH_POSE_USE_LOCK_ROTATION = (1 << 5), + BRUSH_CLOTH_USE_COLLISION = (1 << 6), } eBrushFlags2; typedef enum { diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h index 581248ed52b..04c99c6c4b1 100644 --- a/source/blender/makesdna/DNA_cachefile_types.h +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -52,6 +52,13 @@ typedef struct AlembicObjectPath { char path[4096]; } AlembicObjectPath; +/* CacheFile::velocity_unit + * Determines what temporal unit is used to interpret velocity vectors for motion blur effects. */ +enum { + CACHEFILE_VELOCITY_UNIT_FRAME, + CACHEFILE_VELOCITY_UNIT_SECOND, +}; + typedef struct CacheFile { ID id; struct AnimData *adt; @@ -77,7 +84,11 @@ typedef struct CacheFile { short flag; short draw_flag; /* UNUSED */ - char _pad[4]; + char _pad[3]; + + char velocity_unit; + /* Name of the velocity property in the Alembic file. */ + char velocity_name[64]; /* Runtime */ struct AbcArchiveHandle *handle; diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index c442e160bad..2ae9ba13177 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -273,9 +273,12 @@ typedef struct Curve { int selstart, selend; /* text data */ - /** Number of characters (strinfo). */ - int len_wchar; - /** Number of bytes (str - utf8). */ + /** + * Number of characters (unicode code-points) + * This is the length of #Curve.strinfo and the result of `BLI_strlen_utf8(cu->str)`. + */ + int len_char32; + /** Number of bytes: `strlen(Curve.str)`. */ int len; char *str; struct EditFont *editfont; diff --git a/source/blender/makesdna/DNA_curveprofile_types.h b/source/blender/makesdna/DNA_curveprofile_types.h index b45eb832232..5b425741df2 100644 --- a/source/blender/makesdna/DNA_curveprofile_types.h +++ b/source/blender/makesdna/DNA_curveprofile_types.h @@ -30,7 +30,7 @@ /** Number of table points per control point. */ #define PROF_RESOL 16 /** Dynamic size of widget's high resolution table. Input should be profile->totpoint. */ -#define PROF_N_TABLE(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1) +#define PROF_TABLE_LEN(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1) /** * Each control point that makes up the profile. diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 17f3fe24fbc..749bc55fcb9 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -116,13 +116,13 @@ typedef struct ImageTile { #define IMA_NEED_FRAME_RECALC (1 << 3) #define IMA_SHOW_STEREO (1 << 4) -enum { - TEXTARGET_TEXTURE_2D = 0, - TEXTARGET_TEXTURE_CUBE_MAP = 1, - TEXTARGET_TEXTURE_2D_ARRAY = 2, - TEXTARGET_TEXTURE_TILE_MAPPING = 3, - TEXTARGET_COUNT = 4, -}; +/* Used to get the correct gpu texture from an Image datablock. */ +typedef enum eGPUTextureTarget { + TEXTARGET_2D = 0, + TEXTARGET_2D_ARRAY, + TEXTARGET_TILE_MAPPING, + TEXTARGET_COUNT, +} eGPUTextureTarget; typedef struct Image { ID id; @@ -132,8 +132,8 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; - /** Not written in file 4 = TEXTARGET_COUNT, 2 = stereo eyes. */ - struct GPUTexture *gputexture[4][2]; + /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */ + struct GPUTexture *gputexture[3][2]; /* sources from: */ ListBase anims; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 93434e64df1..b01b3f42e6a 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1276,7 +1276,11 @@ typedef struct OceanModifierData { struct Ocean *ocean; struct OceanCache *oceancache; + /** Render resolution. */ int resolution; + /** Viewport resolution for the non-render case. */ + int viewport_resolution; + int spatial_size; float wind_velocity; @@ -1293,8 +1297,6 @@ typedef struct OceanModifierData { float foam_coverage; float time; - char _pad1[4]; - /* Spectrum being used. */ int spectrum; @@ -2051,6 +2053,10 @@ enum { MOD_NORMALEDIT_MIX_MUL = 3, }; +typedef struct MeshCacheVertexVelocity { + float vel[3]; +} MeshCacheVertexVelocity; + typedef struct MeshSeqCacheModifierData { ModifierData modifier; @@ -2059,11 +2065,31 @@ typedef struct MeshSeqCacheModifierData { char object_path[1024]; char read_flag; - char _pad[7]; + char _pad[3]; + + float velocity_scale; /* Runtime. */ struct CacheReader *reader; char reader_object_path[1024]; + + /* Vertex velocities read from the cache. The velocities are not automatically read during + * modifier execution, and therefore have to manually be read when needed. This is only used + * through the RNA for now. */ + struct MeshCacheVertexVelocity *vertex_velocities; + + /* The number of vertices of the Alembic mesh, set when the modifier is executed. */ + int num_vertices; + + /* Time (in frames or seconds) between two velocity samples. Automatically computed to + * scale the velocity vectors at render time for generating proper motion blur data. */ + float velocity_delta; + + /* Caches the scene time (in seconds) used to lookup data in the Alembic archive when the + * modifier was last executed. Used to access Alembic samples through the RNA. */ + float last_lookup_time; + + int _pad1; } MeshSeqCacheModifierData; /* MeshSeqCacheModifierData.read_flag */ diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h index 123ff5bfb7e..2b1fd546450 100644 --- a/source/blender/makesdna/DNA_movieclip_types.h +++ b/source/blender/makesdna/DNA_movieclip_types.h @@ -63,8 +63,8 @@ typedef struct MovieClipProxy { typedef struct MovieClip_RuntimeGPUTexture { void *next, *prev; MovieClipUser user; - /** Not written in file 4 = TEXTARGET_COUNT. */ - struct GPUTexture *gputexture[4]; + /** Not written in file 3 = TEXTARGET_COUNT. */ + struct GPUTexture *gputexture[3]; } MovieClip_RuntimeGPUTexture; typedef struct MovieClip_Runtime { diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h index 3a4925217ff..02a4a158d8c 100644 --- a/source/blender/makesdna/DNA_rigidbody_types.h +++ b/source/blender/makesdna/DNA_rigidbody_types.h @@ -213,7 +213,7 @@ typedef enum eRigidBody_Shape { RB_SHAPE_TRIMESH = 6, /* concave mesh approximated using primitives */ - // RB_SHAPE_COMPOUND, + RB_SHAPE_COMPOUND = 7, } eRigidBody_Shape; typedef enum eRigidBody_MeshSource { diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h index de4c9522334..8cc2db99332 100644 --- a/source/blender/makesdna/DNA_simulation_types.h +++ b/source/blender/makesdna/DNA_simulation_types.h @@ -29,16 +29,21 @@ typedef struct Simulation { struct bNodeTree *nodetree; - int flag; + uint32_t flag; + + /** This is the frame in scene time, that the states correspond to. */ float current_frame; + + /** Time since the start of the simulation in simulation time (which might differ from scene + * time). */ float current_simulation_time; char _pad[4]; /** List containing SimulationState objects. */ struct ListBase states; - /** List containing PersistentDataHandleItem objects. */ - struct ListBase persistent_data_handles; + /** List containing SimulationDependency objects. */ + struct ListBase dependencies; } Simulation; typedef struct SimulationState { @@ -53,8 +58,8 @@ typedef struct ParticleSimulationState { SimulationState head; /** Contains the state of the particles at time Simulation->current_frame. */ - int tot_particles; - int next_particle_id; + int32_t tot_particles; + int32_t next_particle_id; struct CustomData attributes; } ParticleSimulationState; @@ -65,19 +70,26 @@ typedef struct ParticleMeshEmitterSimulationState { char _pad[4]; } ParticleMeshEmitterSimulationState; -/** Stores a mapping between an integer handle and a corresponding ID data block. */ -typedef struct PersistentDataHandleItem { - struct PersistentDataHandleItem *next; - struct PersistentDataHandleItem *prev; +/** Stores a reference to data that the simulation depends on. This is partially derived from the + * simulation node tree. */ +typedef struct SimulationDependency { + struct SimulationDependency *next; + struct SimulationDependency *prev; struct ID *id; - int handle; - char _pad[4]; -} PersistentDataHandleItem; + int32_t handle; + uint32_t flag; +} SimulationDependency; /* Simulation.flag */ enum { SIM_DS_EXPAND = (1 << 0), }; +/* SimulationDependency.flag */ +enum { + SIM_DEPENDS_ON_TRANSFORM = (1 << 0), + SIM_DEPENDS_ON_GEOMETRY = (1 << 1), +}; + #define SIM_TYPE_NAME_PARTICLE_SIMULATION "Particle Simulation" #define SIM_TYPE_NAME_PARTICLE_MESH_EMITTER "Particle Mesh Emitter" diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index a632d42fd8b..c2ed6c97d3d 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -732,7 +732,7 @@ typedef struct UserDef { char _pad1[2]; int undomemory; float gpu_viewport_quality DNA_DEPRECATED; - short gp_manhattendist, gp_euclideandist, gp_eraser; + short gp_manhattandist, gp_euclideandist, gp_eraser; /** #eGP_UserdefSettings. */ short gp_settings; char _pad13[4]; diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h index d7a6386d12f..63038b6be2d 100644 --- a/source/blender/makesdna/DNA_view2d_types.h +++ b/source/blender/makesdna/DNA_view2d_types.h @@ -136,7 +136,7 @@ enum { /* apply pixel offsets on y-axis when setting view matrices */ V2D_PIXELOFS_Y = (1 << 3), /* view settings need to be set still... */ - V2D_IS_INITIALISED = (1 << 10), + V2D_IS_INIT = (1 << 10), }; /* scroller flags for View2D (v2d->scroll) */ diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 4a6d642bcb6..dd11fed021d 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -197,8 +197,8 @@ typedef struct wmWindowManager { /* wmWindowManager.initialized */ enum { - WM_WINDOW_IS_INITIALIZED = (1 << 0), - WM_KEYCONFIG_IS_INITIALIZED = (1 << 1), + WM_WINDOW_IS_INIT = (1 << 0), + WM_KEYCONFIG_IS_INIT = (1 << 1), }; /* wmWindowManager.outliner_sync_select_dirty */ diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index f2cf72843bd..a73fc747f84 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -67,6 +67,7 @@ DNA_STRUCT_RENAME_ELEM(Bone, scaleOut, scale_out_x) DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_f, hardeness) DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_s, aspect_ratio) DNA_STRUCT_RENAME_ELEM(Camera, YF_dofdist, dof_distance) +DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Camera, clipend, clip_end) DNA_STRUCT_RENAME_ELEM(Camera, clipsta, clip_start) DNA_STRUCT_RENAME_ELEM(Collection, dupli_ofs, instance_offset) @@ -90,6 +91,7 @@ DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) DNA_STRUCT_RENAME_ELEM(Text, name, filepath) DNA_STRUCT_RENAME_ELEM(ThemeSpace, scrubbing_background, time_scrub_background) DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) +DNA_STRUCT_RENAME_ELEM(UserDef, gp_manhattendist, gp_manhattandist) DNA_STRUCT_RENAME_ELEM(VFont, name, filepath) DNA_STRUCT_RENAME_ELEM(View3D, far, clip_end) DNA_STRUCT_RENAME_ELEM(View3D, near, clip_start) diff --git a/source/blender/makesdna/intern/dna_utils.c b/source/blender/makesdna/intern/dna_utils.c index 97f4785374a..3cf5c52a4c6 100644 --- a/source/blender/makesdna/intern/dna_utils.c +++ b/source/blender/makesdna/intern/dna_utils.c @@ -235,7 +235,9 @@ void DNA_alias_maps(enum eDNA_RenameDir version_dir, GHash **r_struct_map, GHash if (version_dir == DNA_RENAME_STATIC_FROM_ALIAS) { const char *renames[][2] = { - {"int8_t", "char"}, /* Note that a char is always unsigned in Blender. */ + /* Disable 'int8_t' until we support 'signed char', since changing negative + * values to a different type isn't supported and will change the value. */ + /* {"int8_t", "char"}, */ {"uint8_t", "uchar"}, {"int16_t", "short"}, {"uint16_t", "ushort"}, diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 898d2e58e45..628b9bdff75 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -1532,12 +1532,21 @@ int main(int argc, char **argv) #endif /* if 0 */ -/* even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit, - * use int or int64_t instead. +/** + * Disable types: + * + * - 'long': even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit, + * use int, int32_t or int64_t instead. + * - 'int8_t': as DNA doesn't yet support 'signed char' types, + * all char types are assumed to be unsigned. + * We should be able to support this, it's just not something which has been added yet. + * * Only valid use would be as a runtime variable if an API expected a long, - * but so far we dont have this happening. */ + * but so far we don't have this happening. + */ #ifdef __GNUC__ # pragma GCC poison long +# pragma GCC poison int8_t #endif #include "DNA_ID.h" diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index b7f0fb87536..bebb3a49c04 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -5147,7 +5147,7 @@ int main(int argc, char **argv) { int return_status = 0; - MEM_initialize_memleak_detection(); + MEM_init_memleak_detection(); MEM_set_error_callback(mem_error_cb); CLG_init(); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 84f9ec749cb..11b563dae52 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -237,12 +237,27 @@ static EnumPropertyItem rna_enum_gpencil_fill_draw_modes_items[] = { {GP_FILL_DMODE_BOTH, "BOTH", 0, - "Default", + "All", "Use both visible strokes and edit lines as fill boundary limits"}, {GP_FILL_DMODE_STROKE, "STROKE", 0, "Strokes", "Use visible strokes as fill boundary limits"}, {GP_FILL_DMODE_CONTROL, "CONTROL", 0, "Edit Lines", "Use edit lines as fill boundary limits"}, {0, NULL, 0, NULL, NULL}}; +static EnumPropertyItem rna_enum_gpencil_fill_layers_modes_items[] = { + {GP_FILL_GPLMODE_VISIBLE, "VISIBLE", 0, "Visible", "Visible layers"}, + {GP_FILL_GPLMODE_ACTIVE, "ACTIVE", 0, "Active", "Only active layer"}, + {GP_FILL_GPLMODE_ABOVE, "ABOVE", 0, "Layer Above", "Layer above active"}, + {GP_FILL_GPLMODE_BELOW, "BELOW", 0, "Layer Below", "Layer below active"}, + {GP_FILL_GPLMODE_ALL_ABOVE, "ALL_ABOVE", 0, "All Above", "All layers above active"}, + {GP_FILL_GPLMODE_ALL_BELOW, "ALL_BELOW", 0, "All Below", "All layers below active"}, + {0, NULL, 0, NULL, NULL}}; + +static EnumPropertyItem rna_enum_gpencil_brush_modes_items[] = { + {GP_BRUSH_MODE_ACTIVE, "ACTIVE", 0, "Active", "Use current mode"}, + {GP_BRUSH_MODE_MATERIAL, "MATERIAL", 0, "Material", "Use always material mode"}, + {GP_BRUSH_MODE_VERTEXCOLOR, "VERTEXCOLOR", 0, "Vertex Color", "Use always Vertex Color mode"}, + {0, NULL, 0, NULL, NULL}}; + static EnumPropertyItem rna_enum_gpencil_brush_paint_icons_items[] = { {GP_BRUSH_ICON_PENCIL, "PENCIL", ICON_GPBRUSH_PENCIL, "Pencil", ""}, {GP_BRUSH_ICON_PEN, "PEN", ICON_GPBRUSH_PEN, "Pen", ""}, @@ -359,7 +374,7 @@ static bool rna_BrushCapabilities_has_overlay_get(PointerRNA *ptr) static bool rna_BrushCapabilitiesSculpt_has_persistence_get(PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; - return br->sculpt_tool == SCULPT_TOOL_LAYER; + return ELEM(br->sculpt_tool, SCULPT_TOOL_LAYER, SCULPT_TOOL_CLOTH); } static bool rna_BrushCapabilitiesSculpt_has_pinch_factor_get(PointerRNA *ptr) @@ -1640,6 +1655,18 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Mode to draw boundary limits"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "fill_layer_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "fill_layer_mode"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_fill_layers_modes_items); + RNA_def_property_ui_text(prop, "Layer Mode", "Layers used as boundaries"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "brush_draw_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "brush_draw_mode"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_modes_items); + RNA_def_property_ui_text(prop, "Mode", "Preselected mode when using this brush"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "trim", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_TRIM_STROKE); RNA_def_property_boolean_default(prop, false); @@ -1956,6 +1983,16 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem brush_cloth_simulation_area_type_items[] = { + {BRUSH_CLOTH_SIMULATION_AREA_LOCAL, + "LOCAL", + 0, + "Local", + "Simulates only a specific area arround the brush limited by a fixed radius"}, + {BRUSH_CLOTH_SIMULATION_AREA_GLOBAL, "GLOBAL", 0, "Global", "Simulates the entire mesh"}, + {0, NULL, 0, NULL, NULL}, + }; + static const EnumPropertyItem brush_smooth_deform_type_items[] = { {BRUSH_SMOOTH_DEFORM_LAPLACIAN, "LAPLACIAN", @@ -1973,7 +2010,7 @@ static void rna_def_brush(BlenderRNA *brna) static const EnumPropertyItem brush_pose_deform_type_items[] = { {BRUSH_POSE_DEFORM_ROTATE_TWIST, "ROTATE_TWIST", 0, "Rotate/Twist", ""}, {BRUSH_POSE_DEFORM_SCALE_TRASLATE, "SCALE_TRANSLATE", 0, "Scale/Translate", ""}, - {BRUSH_POSE_DEFORM_SQUASH_STRETCH, "SQUASH_STRETCH", 0, "Squash/Stretch", ""}, + {BRUSH_POSE_DEFORM_SQUASH_STRETCH, "SQUASH_STRETCH", 0, "Squash & Stretch", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2126,6 +2163,14 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Force Falloff", "Shape used in the brush to apply force to the cloth"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "cloth_simulation_area_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_cloth_simulation_area_type_items); + RNA_def_property_ui_text( + prop, + "Simulation Area", + "Part of the mesh that is going to be simulated when the stroke is active"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "smooth_deform_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, brush_smooth_deform_type_items); RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); @@ -2556,6 +2601,15 @@ static void rna_def_brush(BlenderRNA *brna) "Area to apply deformation falloff to the effects of the simulation"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "cloth_constraint_softbody_strength", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "cloth_constraint_softbody_strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text( + prop, + "Soft Body Influence", + "How much the simulation preserves the original shape, acting as a soft body"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "hardness", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "hardness"); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -2767,11 +2821,32 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Keep Anchor Point", "Keep the position of the last segment in the IK chain fixed"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_pose_lock_rotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_POSE_USE_LOCK_ROTATION); + RNA_def_property_ui_text(prop, + "Lock Rotation When Scaling", + "Do not rotate the segment when using the scale deform mode"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_connected_only", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_USE_CONNECTED_ONLY); RNA_def_property_ui_text(prop, "Connected Only", "Affect only topologically connected elements"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_cloth_pin_simulation_boundary", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY); + RNA_def_property_ui_text( + prop, + "Pin Simulation Boundary", + "Lock the position of the vertices in the simulation falloff area to avoid artifacts and " + "create a softer transitionwith with unnafected areas"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_cloth_collision", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CLOTH_USE_COLLISION); + RNA_def_property_ui_text(prop, "Enable Collision", "Collide with objects during the simulation"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "invert_to_scrape_fill", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_INVERT_TO_SCRAPE_FILL); RNA_def_property_ui_text(prop, diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index f9275ef1993..c25cea1b4b3 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -174,6 +174,32 @@ static void rna_def_cachefile(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Object Paths", "Paths of the objects inside the Alembic archive"); + /* ----------------- Alembic Velocity Attribute ----------------- */ + + prop = RNA_def_property(srna, "velocity_name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, + "Velocity Attribute", + "Name of the Alembic attribute used for generating motion blur data"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + static const EnumPropertyItem velocity_unit_items[] = { + {CACHEFILE_VELOCITY_UNIT_SECOND, "SECOND", 0, "Second", ""}, + {CACHEFILE_VELOCITY_UNIT_FRAME, "FRAME", 0, "Frame", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "velocity_unit", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "velocity_unit"); + RNA_def_property_enum_items(prop, velocity_unit_items); + RNA_def_property_ui_text( + prop, + "Velocity Unit", + "Define how the velocity vectors are interpreted with regard to time, 'frame' means " + "the delta time is 1 frame, 'second' means the delta time is 1 / FPS"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_define_lib_overridable(false); rna_def_cachefile_object_paths(brna, prop); diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 56ad8e2677b..60b6cc40792 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -702,7 +702,7 @@ static float rna_CurveMapping_evaluateF(struct CurveMapping *cumap, static void rna_CurveMap_initialize(struct CurveMapping *cumap) { - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); } #else diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index c8abf774561..e6dceb5af72 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -41,7 +41,7 @@ const EnumPropertyItem rna_enum_context_mode_items[] = { {CTX_MODE_EDIT_ARMATURE, "EDIT_ARMATURE", 0, "Armature Edit", ""}, {CTX_MODE_EDIT_METABALL, "EDIT_METABALL", 0, "Metaball Edit", ""}, {CTX_MODE_EDIT_LATTICE, "EDIT_LATTICE", 0, "Lattice Edit", ""}, - {CTX_MODE_POSE, "POSE", 0, "Pose ", ""}, + {CTX_MODE_POSE, "POSE", 0, "Pose", ""}, {CTX_MODE_SCULPT, "SCULPT", 0, "Sculpt", ""}, {CTX_MODE_PAINT_WEIGHT, "PAINT_WEIGHT", 0, "Weight Paint", ""}, {CTX_MODE_PAINT_VERTEX, "PAINT_VERTEX", 0, "Vertex Paint", ""}, diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 771235c85aa..1768d79fe8f 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -570,7 +570,7 @@ static void rna_Curve_body_set(PointerRNA *ptr, const char *value) Curve *cu = (Curve *)ptr->owner_id; - cu->len_wchar = len_chars; + cu->len_char32 = len_chars; cu->len = len_bytes; cu->pos = len_chars; @@ -1191,9 +1191,9 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna) RNA_def_property_ui_text( prop, "Object Font", - "Use Objects as font characters (give font objects a common name " + "Use objects as font characters (give font objects a common name " "followed by the character they represent, eg. 'family-a', 'family-b', etc, " - "set this setting to 'family-', and turn on Vertex Duplication)"); + "set this setting to 'family-', and turn on Vertex Instancing)"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "body", PROP_STRING, PROP_NONE); @@ -1206,7 +1206,7 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "body_format", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "strinfo", "len_wchar"); + RNA_def_property_collection_sdna(prop, NULL, "strinfo", "len_char32"); RNA_def_property_struct_type(prop, "TextCharacterFormat"); RNA_def_property_ui_text(prop, "Character Info", "Stores the style of each character"); diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c index ce91fc79085..ee1c659fcd5 100644 --- a/source/blender/makesrna/intern/rna_curveprofile.c +++ b/source/blender/makesrna/intern/rna_curveprofile.c @@ -146,7 +146,7 @@ static void rna_CurveProfile_evaluate(struct CurveProfile *profile, static void rna_CurveProfile_initialize(struct CurveProfile *profile, int segments_len) { - BKE_curveprofile_initialize(profile, (short)segments_len); + BKE_curveprofile_init(profile, (short)segments_len); } static void rna_CurveProfile_update(struct CurveProfile *profile) diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 3ae16f8577a..d5449a69cf6 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1635,7 +1635,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) /* TODO: setting this to true must ensure that all others in stack are turned off too... */ prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_ACTIVE); - RNA_def_property_ui_text(prop, "Active", "F-Curve Modifier is the one being edited "); + RNA_def_property_ui_text(prop, "Active", "F-Curve Modifier is the one being edited"); RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_active_set"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_active_update"); RNA_def_property_ui_icon(prop, ICON_RADIOBUT_OFF, 1); diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 47e0333edb1..0a58f8af593 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -1681,7 +1681,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_range(prop, 0.001, 1.0); RNA_def_property_ui_range(prop, 0.01, 1.0, 0.05, -1); RNA_def_property_ui_text(prop, - "Obstacle-Fluid Threshold ", + "Obstacle-Fluid Threshold", "Determines how much fluid is allowed in an obstacle cell " "(higher values will tag a boundary cell as an obstacle easier " "and reduce the boundary smoothening effect)"); @@ -2052,6 +2052,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, "Start", "Frame on which the simulation starts. This is the first frame that will be baked"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); prop = RNA_def_property(srna, "cache_frame_end", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "cache_frame_end"); @@ -2061,6 +2062,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, "End", "Frame on which the simulation stops. This is the last frame that will be baked"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); prop = RNA_def_property(srna, "cache_frame_offset", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "cache_frame_offset"); @@ -2070,6 +2072,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "Offset", "Frame offset that is used when loading the simulation from the cache. It is not considered " "when baking the simulation, only when loading it"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); prop = RNA_def_property(srna, "cache_frame_pause_data", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "cache_frame_pause_data"); @@ -2093,6 +2096,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, NULL, "rna_Fluid_cachetype_mesh_set", "rna_Fluid_cachetype_mesh_itemf"); RNA_def_property_ui_text( prop, "File Format", "Select the file format to be used for caching surface data"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_meshcache_reset"); prop = RNA_def_property(srna, "cache_data_format", PROP_ENUM, PROP_NONE); @@ -2102,6 +2106,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, NULL, "rna_Fluid_cachetype_data_set", "rna_Fluid_cachetype_volume_itemf"); RNA_def_property_ui_text( prop, "File Format", "Select the file format to be used for caching volumetric data"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); prop = RNA_def_property(srna, "cache_particle_format", PROP_ENUM, PROP_NONE); @@ -2111,6 +2116,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, NULL, "rna_Fluid_cachetype_particle_set", "rna_Fluid_cachetype_particle_itemf"); RNA_def_property_ui_text( prop, "File Format", "Select the file format to be used for caching particle data"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_particlescache_reset"); prop = RNA_def_property(srna, "cache_noise_format", PROP_ENUM, PROP_NONE); @@ -2120,6 +2126,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, NULL, "rna_Fluid_cachetype_noise_set", "rna_Fluid_cachetype_volume_itemf"); RNA_def_property_ui_text( prop, "File Format", "Select the file format to be used for caching noise data"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_noisecache_reset"); prop = RNA_def_property(srna, "cache_type", PROP_ENUM, PROP_NONE); @@ -2127,6 +2134,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, cache_types); RNA_def_property_enum_funcs(prop, NULL, "rna_Fluid_cachetype_set", NULL); RNA_def_property_ui_text(prop, "Type", "Change the cache type of the simulation"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_domain_data_reset"); prop = RNA_def_property(srna, "cache_resumable", PROP_BOOLEAN, PROP_NONE); @@ -2137,6 +2145,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "Additional data will be saved so that the bake jobs can be resumed after pausing. Because " "more data will be written to disk it is recommended to avoid enabling this option when " "baking at high resolutions"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); prop = RNA_def_property(srna, "cache_directory", PROP_STRING, PROP_DIRPATH); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 887bded8540..1c43815d3a2 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -67,7 +67,6 @@ static const EnumPropertyItem image_source_items[] = { # include "BKE_global.h" -# include "GPU_draw.h" # include "GPU_texture.h" # include "IMB_imbuf.h" @@ -200,7 +199,7 @@ static void rna_Image_gpu_texture_update(Main *UNUSED(bmain), Image *ima = (Image *)ptr->owner_id; if (!G.background) { - GPU_free_image(ima); + BKE_image_free_gputextures(ima); } WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id); @@ -398,7 +397,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0]; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } @@ -516,7 +515,7 @@ static void rna_Image_pixels_set(PointerRNA *ptr, const float *values) ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID | IB_MIPMAP_INVALID; BKE_image_mark_dirty(ima, ibuf); if (!G.background) { - GPU_free_image(ima); + BKE_image_free_gputextures(ima); } WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id); } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 41c0e724234..6f876923e52 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -50,8 +50,6 @@ # include "DNA_image_types.h" # include "DNA_scene_types.h" -# include "GPU_glew.h" - # include "MEM_guardedalloc.h" static void rna_ImagePackedFile_save(ImagePackedFile *imapf, Main *bmain, ReportList *reports) @@ -222,23 +220,24 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame) BKE_imageuser_default(&iuser); iuser.framenr = frame; - GPUTexture *tex = GPU_texture_from_blender(image, &iuser, NULL, GL_TEXTURE_2D); + GPUTexture *tex = BKE_image_get_gpu_texture(image, &iuser, NULL); if (tex == NULL) { BKE_reportf(reports, RPT_ERROR, "Failed to load image texture '%s'", image->id.name + 2); - return (int)GL_INVALID_OPERATION; + /* TODO(fclem) this error code makes no sense for vulkan. */ + return 0x0502; /* GL_INVALID_OPERATION */ } - return GL_NO_ERROR; + return 0; /* GL_NO_ERROR */ } static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame) { - int error = GL_NO_ERROR; + int error = 0; /* GL_NO_ERROR */ BKE_image_tag_time(image); - if (image->gputexture[TEXTARGET_TEXTURE_2D] == NULL) { + if (image->gputexture[TEXTARGET_2D][0] == NULL) { error = rna_Image_gl_load(image, reports, frame); } @@ -247,7 +246,7 @@ static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame) static void rna_Image_gl_free(Image *image) { - GPU_free_image(image); + BKE_image_free_gputextures(image); /* remove the nocollect flag, image is available for garbage collection again */ image->flag &= ~IMA_NOCOLLECT; diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 86f05c350f3..05e11ffc919 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -302,7 +302,7 @@ const EnumPropertyItem rna_enum_modifier_triangulate_quad_method_items[] = { {MOD_TRIANGULATE_QUAD_BEAUTY, "BEAUTY", 0, - "Beauty ", + "Beauty", "Split the quads in nice triangles, slower method"}, {MOD_TRIANGULATE_QUAD_FIXED, "FIXED", @@ -1163,7 +1163,7 @@ static void rna_BevelModifier_update_segments(Main *bmain, Scene *scene, Pointer BevelModifierData *bmd = (BevelModifierData *)ptr->data; if (RNA_enum_get(ptr, "profile_type") == MOD_BEVEL_PROFILE_CUSTOM) { short segments = (short)RNA_int_get(ptr, "segments"); - BKE_curveprofile_initialize(bmd->custom_profile, segments); + BKE_curveprofile_init(bmd->custom_profile, segments); } rna_Modifier_update(bmain, scene, ptr); } @@ -1699,6 +1699,51 @@ static bool rna_Modifier_show_expanded_get(PointerRNA *ptr) return md->ui_expand_flag & (1 << 0); } +static int rna_MeshSequenceCacheModifier_has_velocity_get(PointerRNA *ptr) +{ +# ifdef WITH_ALEMBIC + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; + return ABC_has_vec3_array_property_named(mcmd->reader, mcmd->cache_file->velocity_name); +# else + return false; + UNUSED_VARS(ptr); +# endif +} + +static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr) +{ +# ifdef WITH_ALEMBIC + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; + + if (mcmd->num_vertices == 0) { + return 0; + } + + if (mcmd->vertex_velocities) { + MEM_freeN(mcmd->vertex_velocities); + } + + mcmd->vertex_velocities = MEM_mallocN(sizeof(MeshCacheVertexVelocity) * mcmd->num_vertices, + "Mesh Cache Velocities"); + + int num_read = ABC_read_velocity_cache(mcmd->reader, + mcmd->cache_file->velocity_name, + mcmd->last_lookup_time, + mcmd->velocity_scale * mcmd->velocity_delta, + mcmd->num_vertices, + (float *)mcmd->vertex_velocities); + + if (num_read == -1 || num_read != mcmd->num_vertices) { + return false; + } + + return true; +# else + return false; + UNUSED_VARS(ptr); +# endif +} + #else /* NOTE: *MUST* return subdivision_type property. */ @@ -5667,7 +5712,17 @@ static void rna_def_modifier_ocean(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 1, 1024); RNA_def_property_ui_range(prop, 1, 32, 1, -1); - RNA_def_property_ui_text(prop, "Resolution", "Resolution of the generated surface"); + RNA_def_property_ui_text( + prop, "Render Resolution", "Resolution of the generated surface for rendering and baking"); + RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update"); + + prop = RNA_def_property(srna, "viewport_resolution", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "viewport_resolution"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_range(prop, 1, 1024); + RNA_def_property_ui_range(prop, 1, 32, 1, -1); + RNA_def_property_ui_text( + prop, "Viewport Resolution", "Viewport resolution of the generated surface"); RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update"); prop = RNA_def_property(srna, "spatial_size", PROP_INT, PROP_NONE); @@ -5905,7 +5960,7 @@ static void rna_def_modifier_triangulate(BlenderRNA *brna) static void rna_def_modifier_meshcache(BlenderRNA *brna) { static const EnumPropertyItem prop_format_type_items[] = { - {MOD_MESHCACHE_TYPE_MDD, "MDD", 0, "MDD ", ""}, + {MOD_MESHCACHE_TYPE_MDD, "MDD", 0, "MDD", ""}, {MOD_MESHCACHE_TYPE_PC2, "PC2", 0, "PC2", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -5926,7 +5981,7 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) }; static const EnumPropertyItem prop_interpolation_type_items[] = { - {MOD_MESHCACHE_INTERP_NONE, "NONE", 0, "None ", ""}, + {MOD_MESHCACHE_INTERP_NONE, "NONE", 0, "None", ""}, {MOD_MESHCACHE_INTERP_LINEAR, "LINEAR", 0, "Linear", ""}, /* for cardinal we'd need to read 4x cache's */ // {MOD_MESHCACHE_INTERP_CARDINAL, "CARDINAL", 0, "Cardinal", ""}, @@ -6066,6 +6121,22 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) RNA_define_lib_overridable(false); } +static void rna_def_mesh_cache_velocities(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "MeshCacheVertexVelocity", NULL); + RNA_def_struct_ui_text(srna, "Mesh Cache Velocity", "Velocity attribute of an Alembic mesh"); + RNA_def_struct_ui_icon(srna, ICON_VERTEXSEL); + + prop = RNA_def_property(srna, "velocity", PROP_FLOAT, PROP_VELOCITY); + RNA_def_property_array(prop, 3); + RNA_def_property_float_sdna(prop, NULL, "vel"); + RNA_def_property_ui_text(prop, "Velocity", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); +} + static void rna_def_modifier_meshseqcache(BlenderRNA *brna) { StructRNA *srna; @@ -6108,6 +6179,35 @@ static void rna_def_modifier_meshseqcache(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "velocity_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "velocity_scale"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text( + prop, + "Velocity Scale", + "Multiplier used to control the magnitude of the velocity vectors for time effects"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + /* -------------------------- Velocity Vectors -------------------------- */ + + prop = RNA_def_property(srna, "vertex_velocities", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "vertex_velocities", "num_vertices"); + RNA_def_property_struct_type(prop, "MeshCacheVertexVelocity"); + RNA_def_property_ui_text( + prop, "Fluid Mesh Vertices", "Vertices of the fluid mesh generated by simulation"); + + rna_def_mesh_cache_velocities(brna); + + prop = RNA_def_property(srna, "has_velocity", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Has Velocity Cache", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshSequenceCacheModifier_has_velocity_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "read_velocity", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Read Velocity Cache", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshSequenceCacheModifier_read_velocity_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_define_lib_overridable(false); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 3eb2c15c053..af07185ab4a 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -38,6 +38,7 @@ #include "BKE_animsys.h" #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_simulation.h" #include "BKE_texture.h" #include "RNA_access.h" @@ -2848,6 +2849,14 @@ static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA * } } +static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C, PointerRNA *ptr) +{ + rna_NodeSocketStandard_value_update(C, ptr); + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + Main *bmain = CTX_data_main(C); + ntreeUpdateTree(bmain, ntree); +} + /* ******** Node Types ******** */ static void rna_NodeInternalSocketTemplate_name_get(PointerRNA *ptr, char *value) @@ -6495,7 +6504,7 @@ static void def_cmp_channel_matte(StructRNA *srna) static const EnumPropertyItem algorithm_items[] = { {0, "SINGLE", 0, "Single", "Limit by single channel"}, - {1, "MAX", 0, "Max", "Limit by max of other channels "}, + {1, "MAX", 0, "Max", "Limit by max of other channels"}, {0, NULL, 0, NULL, NULL}, }; @@ -8868,7 +8877,8 @@ static void rna_def_node_socket_object(BlenderRNA *brna, RNA_def_property_pointer_sdna(prop, NULL, "value"); RNA_def_property_struct_type(prop, "Object"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); /* socket interface */ @@ -8902,7 +8912,8 @@ static void rna_def_node_socket_image(BlenderRNA *brna, RNA_def_property_pointer_sdna(prop, NULL, "value"); RNA_def_property_struct_type(prop, "Image"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); /* socket interface */ @@ -9916,7 +9927,7 @@ static void rna_def_composite_nodetree(BlenderRNA *brna) prop = RNA_def_property(srna, "use_viewer_border", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NTREE_VIEWER_BORDER); RNA_def_property_ui_text( - prop, "Viewer Border", "Use boundaries for viewer nodes and composite backdrop"); + prop, "Viewer Region", "Use boundaries for viewer nodes and composite backdrop"); RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update"); } diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 84b83bee089..08ca3f16b6d 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -193,6 +193,12 @@ static EnumPropertyItem instance_items_nogroup[] = { INSTANCE_ITEMS_SHARED, {0, NULL, 0, NULL, NULL}, }; + +static EnumPropertyItem instance_items_pointcloud[] = { + {0, "NONE", 0, "None", ""}, + {OB_DUPLIVERTS, "POINTS", 0, "Points", "Instantiate child objects on all points"}, + {0, NULL, 0, NULL, NULL}, +}; #endif #undef INSTANCE_ITEMS_SHARED #undef INSTANCE_ITEM_COLLECTION @@ -707,6 +713,9 @@ static const EnumPropertyItem *rna_Object_instance_type_itemf(bContext *UNUSED(C if (ob->type == OB_EMPTY) { item = instance_items; } + else if (ob->type == OB_POINTCLOUD) { + item = instance_items_pointcloud; + } else { item = instance_items_nogroup; } diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index af1f1847fc6..3067a5a9453 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -2887,7 +2887,7 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "userjit"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0, 1000); - RNA_def_property_ui_text(prop, "P/F", "Emission locations / face (0 = automatic)"); + RNA_def_property_ui_text(prop, "Particles/Face", "Emission locations per face (0 = automatic)"); RNA_def_property_update(prop, 0, "rna_Particle_reset"); prop = RNA_def_property(srna, "grid_resolution", PROP_INT, PROP_UNSIGNED); diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c index 325c4e3caa9..450d148d8a3 100644 --- a/source/blender/makesrna/intern/rna_rigidbody.c +++ b/source/blender/makesrna/intern/rna_rigidbody.c @@ -75,6 +75,11 @@ const EnumPropertyItem rna_enum_rigidbody_object_shape_items[] = { "Mesh", "Mesh consisting of triangles only, allowing for more detailed interactions than convex " "hulls"}, + {RB_SHAPE_COMPOUND, + "COMPOUND", + ICON_MESH_DATA, + "Compound Parent", + "Combines all of its direct rigid body children into one rigid object."}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 66698d60423..262c9f87b66 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2862,7 +2862,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) "3D Cursor", "Draw stroke at 3D cursor location"}, /* Weird, GP_PROJECT_VIEWALIGN is inverted. */ - {0, "VIEW", ICON_RESTRICT_VIEW_ON, "View", "Stick stroke to the view "}, + {0, "VIEW", ICON_RESTRICT_VIEW_ON, "View", "Stick stroke to the view"}, {GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_VIEW, "SURFACE", ICON_FACESEL, @@ -3589,7 +3589,7 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, brush_size_unit_items); RNA_def_property_ui_text( - prop, "Radius Unit", "Measure brush size relative to the view or the scene "); + prop, "Radius Unit", "Measure brush size relative to the view or the scene"); } static void rna_def_curve_paint_settings(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index a66e20258d2..06c73fbb19c 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -138,8 +138,7 @@ static void rna_SceneRender_get_frame_path( } static void rna_Scene_ray_cast(Scene *scene, - Main *bmain, - ViewLayer *view_layer, + Depsgraph *depsgraph, float origin[3], float direction[3], float ray_dist, @@ -151,8 +150,6 @@ static void rna_Scene_ray_cast(Scene *scene, float r_obmat[16]) { normalize_v3(direction); - - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); SnapObjectContext *sctx = ED_transform_snap_object_context_create(scene, 0); bool ret = ED_transform_snap_object_project_ray_ex(sctx, @@ -292,9 +289,9 @@ void RNA_api_scene(StructRNA *srna) /* Ray Cast */ func = RNA_def_function(srna, "ray_cast", "rna_Scene_ray_cast"); - RNA_def_function_flag(func, FUNC_USE_MAIN); RNA_def_function_ui_description(func, "Cast a ray onto in object space"); - parm = RNA_def_pointer(func, "view_layer", "ViewLayer", "", "Scene Layer"); + + parm = RNA_def_pointer(func, "depsgraph", "Depsgraph", "", "The current dependency graph"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); /* ray start and end */ parm = RNA_def_float_vector(func, "origin", 3, NULL, -FLT_MAX, FLT_MAX, "", "", -1e4, 1e4); diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index 24c4818694f..fb2a60db0fd 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -231,7 +231,7 @@ static int rna_Area_ui_type_get(PointerRNA *ptr) * the area type is changing. * So manually do the lookup in those cases, but do not actually change area->type * since that prevents a proper exit when the area type is changing. - * Logic copied from `ED_area_initialize()`.*/ + * Logic copied from `ED_area_init()`.*/ SpaceType *type = area->type; if (type == NULL || area_changing) { type = BKE_spacetype_from_id(area_type); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 4157747455d..59cedf8fcb8 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -370,13 +370,14 @@ static void rna_Sequences_remove( Sequence *seq = seq_ptr->data; Scene *scene = (Scene *)id; - if (BLI_remlink_safe(&ed->seqbase, seq) == false) { + if (BLI_findindex(&ed->seqbase, seq) == -1) { BKE_reportf( reports, RPT_ERROR, "Sequence '%s' not in scene '%s'", seq->name + 2, scene->id.name + 2); return; } - BKE_sequence_free(scene, seq, true); + BKE_sequencer_flag_for_removal(scene, &ed->seqbase, seq); + BKE_sequencer_remove_flagged_sequences(scene, &ed->seqbase); RNA_POINTER_INVALIDATE(seq_ptr); DEG_relations_tag_update(bmain); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 494fcec4c31..155f5ab3043 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1776,87 +1776,28 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(bContext *UNUSE { SpaceProperties *sbuts = (SpaceProperties *)(ptr->data); EnumPropertyItem *item = NULL; - int totitem = 0; - - if (sbuts->pathflag & (1 << BCONTEXT_TOOL)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TOOL); - } - - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } - - if (sbuts->pathflag & (1 << BCONTEXT_RENDER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_RENDER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_OUTPUT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OUTPUT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_VIEW_LAYER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SCENE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_WORLD)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_WORLD); - } - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } - - if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OBJECT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MODIFIER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SHADERFX); - } - - if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PARTICLE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PHYSICS); - } - - if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_CONSTRAINT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_DATA)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_DATA); - (item + totitem - 1)->icon = sbuts->dataicon; - } - - if (sbuts->pathflag & (1 << BCONTEXT_BONE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_BONE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_BONE_CONSTRAINT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_BONE_CONSTRAINT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_MATERIAL)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MATERIAL); - } + /* We use 32 tabs maximum here so a flag for each can fit into a 32 bit integer flag. + * A theoretical maximum would be BCONTEXT_TOT * 2, with every tab displayed and a spacer + * in every other item. But this size is currently limited by the size of integer + * supported by RNA enums. */ + int context_tabs_array[32]; + int totitem = ED_buttons_tabs_list(sbuts, context_tabs_array); + BLI_assert(totitem <= ARRAY_SIZE(context_tabs_array)); + + int totitem_added = 0; + for (int i = 0; i < totitem; i++) { + if (context_tabs_array[i] == -1) { + RNA_enum_item_add_separator(&item, &totitem_added); + continue; + } - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } + RNA_enum_items_add_value(&item, &totitem_added, buttons_context_items, context_tabs_array[i]); - if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TEXTURE); + /* Add the object data icon dynamically for the data tab. */ + if (context_tabs_array[i] == BCONTEXT_DATA) { + (item + totitem_added - 1)->icon = sbuts->dataicon; + } } RNA_enum_item_end(&item, &totitem); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 00bbff5cf51..0567b22d23f 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -179,6 +179,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "BKE_blender.h" # include "BKE_global.h" # include "BKE_idprop.h" +# include "BKE_image.h" # include "BKE_main.h" # include "BKE_mesh_runtime.h" # include "BKE_paint.h" @@ -187,9 +188,9 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "DEG_depsgraph.h" -# include "GPU_draw.h" # include "GPU_extensions.h" # include "GPU_select.h" +# include "GPU_texture.h" # include "BLF_api.h" @@ -363,13 +364,14 @@ static void rna_userdef_load_ui_update(Main *UNUSED(bmain), Scene *UNUSED(scene) static void rna_userdef_anisotropic_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - GPU_set_anisotropic(U.anisotropic_filter); + GPU_samplers_free(); + GPU_samplers_init(); rna_userdef_update(bmain, scene, ptr); } static void rna_userdef_gl_texture_limit_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - GPU_free_images(bmain); + BKE_image_free_all_gputextures(bmain); rna_userdef_update(bmain, scene, ptr); } @@ -4593,7 +4595,7 @@ static void rna_def_userdef_view(BlenderRNA *brna) "no matter opening direction"); static const EnumPropertyItem header_align_items[] = { - {0, "NONE", 0, "Default", "Keep existing header alignment"}, + {0, "NONE", 0, "Keep Existing", "Keep existing header alignment"}, {USER_HEADER_FROM_PREF, "TOP", 0, "Top", "Top aligned on load"}, {USER_HEADER_FROM_PREF | USER_HEADER_BOTTOM, "BOTTOM", @@ -4988,7 +4990,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna) /* grease pencil */ prop = RNA_def_property(srna, "grease_pencil_manhattan_distance", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "gp_manhattendist"); + RNA_def_property_int_sdna(prop, NULL, "gp_manhattandist"); RNA_def_property_range(prop, 0, 100); RNA_def_property_ui_text(prop, "Grease Pencil Manhattan Distance", diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 083348dfb26..746f9e56c06 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -304,7 +304,7 @@ static void deformVerts_do(HookModifierData *hmd, } if (hmd->curfalloff) { - BKE_curvemapping_initialize(hmd->curfalloff); + BKE_curvemapping_init(hmd->curfalloff); } /* Generic data needed for applying per-vertex calculations (initialize all members) */ diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 93fb7749392..7b09d3c470d 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -44,12 +44,7 @@ #include "BKE_lib_query.h" #include "BKE_mesh.h" #include "BKE_modifier.h" - -/* SpaceType struct has a member called 'new' which obviously conflicts with C++ - * so temporarily redefining the new keyword to make it compile. */ -#define new extern_new #include "BKE_screen.h" -#undef new #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 8f6676dd0b2..5513e6b4971 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -33,6 +33,8 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "MEM_guardedalloc.h" + #include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_lib_query.h" @@ -65,6 +67,9 @@ static void initData(ModifierData *md) mcmd->cache_file = NULL; mcmd->object_path[0] = '\0'; mcmd->read_flag = MOD_MESHSEQ_READ_ALL; + mcmd->velocity_scale = 1.0f; + mcmd->vertex_velocities = NULL; + mcmd->num_vertices = 0; mcmd->reader = NULL; mcmd->reader_object_path[0] = '\0'; @@ -91,6 +96,10 @@ static void freeData(ModifierData *md) mcmd->reader_object_path[0] = '\0'; BKE_cachefile_reader_free(mcmd->cache_file, &mcmd->reader); } + + if (mcmd->vertex_velocities) { + MEM_freeN(mcmd->vertex_velocities); + } } static bool isDisabled(const struct Scene *UNUSED(scene), @@ -154,6 +163,17 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * Mesh *result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag); + mcmd->velocity_delta = 1.0f; + if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_SECOND) { + mcmd->velocity_delta /= FPS; + } + + mcmd->last_lookup_time = time; + + if (result != NULL) { + mcmd->num_vertices = result->totvert; + } + if (err_str) { BKE_modifier_set_error(md, "%s", err_str); } @@ -221,6 +241,8 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(layout, &ptr, "read_data", UI_ITEM_R_EXPAND, NULL, ICON_NONE); } + uiItemR(layout, &ptr, "velocity_scale", 0, NULL, ICON_NONE); + modifier_panel_end(layout, &ptr); } diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 6374f081581..7b31886a220 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -59,7 +59,7 @@ #include "MOD_ui_common.h" #ifdef WITH_OCEANSIM -static void init_cache_data(Object *ob, struct OceanModifierData *omd) +static void init_cache_data(Object *ob, struct OceanModifierData *omd, const int resolution) { const char *relbase = BKE_modifier_path_relbase_from_global(ob); @@ -71,7 +71,7 @@ static void init_cache_data(Object *ob, struct OceanModifierData *omd) omd->chop_amount, omd->foam_coverage, omd->foam_fade, - omd->resolution); + resolution); } static void simulate_ocean_modifier(struct OceanModifierData *omd) @@ -87,7 +87,11 @@ static void initData(ModifierData *md) #ifdef WITH_OCEANSIM OceanModifierData *omd = (OceanModifierData *)md; + /* Render resolution */ omd->resolution = 7; + /* Display resolution for the non-render case */ + omd->viewport_resolution = 7; + omd->spatial_size = 50; omd->wave_alignment = 0.0; @@ -126,7 +130,7 @@ static void initData(ModifierData *md) omd->spraylayername[0] = '\0'; /* layer name empty by default */ omd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(omd->ocean, omd); + BKE_ocean_init_from_modifier(omd->ocean, omd, omd->viewport_resolution); simulate_ocean_modifier(omd); #else /* WITH_OCEANSIM */ /* unused */ @@ -164,7 +168,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla tomd->oceancache = NULL; tomd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(tomd->ocean, tomd); + BKE_ocean_init_from_modifier(tomd->ocean, tomd, tomd->viewport_resolution); simulate_ocean_modifier(tomd); #else /* WITH_OCEANSIM */ /* unused */ @@ -288,7 +292,7 @@ static void generate_ocean_geometry_uvs(void *__restrict userdata, } } -static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig) +static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, const int resolution) { Mesh *result; @@ -297,10 +301,10 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig) int num_verts; int num_polys; - const bool use_threading = omd->resolution > 4; + const bool use_threading = resolution > 4; - gogd.rx = omd->resolution * omd->resolution; - gogd.ry = omd->resolution * omd->resolution; + gogd.rx = resolution * resolution; + gogd.ry = resolution * resolution; gogd.res_x = gogd.rx * omd->repeat_x; gogd.res_y = gogd.ry * omd->repeat_y; @@ -362,6 +366,9 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes Mesh *result = NULL; OceanResult ocr; + const int resolution = (ctx->flag & MOD_APPLY_RENDER) ? omd->resolution : + omd->viewport_resolution; + MVert *mverts; int cfra_for_cache; @@ -383,7 +390,7 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes /* do ocean simulation */ if (omd->cached == true) { if (!omd->oceancache) { - init_cache_data(ob, omd); + init_cache_data(ob, omd, resolution); } BKE_ocean_simulate_cache(omd->oceancache, cfra_scene); } @@ -393,12 +400,12 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes * This function is only called on an original object when applying the modifier * using the 'Apply Modifier' button, and thus it is not called frequently for * simulation. */ - allocated_ocean |= BKE_ocean_ensure(omd); + allocated_ocean |= BKE_ocean_ensure(omd, resolution); simulate_ocean_modifier(omd); } if (omd->geometry_mode == MOD_OCEAN_GEOM_GENERATE) { - result = generate_ocean_geometry(omd, mesh); + result = generate_ocean_geometry(omd, mesh, resolution); BKE_mesh_ensure_normals(result); } else if (omd->geometry_mode == MOD_OCEAN_GEOM_DISPLACE) { @@ -558,7 +565,10 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(sub, &ptr, "repeat_x", 0, IFACE_("Repeat X"), ICON_NONE); uiItemR(sub, &ptr, "repeat_y", 0, IFACE_("Y"), ICON_NONE); } - uiItemR(col, &ptr, "resolution", 0, NULL, ICON_NONE); + + sub = uiLayoutColumn(col, true); + uiItemR(sub, &ptr, "viewport_resolution", 0, IFACE_("Resolution Viewport"), ICON_NONE); + uiItemR(sub, &ptr, "resolution", 0, IFACE_("Render"), ICON_NONE); uiItemR(col, &ptr, "time", 0, NULL, ICON_NONE); diff --git a/source/blender/modifiers/intern/MOD_simulation.cc b/source/blender/modifiers/intern/MOD_simulation.cc index d9cc9840e08..92ad02ae34a 100644 --- a/source/blender/modifiers/intern/MOD_simulation.cc +++ b/source/blender/modifiers/intern/MOD_simulation.cc @@ -46,16 +46,11 @@ #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_pointcloud.h" +#include "BKE_screen.h" #include "BKE_simulation.h" #include "BLO_read_write.h" -/* SpaceType struct has a member called 'new' which obviously conflicts with C++ - * so temporarily redefining the new keyword to make it compile. */ -#define new extern_new -#include "BKE_screen.h" -#undef new - #include "UI_interface.h" #include "UI_resources.h" @@ -114,10 +109,12 @@ static PointCloud *modifyPointCloud(ModifierData *md, const float3 *positions = (const float3 *)CustomData_get_layer_named( &state->attributes, CD_PROP_FLOAT3, "Position"); + const float *radii = (const float *)CustomData_get_layer_named( + &state->attributes, CD_PROP_FLOAT, "Radius"); memcpy(pointcloud->co, positions, sizeof(float3) * state->tot_particles); for (int i = 0; i < state->tot_particles; i++) { - pointcloud->radius[i] = 0.03f; + pointcloud->radius[i] = radii[i]; } return pointcloud; @@ -131,6 +128,9 @@ static void panel_draw(const bContext *C, Panel *panel) PointerRNA ob_ptr; modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, &ptr, "simulation", 0, NULL, ICON_NONE); uiItemR(layout, &ptr, "data_path", 0, NULL, ICON_NONE); diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index cbe774e91da..2657e3d894a 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -236,7 +236,7 @@ static void warpModifier_do(WarpModifierData *wmd, } if (wmd->curfalloff) { - BKE_curvemapping_initialize(wmd->curfalloff); + BKE_curvemapping_init(wmd->curfalloff); } invert_m4_m4(obinv, ob->obmat); diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index 54d3aa46344..4ee1f9f669a 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -83,7 +83,7 @@ void weightvg_do_map( } if (cmap && falloff_type == MOD_WVG_MAPPING_CURVE) { - BKE_curvemapping_initialize(cmap); + BKE_curvemapping_init(cmap); } /* Map each weight (vertex) to its new value, accordingly to the chosen mode. */ @@ -382,4 +382,4 @@ void weightvg_ui_common(const bContext *C, PointerRNA *ob_ptr, PointerRNA *ptr, } } } -}
\ No newline at end of file +} diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 8039856172a..6bb4f3dc1b5 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -72,7 +72,7 @@ static void initData(ModifierData *md) wmd->default_weight = 0.0f; wmd->cmap_curve = BKE_curvemapping_add(1, 0.0, 0.0, 1.0, 1.0); - BKE_curvemapping_initialize(wmd->cmap_curve); + BKE_curvemapping_init(wmd->cmap_curve); wmd->rem_threshold = 0.01f; wmd->add_threshold = 0.01f; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 80720f5206a..33b95d50cc0 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -136,6 +136,7 @@ set(SRC function/nodes/node_fn_float_compare.cc function/nodes/node_fn_group_instance_id.cc function/nodes/node_fn_object_transforms.cc + function/nodes/node_fn_random_float.cc function/nodes/node_fn_switch.cc function/node_function_util.cc @@ -160,7 +161,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_clamp.cc shader/nodes/node_shader_common.c shader/nodes/node_shader_curves.c shader/nodes/node_shader_displacement.c @@ -232,10 +233,12 @@ set(SRC shader/node_shader_tree.c shader/node_shader_util.c + simulation/nodes/node_sim_age_reached_event.cc simulation/nodes/node_sim_common.cc simulation/nodes/node_sim_emit_particles.cc simulation/nodes/node_sim_execute_condition.cc simulation/nodes/node_sim_force.cc + simulation/nodes/node_sim_kill_particle.cc simulation/nodes/node_sim_multi_execute.cc simulation/nodes/node_sim_particle_attribute.cc simulation/nodes/node_sim_particle_birth_event.cc @@ -278,6 +281,7 @@ set(SRC intern/node_common.c intern/node_exec.c intern/node_socket.cc + intern/node_tree_dependencies.cc intern/node_tree_multi_function.cc intern/node_tree_ref.cc intern/node_util.c @@ -292,6 +296,7 @@ set(SRC NOD_composite.h NOD_derived_node_tree.hh NOD_function.h + NOD_node_tree_dependencies.hh NOD_node_tree_multi_function.hh NOD_node_tree_ref.hh NOD_shader.h diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index 205ba68dd0a..570b6cb704d 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -171,7 +171,6 @@ using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>; class DerivedNodeTree : NonCopyable, NonMovable { private: LinearAllocator<> allocator_; - bNodeTree *btree_; Vector<DNode *> nodes_by_id_; Vector<DGroupInput *> group_inputs_; Vector<DParentNode *> parent_nodes_; @@ -180,7 +179,7 @@ class DerivedNodeTree : NonCopyable, NonMovable { Vector<DInputSocket *> input_sockets_; Vector<DOutputSocket *> output_sockets_; - Map<const bNodeType *, Vector<DNode *>> nodes_by_type_; + MultiValueMap<const bNodeType *, DNode *> nodes_by_type_; public: DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs); @@ -482,13 +481,7 @@ inline Span<const DNode *> DerivedNodeTree::nodes_by_type(StringRefNull idname) inline Span<const DNode *> DerivedNodeTree::nodes_by_type(const bNodeType *nodetype) const { - const Vector<DNode *> *nodes = nodes_by_type_.lookup_ptr(nodetype); - if (nodes == nullptr) { - return {}; - } - else { - return *nodes; - } + return nodes_by_type_.lookup(nodetype); } inline Span<const DSocket *> DerivedNodeTree::sockets() const diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 5391951debb..58a968151ac 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -26,6 +26,7 @@ void register_node_type_fn_switch(void); void register_node_type_fn_group_instance_id(void); void register_node_type_fn_combine_strings(void); void register_node_type_fn_object_transforms(void); +void register_node_type_fn_random_float(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_node_tree_dependencies.hh b/source/blender/nodes/NOD_node_tree_dependencies.hh new file mode 100644 index 00000000000..ca7059caa5f --- /dev/null +++ b/source/blender/nodes/NOD_node_tree_dependencies.hh @@ -0,0 +1,79 @@ +/* + * 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 __NOD_NODE_TREE_DEPENDENCIES_H__ +#define __NOD_NODE_TREE_DEPENDENCIES_H__ + +#include "BLI_vector_set.hh" + +#include "DNA_ID.h" +#include "DNA_object_types.h" + +struct bNodeTree; + +namespace blender::nodes { + +class NodeTreeDependencies { + private: + VectorSet<Object *> transform_deps_; + VectorSet<Object *> geometry_deps_; + VectorSet<ID *> id_deps_; + + public: + void add_transform_dependency(Object *object) + { + if (object == nullptr) { + return; + } + transform_deps_.add(object); + id_deps_.add(&object->id); + } + + void add_geometry_dependency(Object *object) + { + if (object == nullptr) { + return; + } + geometry_deps_.add(object); + id_deps_.add(&object->id); + } + + bool depends_on(ID *id) const + { + return id_deps_.contains(id); + } + + Span<Object *> transform_dependencies() + { + return transform_deps_; + } + + Span<Object *> geometry_dependencies() + { + return geometry_deps_; + } + + Span<ID *> id_dependencies() + { + return id_deps_; + } +}; + +NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree); + +} // namespace blender::nodes + +#endif /* __NOD_NODE_TREE_DEPENDENCIES_H__ */ diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh index f5cb827dc4f..cbd7a47f090 100644 --- a/source/blender/nodes/NOD_node_tree_multi_function.hh +++ b/source/blender/nodes/NOD_node_tree_multi_function.hh @@ -250,19 +250,17 @@ class MFNetworkBuilderBase { */ class SocketMFNetworkBuilder : public MFNetworkBuilderBase { private: - const DSocket *dsocket_ = nullptr; - const DGroupInput *group_input_ = nullptr; bNodeSocket *bsocket_; fn::MFOutputSocket *built_socket_ = nullptr; public: SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket) - : MFNetworkBuilderBase(common), dsocket_(&dsocket), bsocket_(dsocket.bsocket()) + : MFNetworkBuilderBase(common), bsocket_(dsocket.bsocket()) { } SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DGroupInput &group_input) - : MFNetworkBuilderBase(common), group_input_(&group_input), bsocket_(group_input.bsocket()) + : MFNetworkBuilderBase(common), bsocket_(group_input.bsocket()) { } diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index f18a20d6df9..6d1c239d2cb 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -46,6 +46,7 @@ #include "BLI_array.hh" #include "BLI_linear_allocator.hh" #include "BLI_map.hh" +#include "BLI_multi_value_map.hh" #include "BLI_string_ref.hh" #include "BLI_timeit.hh" #include "BLI_utility_mixins.hh" @@ -161,7 +162,7 @@ class NodeTreeRef : NonCopyable, NonMovable { Vector<SocketRef *> sockets_by_id_; Vector<InputSocketRef *> input_sockets_; Vector<OutputSocketRef *> output_sockets_; - Map<const bNodeType *, Vector<NodeRef *>> nodes_by_type_; + MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_; public: NodeTreeRef(bNodeTree *btree); @@ -410,13 +411,7 @@ inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) co inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const { - const Vector<NodeRef *> *nodes = nodes_by_type_.lookup_ptr(nodetype); - if (nodes == nullptr) { - return {}; - } - else { - return *nodes; - } + return nodes_by_type_.lookup(nodetype); } inline Span<const SocketRef *> NodeTreeRef::sockets() const diff --git a/source/blender/nodes/NOD_simulation.h b/source/blender/nodes/NOD_simulation.h index d769bbce204..266ded997c6 100644 --- a/source/blender/nodes/NOD_simulation.h +++ b/source/blender/nodes/NOD_simulation.h @@ -38,6 +38,8 @@ void register_node_type_sim_particle_mesh_collision_event(void); void register_node_type_sim_emit_particles(void); void register_node_type_sim_time(void); void register_node_type_sim_particle_attribute(void); +void register_node_type_sim_age_reached_event(void); +void register_node_type_sim_kill_particle(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 31ce3f81450..7922a73902c 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -270,6 +270,8 @@ DefNode(SimulationNode, SIM_NODE_PARTICLE_MESH_COLLISION_EVENT, 0, "PARTIC DefNode(SimulationNode, SIM_NODE_EMIT_PARTICLES, 0, "EMIT_PARTICLES", EmitParticles, "Emit Particles", "") DefNode(SimulationNode, SIM_NODE_TIME, def_sim_time, "TIME", Time, "Time", "") DefNode(SimulationNode, SIM_NODE_PARTICLE_ATTRIBUTE, def_sim_particle_attribute, "PARTICLE_ATTRIBUTE", ParticleAttribute, "Particle Attribute", "") +DefNode(SimulationNode, SIM_NODE_AGE_REACHED_EVENT, 0, "AGE_REACHED_EVENT", AgeReachedEvent, "Age Reached Event", "") +DefNode(SimulationNode, SIM_NODE_KILL_PARTICLE, 0, "KILL_PARTICLE", KillParticle, "Kill Particle", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") @@ -277,7 +279,7 @@ DefNode(FunctionNode, FN_NODE_SWITCH, def_fn_switch, "SWITCH", DefNode(FunctionNode, FN_NODE_GROUP_INSTANCE_ID, 0, "GROUP_INSTANCE_ID", GroupInstanceID, "Group Instance ID", "") DefNode(FunctionNode, FN_NODE_COMBINE_STRINGS, 0, "COMBINE_STRINGS", CombineStrings, "Combine Strings", "") DefNode(FunctionNode, FN_NODE_OBJECT_TRANSFORMS, 0, "OBJECT_TRANSFORMS", ObjectTransforms, "Object Transforms", "") - +DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") /* undefine macros */ diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc new file mode 100644 index 00000000000..2ee0830637a --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -0,0 +1,89 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "BLI_hash.h" + +static bNodeSocketTemplate fn_node_random_float_in[] = { + {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_INT, N_("Seed")}, + {-1, ""}, +}; + +static bNodeSocketTemplate fn_node_random_float_out[] = { + {SOCK_FLOAT, N_("Value")}, + {-1, ""}, +}; + +class RandomFloatFunction : public blender::fn::MultiFunction { + private: + uint32_t function_seed_; + + public: + RandomFloatFunction(uint32_t function_seed) : function_seed_(function_seed) + { + blender::fn::MFSignatureBuilder signature = this->get_builder("Random float"); + signature.single_input<float>("Min"); + signature.single_input<float>("Max"); + signature.single_input<int>("Seed"); + signature.single_output<float>("Value"); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + blender::fn::VSpan<float> min_values = params.readonly_single_input<float>(0, "Min"); + blender::fn::VSpan<float> max_values = params.readonly_single_input<float>(1, "Max"); + blender::fn::VSpan<int> seeds = params.readonly_single_input<int>(2, "Seed"); + blender::MutableSpan<float> values = params.uninitialized_single_output<float>(3, "Value"); + + for (int64_t i : mask) { + const float min_value = min_values[i]; + const float max_value = max_values[i]; + const int seed = seeds[i]; + const float value = BLI_hash_int_01((uint32_t)seed ^ function_seed_); + values[i] = value * (max_value - min_value) + min_value; + } + } +}; + +static void fn_node_random_float_expand_in_mf_network( + blender::nodes::NodeMFNetworkBuilder &builder) +{ + uint32_t function_seed = 1746872341u; + const blender::nodes::DNode &node = builder.dnode(); + const blender::DefaultHash<blender::StringRefNull> hasher; + function_seed = 33 * function_seed + hasher(node.name()); + for (const blender::nodes::DParentNode *parent = node.parent(); parent != nullptr; + parent = parent->parent()) { + function_seed = 33 * function_seed + hasher(parent->node_ref().name()); + } + + builder.construct_and_set_matching_fn<RandomFloatFunction>(function_seed); +} + +void register_node_type_fn_random_float() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); + node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); + ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index b7c78cb1499..bcef8c33a3b 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -28,7 +28,7 @@ static const NodeTreeRef &get_tree_ref(NodeTreeRefMap &node_tree_refs, bNodeTree [&]() { return std::make_unique<NodeTreeRef>(btree); }); } -DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : btree_(btree) +DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) { const NodeTreeRef &main_tree_ref = get_tree_ref(node_tree_refs, btree); @@ -321,7 +321,7 @@ BLI_NOINLINE void DerivedNodeTree::store_in_this_and_init_ids( node->id_ = node_index; const bNodeType *nodetype = node->node_ref_->bnode()->typeinfo; - nodes_by_type_.lookup_or_add_default(nodetype).append(node); + nodes_by_type_.add(nodetype, node); for (DInputSocket *socket : node->inputs_) { socket->id_ = sockets_by_id_.append_and_get_index(socket); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 04d86f5b44e..0dfae7424cb 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -634,10 +634,10 @@ static bNodeSocketType *make_socket_type_string() class ObjectSocketMultiFunction : public blender::fn::MultiFunction { private: - const Object *object_; + Object *object_; public: - ObjectSocketMultiFunction(const Object *object) : object_(object) + ObjectSocketMultiFunction(Object *object) : object_(object) { blender::fn::MFSignatureBuilder signature = this->get_builder("Object Socket"); signature.depends_on_context(); @@ -679,7 +679,7 @@ static bNodeSocketType *make_socket_type_object() return blender::fn::MFDataType::ForSingle<blender::bke::PersistentObjectHandle>(); }; socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) { - const Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value; + Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value; builder.construct_generator_fn<ObjectSocketMultiFunction>(object); }; return socktype; diff --git a/source/blender/nodes/intern/node_tree_dependencies.cc b/source/blender/nodes/intern/node_tree_dependencies.cc new file mode 100644 index 00000000000..efe75a10f7e --- /dev/null +++ b/source/blender/nodes/intern/node_tree_dependencies.cc @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "NOD_node_tree_dependencies.hh" + +#include "DNA_node_types.h" + +#include "BKE_node.h" + +namespace blender::nodes { + +static void add_dependencies_of_node_tree(bNodeTree &ntree, NodeTreeDependencies &r_dependencies) +{ + /* TODO: Do a bit more sophisticated parsing to see which dependencies are really required. */ + LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->type == SOCK_OBJECT) { + Object *object = ((bNodeSocketValueObject *)socket->default_value)->value; + if (object != nullptr) { + r_dependencies.add_transform_dependency(object); + if (object->type == OB_MESH) { + r_dependencies.add_geometry_dependency(object); + } + } + } + } + + if (node->type == NODE_GROUP) { + bNodeTree *group = (bNodeTree *)node->id; + if (group != nullptr) { + add_dependencies_of_node_tree(*group, r_dependencies); + } + } + } +} + +NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree) +{ + NodeTreeDependencies dependencies; + add_dependencies_of_node_tree(ntree, dependencies); + return dependencies; +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 186ca750f10..47669bc5ca2 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -79,7 +79,7 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) for (NodeRef *node : nodes_by_id_) { const bNodeType *nodetype = node->bnode_->typeinfo; - nodes_by_type_.lookup_or_add_default(nodetype).append(node); + nodes_by_type_.add(nodetype, node); } } diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 9efbdc079e6..b2309abe32e 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -73,7 +73,7 @@ void *node_initexec_curves(bNodeExecContext *UNUSED(context), bNode *node, bNodeInstanceKey UNUSED(key)) { - BKE_curvemapping_initialize(node->storage); + BKE_curvemapping_init(node->storage); return NULL; /* unused return */ } diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.c b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 808f9686f0a..1077f616a62 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.c +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -50,6 +50,30 @@ static int gpu_shader_clamp(GPUMaterial *mat, GPU_stack_link(mat, node, "clamp_range", in, out); } +static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{ + "Clamp (Min Max)", + [](float value, float min, float max) { return std::min(std::max(value, min), max); }}; + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> range_fn{ + "Clamp (Range)", [](float value, float a, float b) { + if (a < b) { + return clamp_f(value, a, b); + } + else { + return clamp_f(value, b, a); + } + }}; + + int clamp_type = builder.bnode().custom1; + if (clamp_type == NODE_CLAMP_MINMAX) { + builder.set_matching_fn(minmax_fn); + } + else { + builder.set_matching_fn(range_fn); + } +} + void register_node_type_sh_clamp(void) { static bNodeType ntype; @@ -58,6 +82,7 @@ void register_node_type_sh_clamp(void) node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); + ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.c index 68f252cb092..06aaf9e74cb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.c +++ b/source/blender/nodes/shader/nodes/node_shader_curves.c @@ -168,7 +168,7 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, CurveMapping *cumap = node->storage; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); BKE_curvemapping_table_RGBA(cumap, &array, &size); GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.c b/source/blender/nodes/shader/nodes/node_shader_geometry.c index deb0ee9037c..30e376f8e09 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geometry.c +++ b/source/blender/nodes/shader/nodes/node_shader_geometry.c @@ -45,6 +45,9 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; GPUNodeLink *bary_link = (!out[5].hasoutput) ? GPU_constant(val) : GPU_builtin(GPU_BARYCENTRIC_TEXCO); + if (out[5].hasoutput) { + GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); + } /* Opti: don't request orco if not needed. */ GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) : GPU_attribute(mat, CD_ORCO, ""); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c index 0cf4b51f307..38e79ebe94d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c @@ -19,8 +19,6 @@ #include "../node_shader_util.h" -#include "GPU_draw.h" - /* **************** OUTPUT ******************** */ static bNodeSocketTemplate sh_node_tex_environment_in[] = { @@ -59,7 +57,8 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, NodeTexImage *tex_original = node_original->storage; ImageUser *iuser = &tex_original->iuser; eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; - if (GPU_get_mipmap()) { + /* TODO(fclem) For now assume mipmap is always enabled. */ + if (true) { sampler |= GPU_SAMPLER_MIPMAP; } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index cbda72cd228..1a78d2f5bf2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -19,8 +19,6 @@ #include "../node_shader_util.h" -#include "GPU_draw.h" - /* **************** OUTPUT ******************** */ static bNodeSocketTemplate sh_node_tex_image_in[] = { @@ -95,7 +93,8 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, if (tex->interpolation != SHD_INTERP_CLOSEST) { sampler_state |= GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; - sampler_state |= GPU_get_mipmap() ? GPU_SAMPLER_MIPMAP : 0; + /* TODO(fclem) For now assume mipmap is always enabled. */ + sampler_state |= true ? GPU_SAMPLER_MIPMAP : 0; } const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART); diff --git a/source/blender/nodes/shader/nodes/node_shader_wireframe.c b/source/blender/nodes/shader/nodes/node_shader_wireframe.c index e1da1cd34e4..d1ed13e1ffd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_wireframe.c +++ b/source/blender/nodes/shader/nodes/node_shader_wireframe.c @@ -36,6 +36,7 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { + GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); /* node->custom1 is use_pixel_size */ if (node->custom1) { return GPU_stack_link( diff --git a/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc b/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc new file mode 100644 index 00000000000..add8c4eba4d --- /dev/null +++ b/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_simulation_util.h" + +static bNodeSocketTemplate sim_node_age_reached_event_in[] = { + {SOCK_FLOAT, N_("Age"), 3, 0, 0, 0, 0, 10000000}, + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate sim_node_age_reached_event_out[] = { + {SOCK_EVENTS, N_("Event")}, + {-1, ""}, +}; + +void register_node_type_sim_age_reached_event() +{ + static bNodeType ntype; + + sim_node_type_base(&ntype, SIM_NODE_AGE_REACHED_EVENT, "Age Reached Event", 0, 0); + node_type_socket_templates( + &ntype, sim_node_age_reached_event_in, sim_node_age_reached_event_out); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc b/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc new file mode 100644 index 00000000000..793b40d9365 --- /dev/null +++ b/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_simulation_util.h" + +static bNodeSocketTemplate sim_node_kill_particle_in[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate sim_node_kill_particle_out[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +void register_node_type_sim_kill_particle() +{ + static bNodeType ntype; + + sim_node_type_base(&ntype, SIM_NODE_KILL_PARTICLE, "Kill Particle", 0, 0); + node_type_socket_templates(&ntype, sim_node_kill_particle_in, sim_node_kill_particle_out); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc index 2de7be2d3eb..5e1a6c35d52 100644 --- a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc +++ b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc @@ -20,7 +20,8 @@ static bNodeSocketTemplate sim_node_particle_mesh_emitter_in[] = { {SOCK_OBJECT, N_("Object")}, - {SOCK_FLOAT, N_("Rate"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_FLOAT, N_("Rate"), 100.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_CONTROL_FLOW, N_("Execute")}, {-1, ""}, }; diff --git a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc index 8696dbe340c..8f5c6818cb4 100644 --- a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc +++ b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc @@ -18,6 +18,7 @@ #include "node_simulation_util.h" static bNodeSocketTemplate sim_node_set_particle_attribute_in[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, {SOCK_STRING, N_("Name")}, {SOCK_FLOAT, N_("Float"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, {SOCK_INT, N_("Int"), 0, 0, 0, 0, -10000, 10000}, @@ -38,7 +39,7 @@ static void sim_node_set_particle_attribute_update(bNodeTree *UNUSED(ntree), bNo { int index = 0; LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (index >= 1) { + if (index >= 2) { nodeSetSocketAvailability(sock, sock->type == node->custom1); } index++; diff --git a/source/blender/nodes/texture/nodes/node_texture_curves.c b/source/blender/nodes/texture/nodes/node_texture_curves.c index d42985ba041..70f7e731720 100644 --- a/source/blender/nodes/texture/nodes/node_texture_curves.c +++ b/source/blender/nodes/texture/nodes/node_texture_curves.c @@ -39,7 +39,7 @@ static void time_colorfn( fac = (p->cfra - node->custom1) / (float)(node->custom2 - node->custom1); } - BKE_curvemapping_initialize(node->storage); + BKE_curvemapping_init(node->storage); fac = BKE_curvemapping_evaluateF(node->storage, 0, fac); out[0] = CLAMPIS(fac, 0.0f, 1.0f); } diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index da9b5d642ef..7bcf96116b9 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -82,23 +82,23 @@ bool BPY_execute_text(struct bContext *C, bool BPY_execute_string_as_number(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, double *r_value); bool BPY_execute_string_as_intptr(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, intptr_t *r_value); bool BPY_execute_string_as_string_and_size(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, char **r_value, size_t *r_value_size); bool BPY_execute_string_as_string(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, char **r_value); bool BPY_execute_string_ex(struct bContext *C, diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 37ed96bcaa0..543cd3b4214 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -51,6 +51,10 @@ # include "BLI_math_base.h" /* isfinite() */ #endif +/* -------------------------------------------------------------------- */ +/** \name Fast Python to C Array Conversion for Primitive Types + * \{ */ + /* array utility function */ int PyC_AsArray_FAST(void *array, PyObject *value_fast, @@ -137,11 +141,12 @@ int PyC_AsArray(void *array, return ret; } +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Typed Tuple Packing * * \note See #PyC_Tuple_Pack_* macros that take multiple arguments. - * * \{ */ /* array utility function */ @@ -192,6 +197,10 @@ PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Tuple/List Filling + * \{ */ + /** * Caller needs to ensure tuple is uninitialized. * Handy for filling a tuple with None for eg. @@ -218,6 +227,12 @@ void PyC_List_Fill(PyObject *list, PyObject *value) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Bool/Enum Argument Parsing + * \{ */ + /** * Use with PyArg_ParseTuple's "O&" formatting. * @@ -274,8 +289,16 @@ int PyC_CheckArgs_DeepCopy(PyObject *args) return PyArg_ParseTuple(args, "|O!:__deepcopy__", &PyDict_Type, &dummy_pydict) != 0; } +/** \} */ + #ifndef MATH_STANDALONE +/* -------------------------------------------------------------------- */ +/** \name Simple Printing (for debugging) + * + * These are useful to run directly from a debugger to be able to inspect the state. + * \{ */ + /* for debugging */ void PyC_ObSpit(const char *name, PyObject *var) { @@ -374,6 +397,12 @@ void PyC_StackPrint(/* FILE */ void *fp) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Access Current Frame File Name & Line Number + * \{ */ + void PyC_FileAndNum(const char **r_filename, int *r_lineno) { PyFrameObject *frame; @@ -433,6 +462,12 @@ void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno) PyC_FileAndNum(r_filename, r_lineno); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Access Utilities + * \{ */ + /* Would be nice if python had this built in */ PyObject *PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...) { @@ -461,6 +496,12 @@ PyObject *PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...) return item; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Frozen Set Creation + * \{ */ + PyObject *PyC_FrozenSetFromStrings(const char **strings) { const char **str; @@ -477,6 +518,12 @@ PyObject *PyC_FrozenSetFromStrings(const char **strings) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Exception Utilities + * \{ */ + /** * Similar to #PyErr_Format(), * @@ -542,6 +589,12 @@ void PyC_Err_PrintWithFunc(PyObject *py_func) _PyUnicode_AsString(((PyFunctionObject *)py_func)->func_name)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Exception Buffer Access + * \{ */ + /* returns the exception string as a new PyUnicode object, depends on external traceback module */ # if 0 @@ -648,7 +701,7 @@ error_cleanup: PyObject *PyC_ExceptionBuffer_Simple(void) { - PyObject *string_io_buf; + PyObject *string_io_buf = NULL; PyObject *error_type, *error_value, *error_traceback; @@ -662,7 +715,19 @@ PyObject *PyC_ExceptionBuffer_Simple(void) return NULL; } - string_io_buf = PyObject_Str(error_value); + if (PyErr_GivenExceptionMatches(error_type, PyExc_SyntaxError)) { + /* Special exception for syntax errors, + * in these cases the full error is verbose and not very useful, + * just use the initial text so we know what the error is. */ + if (PyTuple_CheckExact(error_value) && PyTuple_GET_SIZE(error_value) >= 1) { + string_io_buf = PyObject_Str(PyTuple_GET_ITEM(error_value, 0)); + } + } + + if (string_io_buf == NULL) { + string_io_buf = PyObject_Str(error_value); + } + /* Python does this too */ if (UNLIKELY(string_io_buf == NULL)) { string_io_buf = PyUnicode_FromFormat("<unprintable %s object>", Py_TYPE(error_value)->tp_name); @@ -675,6 +740,14 @@ PyObject *PyC_ExceptionBuffer_Simple(void) return string_io_buf; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unicode Conversion + * + * In some cases we need to coerce strings, avoid doing this inline. + * \{ */ + /* string conversion, escape non-unicode chars, coerce must be set to NULL */ const char *PyC_UnicodeAsByteAndSize(PyObject *py_str, Py_ssize_t *size, PyObject **coerce) { @@ -753,6 +826,12 @@ PyObject *PyC_UnicodeFromByte(const char *str) return PyC_UnicodeFromByteAndSize(str, strlen(str)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Name Space Creation/Manipulation + * \{ */ + /***************************************************************************** * Description: This function creates a new Python dictionary object. * note: dict is owned by sys.modules["__main__"] module, reference is borrowed @@ -818,8 +897,18 @@ void PyC_MainModule_Restore(PyObject *main_mod) Py_XDECREF(main_mod); } -/* Must be called before Py_Initialize, - * expects output of BKE_appdir_folder_id(BLENDER_PYTHON, NULL). */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Py_SetPythonHome Wrapper + * \{ */ + +/** + * - Must be called before #Py_Initialize. + * - Expects output of `BKE_appdir_folder_id(BLENDER_PYTHON, NULL)`. + * - Note that the `PYTHONPATH` environment variable isn't reliable, see T31506. + Use #Py_SetPythonHome instead. + */ void PyC_SetHomePath(const char *py_path_bundle) { if (py_path_bundle == NULL) { @@ -838,24 +927,14 @@ void PyC_SetHomePath(const char *py_path_bundle) /* OSX allow file/directory names to contain : character (represented as / in the Finder) * but current Python lib (release 3.1.1) doesn't handle these correctly */ if (strchr(py_path_bundle, ':')) { - printf( - "Warning : Blender application is located in a path containing : or / chars\ - \nThis may make python import function fail\n"); - } -# endif - -# if 0 /* disable for now [#31506] - campbell */ -# ifdef _WIN32 - /* cmake/MSVC debug build crashes without this, why only - * in this case is unknown.. */ - { - /*BLI_setenv("PYTHONPATH", py_path_bundle)*/; + fprintf(stderr, + "Warning! Blender application is located in a path containing ':' or '/' chars\n" + "This may make python import function fail\n"); } -# endif # endif { - static wchar_t py_path_bundle_wchar[1024]; + wchar_t py_path_bundle_wchar[1024]; /* Can't use this, on linux gives bug: #23018, * TODO: try LANG="en_US.UTF-8" /usr/bin/blender, suggested 2008 */ @@ -875,6 +954,12 @@ bool PyC_IsInterpreterActive(void) return (PyThreadState_GetDict() != NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Py_SetPythonHome Wrapper + * \{ */ + /* Would be nice if python had this built in * See: https://wiki.blender.org/wiki/Tools/Debugging/PyFromC */ @@ -1060,6 +1145,14 @@ void *PyC_RNA_AsPointer(PyObject *value, const char *type_name) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Flag Set Utilities (#PyC_FlagSet) + * + * Convert to/from Python set of strings to an int flag. + * \{ */ + PyObject *PyC_FlagSet_AsString(PyC_FlagSet *item) { PyObject *py_items = PyList_New(0); @@ -1160,6 +1253,12 @@ PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Run String (Evaluate to Primitive Types) + * \{ */ + /** * \return success * @@ -1323,6 +1422,8 @@ bool PyC_RunString_AsString(const char *imports[], return PyC_RunString_AsStringAndSize(imports, expr, filename, r_value, &value_size); } +/** \} */ + #endif /* #ifndef MATH_STANDALONE */ /* -------------------------------------------------------------------- */ @@ -1410,6 +1511,12 @@ uint32_t PyC_Long_AsU32(PyObject *value) * PyC_Long_AsU64 */ +#ifdef __GNUC__ +# pragma warning(pop) +#endif + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Py_buffer Utils * @@ -1486,9 +1593,3 @@ bool PyC_StructFmt_type_is_bool(char format) } /** \} */ - -#ifdef __GNUC__ -# pragma warning(pop) -#endif - -/** \} */ diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c index 1379c0e557a..da7674eb7f9 100644 --- a/source/blender/python/gpu/gpu_py_api.c +++ b/source/blender/python/gpu/gpu_py_api.c @@ -43,9 +43,9 @@ /** \name Utils to invalidate functions * \{ */ -bool bpygpu_is_initialized_or_error(void) +bool bpygpu_is_init_or_error(void) { - if (!GPU_is_initialized()) { + if (!GPU_is_init()) { PyErr_SetString(PyExc_SystemError, "GPU functions for drawing are not available in background mode"); diff --git a/source/blender/python/gpu/gpu_py_api.h b/source/blender/python/gpu/gpu_py_api.h index 2360bba1f5d..b8f0cde129f 100644 --- a/source/blender/python/gpu/gpu_py_api.h +++ b/source/blender/python/gpu/gpu_py_api.h @@ -24,14 +24,14 @@ int bpygpu_ParsePrimType(PyObject *o, void *p); PyObject *BPyInit_gpu(void); -bool bpygpu_is_initialized_or_error(void); +bool bpygpu_is_init_or_error(void); #define BPYGPU_IS_INIT_OR_ERROR_OBJ \ - if (UNLIKELY(!bpygpu_is_initialized_or_error())) { \ + if (UNLIKELY(!bpygpu_is_init_or_error())) { \ return NULL; \ } \ ((void)0) #define BPYGPU_IS_INIT_OR_ERROR_INT \ - if (UNLIKELY(!bpygpu_is_initialized_or_error())) { \ + if (UNLIKELY(!bpygpu_is_init_or_error())) { \ return -1; \ } \ ((void)0) diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c index b3df991cf12..f9fe1654db0 100644 --- a/source/blender/python/gpu/gpu_py_batch.c +++ b/source/blender/python/gpu/gpu_py_batch.c @@ -184,8 +184,7 @@ static PyObject *bpygpu_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_sh } GPUShader *shader = py_shader->shader; - GPU_batch_program_set( - self->batch, GPU_shader_get_program(shader), GPU_shader_get_interface(shader)); + GPU_batch_set_shader(self->batch, shader); #ifdef USE_GPU_PY_REFERENCES /* Remove existing user (if any), hold new user. */ @@ -229,9 +228,7 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args) } } else if (self->batch->program != GPU_shader_get_program(py_program->shader)) { - GPU_batch_program_set(self->batch, - GPU_shader_get_program(py_program->shader), - GPU_shader_get_interface(py_program->shader)); + GPU_batch_set_shader(self->batch, py_program->shader); } GPU_batch_draw(self->batch); diff --git a/source/blender/python/intern/bpy_capi_utils.c b/source/blender/python/intern/bpy_capi_utils.c index 89ef2f40a30..27eea80f1f6 100644 --- a/source/blender/python/intern/bpy_capi_utils.c +++ b/source/blender/python/intern/bpy_capi_utils.c @@ -97,7 +97,10 @@ void BPy_reports_write_stdout(const ReportList *reports, const char *header) } } -bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const bool use_location) +bool BPy_errors_to_report_ex(ReportList *reports, + const char *error_prefix, + const bool use_full, + const bool use_location) { PyObject *pystring; @@ -124,40 +127,38 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo return 0; } + if (error_prefix == NULL) { + /* Not very helpful, better than nothing. */ + error_prefix = "Python"; + } + if (use_location) { const char *filename; int lineno; - PyObject *pystring_format; /* workaround, see below */ - const char *cstring; - PyC_FileAndNum(&filename, &lineno); if (filename == NULL) { filename = "<unknown location>"; } -#if 0 /* ARG!. workaround for a bug in blenders use of vsnprintf */ BKE_reportf(reports, RPT_ERROR, - "%s\nlocation: %s:%d\n", + TIP_("%s: %s\nlocation: %s:%d\n"), + error_prefix, _PyUnicode_AsString(pystring), filename, lineno); -#else - pystring_format = PyUnicode_FromFormat( - TIP_("%s\nlocation: %s:%d\n"), _PyUnicode_AsString(pystring), filename, lineno); - - cstring = _PyUnicode_AsString(pystring_format); - BKE_report(reports, RPT_ERROR, cstring); - - /* not exactly needed. just for testing */ - fprintf(stderr, TIP_("%s\nlocation: %s:%d\n"), cstring, filename, lineno); - Py_DECREF(pystring_format); /* workaround */ -#endif + /* Not exactly needed. Useful for developers tracking down issues. */ + fprintf(stderr, + TIP_("%s: %s\nlocation: %s:%d\n"), + error_prefix, + _PyUnicode_AsString(pystring), + filename, + lineno); } else { - BKE_report(reports, RPT_ERROR, _PyUnicode_AsString(pystring)); + BKE_reportf(reports, RPT_ERROR, "%s: %s", error_prefix, _PyUnicode_AsString(pystring)); } Py_DECREF(pystring); @@ -166,5 +167,5 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo bool BPy_errors_to_report(ReportList *reports) { - return BPy_errors_to_report_ex(reports, true, true); + return BPy_errors_to_report_ex(reports, NULL, true, true); } diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h index c021ec14933..861b23190a2 100644 --- a/source/blender/python/intern/bpy_capi_utils.h +++ b/source/blender/python/intern/bpy_capi_utils.h @@ -42,8 +42,10 @@ char *BPy_enum_as_string(const struct EnumPropertyItem *item); short BPy_reports_to_error(struct ReportList *reports, PyObject *exception, const bool clear); void BPy_reports_write_stdout(const struct ReportList *reports, const char *header); bool BPy_errors_to_report_ex(struct ReportList *reports, + const char *error_prefix, const bool use_full, const bool use_location); +bool BPy_errors_to_report_brief_with_prefix(struct ReportList *reports, const char *error_prefix); bool BPy_errors_to_report(struct ReportList *reports); /* TODO - find a better solution! */ diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 5e2162c9e2d..d48ad2b197c 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -411,8 +411,10 @@ static PyObject *bpy_pydriver_depsgraph_as_pyobject(struct Depsgraph *depsgraph) return pyrna_struct_CreatePyObject(&depsgraph_ptr); } -/* Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated - * datablocks, and the current view layer and scene. See T75553. */ +/** + * Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated + * data-blocks, and the current view layer and scene. See T75553. + */ static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars, struct Depsgraph *depsgraph) { @@ -428,17 +430,18 @@ static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars, } } -/* This evals py driver expressions, 'expr' is a Python expression that - * should evaluate to a float number, which is returned. +/** + * This evaluates Python driver expressions, `driver_orig->expression` + * is a Python expression that should evaluate to a float number, which is returned. * * (old)note: PyGILState_Ensure() isn't always called because python can call * the bake operator which intern starts a thread which calls scene update - * which does a driver update. to avoid a deadlock check PyC_IsInterpreterActive() - * if PyGILState_Ensure() is needed - see [#27683] + * which does a driver update. to avoid a deadlock check #PyC_IsInterpreterActive() + * if #PyGILState_Ensure() is needed, see T27683. * - * (new)note: checking if python is running is not threadsafe [#28114] + * (new)note: checking if python is running is not thread-safe T28114 * now release the GIL on python operator execution instead, using - * PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender. + * #PyEval_SaveThread() / #PyEval_RestoreThread() so we don't lock up blender. * * For copy-on-write we always cache expressions and write errors in the * original driver, otherwise these would get freed while editing. Due to diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 4fbe2db3ecd..c311041e4cb 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -258,11 +258,13 @@ void BPY_python_start(int argc, const char **argv) PyThreadState *py_tstate = NULL; const char *py_path_bundle = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, NULL); - /* not essential but nice to set our name */ - static wchar_t program_path_wchar[FILE_MAX]; /* python holds a reference */ - BLI_strncpy_wchar_from_utf8( - program_path_wchar, BKE_appdir_program_path(), ARRAY_SIZE(program_path_wchar)); - Py_SetProgramName(program_path_wchar); + /* Not essential but nice to set our name. */ + { + const char *program_path = BKE_appdir_program_path(); + wchar_t program_path_wchar[FILE_MAX]; + BLI_strncpy_wchar_from_utf8(program_path_wchar, program_path, ARRAY_SIZE(program_path_wchar)); + Py_SetProgramName(program_path_wchar); + } /* must run before python initializes */ PyImport_ExtendInittab(bpy_internal_modules); @@ -621,8 +623,11 @@ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr) /** * \return success */ -bool BPY_execute_string_as_number( - bContext *C, const char *imports[], const char *expr, const bool verbose, double *r_value) +bool BPY_execute_string_as_number(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + double *r_value) { PyGILState_STATE gilstate; bool ok = true; @@ -641,8 +646,8 @@ bool BPY_execute_string_as_number( ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); } else { PyErr_Clear(); @@ -660,7 +665,7 @@ bool BPY_execute_string_as_number( bool BPY_execute_string_as_string_and_size(bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, char **r_value, size_t *r_value_size) { @@ -678,8 +683,8 @@ bool BPY_execute_string_as_string_and_size(bContext *C, ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix); } else { PyErr_Clear(); @@ -691,12 +696,15 @@ bool BPY_execute_string_as_string_and_size(bContext *C, return ok; } -bool BPY_execute_string_as_string( - bContext *C, const char *imports[], const char *expr, const bool verbose, char **r_value) +bool BPY_execute_string_as_string(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value) { size_t value_dummy_size; return BPY_execute_string_as_string_and_size( - C, imports, expr, verbose, r_value, &value_dummy_size); + C, imports, expr, report_prefix, r_value, &value_dummy_size); } /** @@ -704,8 +712,11 @@ bool BPY_execute_string_as_string( * * \return success */ -bool BPY_execute_string_as_intptr( - bContext *C, const char *imports[], const char *expr, const bool verbose, intptr_t *r_value) +bool BPY_execute_string_as_intptr(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + intptr_t *r_value) { BLI_assert(r_value && expr); PyGILState_STATE gilstate; @@ -721,8 +732,8 @@ bool BPY_execute_string_as_intptr( ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); } else { PyErr_Clear(); @@ -916,8 +927,11 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * /* TODO, reloading the module isn't functional at the moment. */ static void bpy_module_free(void *mod); + +/* Defined in 'creator.c' when building as a Python module. */ extern int main_python_enter(int argc, const char **argv); extern void main_python_exit(void); + static struct PyModuleDef bpy_proxy_def = { PyModuleDef_HEAD_INIT, "bpy", /* m_name */ diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index ade80898131..fa53ad522a6 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1879,14 +1879,14 @@ const char *RE_GetActiveRenderView(Render *re) } /* evaluating scene options for general Blender render */ -static int render_initialize_from_main(Render *re, - const RenderData *rd, - Main *bmain, - Scene *scene, - ViewLayer *single_layer, - Object *camera_override, - int anim, - int anim_init) +static int render_init_from_main(Render *re, + const RenderData *rd, + Main *bmain, + Scene *scene, + ViewLayer *single_layer, + Object *camera_override, + int anim, + int anim_init) { int winx, winy; rcti disprect; @@ -2004,8 +2004,7 @@ void RE_RenderFrame(Render *re, scene->r.cfra = frame; - if (render_initialize_from_main( - re, &scene->r, bmain, scene, single_layer, camera_override, 0, 0)) { + if (render_init_from_main(re, &scene->r, bmain, scene, single_layer, camera_override, 0, 0)) { const RenderData rd = scene->r; MEM_reset_peak_memory(); @@ -2058,7 +2057,7 @@ void RE_RenderFrame(Render *re, void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, int render) { re->result_ok = 0; - if (render_initialize_from_main(re, &scene->r, bmain, scene, NULL, NULL, 0, 0)) { + if (render_init_from_main(re, &scene->r, bmain, scene, NULL, NULL, 0, 0)) { if (render) { do_render_3d(re); } @@ -2422,7 +2421,7 @@ void RE_RenderAnim(Render *re, (rd.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)); /* do not fully call for each frame, it initializes & pops output window */ - if (!render_initialize_from_main(re, &rd, bmain, scene, single_layer, camera_override, 0, 1)) { + if (!render_init_from_main(re, &rd, bmain, scene, single_layer, camera_override, 0, 1)) { return; } @@ -2501,7 +2500,7 @@ void RE_RenderAnim(Render *re, render_update_depsgraph(re); /* only border now, todo: camera lens. (ton) */ - render_initialize_from_main(re, &rd, bmain, scene, single_layer, camera_override, 1, 0); + render_init_from_main(re, &rd, bmain, scene, single_layer, camera_override, 1, 0); if (nfra != scene->r.cfra) { /* Skip this frame, but could update for physics and particles system. */ diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c index 8daad33b477..c49cf6203e0 100644 --- a/source/blender/render/intern/source/pointdensity.c +++ b/source/blender/render/intern/source/pointdensity.c @@ -557,7 +557,7 @@ static float density_falloff(PointDensityRangeData *pdr, int index, float square } if (pdr->density_curve && dist != 0.0f) { - BKE_curvemapping_initialize(pdr->density_curve); + BKE_curvemapping_init(pdr->density_curve); density = BKE_curvemapping_evaluateF(pdr->density_curve, 0, density / dist) * dist; } diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt index 243b056db74..cbc6ee65303 100644 --- a/source/blender/simulation/CMakeLists.txt +++ b/source/blender/simulation/CMakeLists.txt @@ -43,7 +43,9 @@ set(SRC intern/implicit_eigen.cpp intern/particle_allocator.cc intern/particle_function.cc + intern/particle_mesh_emitter.cc intern/simulation_collect_influences.cc + intern/simulation_solver_influences.cc intern/simulation_solver.cc intern/simulation_update.cc @@ -52,7 +54,9 @@ set(SRC intern/implicit.h intern/particle_allocator.hh intern/particle_function.hh + intern/particle_mesh_emitter.hh intern/simulation_collect_influences.hh + intern/simulation_solver_influences.hh intern/simulation_solver.hh intern/time_interval.hh diff --git a/source/blender/simulation/SIM_simulation_update.hh b/source/blender/simulation/SIM_simulation_update.hh index 2c64fdec02e..7c2726f038e 100644 --- a/source/blender/simulation/SIM_simulation_update.hh +++ b/source/blender/simulation/SIM_simulation_update.hh @@ -26,4 +26,6 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, Scene *scene_cow, Simulation *simulation_cow); -} +bool update_simulation_dependencies(Simulation *simulation); + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/particle_allocator.cc b/source/blender/simulation/intern/particle_allocator.cc index eb1e998e63a..e47a6354d81 100644 --- a/source/blender/simulation/intern/particle_allocator.cc +++ b/source/blender/simulation/intern/particle_allocator.cc @@ -16,6 +16,8 @@ #include "particle_allocator.hh" +#include "BLI_rand.hh" + namespace blender::sim { AttributesAllocator::~AttributesAllocator() @@ -67,8 +69,15 @@ fn::MutableAttributesRef ParticleAllocator::allocate(int size) ids[pindex] = start_id + pindex; } } + else if (name == "Hash") { + MutableSpan<int> hashes = attributes.get<int>("Hash"); + RandomNumberGenerator rng(hash_seed_ ^ (uint32_t)next_id_); + for (int pindex : IndexRange(size)) { + hashes[pindex] = (int)rng.get_uint32(); + } + } else { - type.fill_uninitialized(info.default_of(i), attributes.get(i).buffer(), size); + type.fill_uninitialized(info.default_of(i), attributes.get(i).data(), size); } } return attributes; diff --git a/source/blender/simulation/intern/particle_allocator.hh b/source/blender/simulation/intern/particle_allocator.hh index ae23c8c8238..c0bbdb845d9 100644 --- a/source/blender/simulation/intern/particle_allocator.hh +++ b/source/blender/simulation/intern/particle_allocator.hh @@ -69,10 +69,11 @@ class ParticleAllocator : NonCopyable, NonMovable { private: AttributesAllocator attributes_allocator_; std::atomic<int> next_id_; + uint32_t hash_seed_; public: - ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id) - : attributes_allocator_(attributes_info), next_id_(next_id) + ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id, uint32_t hash_seed) + : attributes_allocator_(attributes_info), next_id_(next_id), hash_seed_(hash_seed) { } diff --git a/source/blender/simulation/intern/particle_function.cc b/source/blender/simulation/intern/particle_function.cc index 935ef7983d9..035e6d50e21 100644 --- a/source/blender/simulation/intern/particle_function.cc +++ b/source/blender/simulation/intern/particle_function.cc @@ -49,19 +49,17 @@ ParticleFunction::ParticleFunction(const fn::MultiFunction *global_fn, } } -ParticleFunctionEvaluator::ParticleFunctionEvaluator( - const ParticleFunction &particle_fn, - const SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context) +ParticleFunctionEvaluator::ParticleFunctionEvaluator(const ParticleFunction &particle_fn, + const SimulationSolveContext &solve_context, + const ParticleChunkContext &particles) : particle_fn_(particle_fn), solve_context_(solve_context), - particle_chunk_context_(particle_chunk_context), - mask_(particle_chunk_context_.index_mask()), + particles_(particles), + mask_(particles_.index_mask), outputs_(particle_fn_.output_types_.size(), nullptr) { - global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map()); - per_particle_context_.add_global_context("PersistentDataHandleMap", - &solve_context_.handle_map()); + global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map); + per_particle_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map); } ParticleFunctionEvaluator::~ParticleFunctionEvaluator() @@ -92,8 +90,10 @@ void ParticleFunctionEvaluator::compute() fn::GVSpan ParticleFunctionEvaluator::get(int output_index, StringRef expected_name) const { #ifdef DEBUG - StringRef real_name = particle_fn_.output_names_[output_index]; - BLI_assert(expected_name == real_name); + if (expected_name != "") { + StringRef real_name = particle_fn_.output_names_[output_index]; + BLI_assert(expected_name == real_name); + } BLI_assert(is_computed_); #endif UNUSED_VARS_NDEBUG(expected_name); @@ -116,8 +116,9 @@ void ParticleFunctionEvaluator::compute_globals() fn::MFParamsBuilder params(*particle_fn_.global_fn_, mask_.min_array_size()); /* Add input parameters. */ + ParticleFunctionInputContext input_context{solve_context_, particles_}; for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) { - input->add_input(particle_chunk_context_.attributes(), params, resources_); + input->add_input(input_context, params, resources_); } /* Add output parameters. */ @@ -143,8 +144,9 @@ void ParticleFunctionEvaluator::compute_per_particle() fn::MFParamsBuilder params(*particle_fn_.per_particle_fn_, mask_.min_array_size()); /* Add input parameters. */ + ParticleFunctionInputContext input_context{solve_context_, particles_}; for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) { - input->add_input(particle_chunk_context_.attributes(), params, resources_); + input->add_input(input_context, params, resources_); } /* Add output parameters. */ diff --git a/source/blender/simulation/intern/particle_function.hh b/source/blender/simulation/intern/particle_function.hh index cc8ccbd8243..ead4e6f3c31 100644 --- a/source/blender/simulation/intern/particle_function.hh +++ b/source/blender/simulation/intern/particle_function.hh @@ -25,10 +25,15 @@ namespace blender::sim { +struct ParticleFunctionInputContext { + const SimulationSolveContext &solve_context; + const ParticleChunkContext &particles; +}; + class ParticleFunctionInput { public: virtual ~ParticleFunctionInput() = default; - virtual void add_input(fn::AttributesRef attributes, + virtual void add_input(ParticleFunctionInputContext &context, fn::MFParamsBuilder ¶ms, ResourceCollector &resources) const = 0; }; @@ -60,7 +65,7 @@ class ParticleFunctionEvaluator { ResourceCollector resources_; const ParticleFunction &particle_fn_; const SimulationSolveContext &solve_context_; - const ParticleChunkContext &particle_chunk_context_; + const ParticleChunkContext &particles_; IndexMask mask_; fn::MFContextBuilder global_context_; fn::MFContextBuilder per_particle_context_; @@ -70,13 +75,13 @@ class ParticleFunctionEvaluator { public: ParticleFunctionEvaluator(const ParticleFunction &particle_fn, const SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context); + const ParticleChunkContext &particles); ~ParticleFunctionEvaluator(); void compute(); - fn::GVSpan get(int output_index, StringRef expected_name) const; + fn::GVSpan get(int output_index, StringRef expected_name = "") const; - template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name) const + template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name = "") const { return this->get(output_index, expected_name).typed<T>(); } diff --git a/source/blender/simulation/intern/particle_mesh_emitter.cc b/source/blender/simulation/intern/particle_mesh_emitter.cc new file mode 100644 index 00000000000..26541d550eb --- /dev/null +++ b/source/blender/simulation/intern/particle_mesh_emitter.cc @@ -0,0 +1,362 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "particle_mesh_emitter.hh" + +#include "BLI_float4x4.hh" +#include "BLI_rand.hh" +#include "BLI_vector_adaptor.hh" + +#include "BKE_mesh_runtime.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +namespace blender::sim { + +ParticleMeshEmitter::~ParticleMeshEmitter() = default; + +struct EmitterSettings { + Object *object; + float rate; +}; + +static BLI_NOINLINE void compute_birth_times(float rate, + TimeInterval emit_interval, + ParticleMeshEmitterSimulationState &state, + Vector<float> &r_birth_times) +{ + const float time_between_particles = 1.0f / rate; + int counter = 0; + while (true) { + counter++; + const float time_offset = counter * time_between_particles; + const float birth_time = state.last_birth_time + time_offset; + if (birth_time > emit_interval.stop()) { + break; + } + if (birth_time <= emit_interval.start()) { + continue; + } + r_birth_times.append(birth_time); + } +} + +static BLI_NOINLINE Span<MLoopTri> get_mesh_triangles(Mesh &mesh) +{ + const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(&mesh); + int amount = BKE_mesh_runtime_looptri_len(&mesh); + return Span(triangles, amount); +} + +static BLI_NOINLINE void compute_triangle_areas(Mesh &mesh, + Span<MLoopTri> triangles, + MutableSpan<float> r_areas) +{ + assert_same_size(triangles, r_areas); + + for (int i : triangles.index_range()) { + const MLoopTri &tri = triangles[i]; + + const float3 v1 = mesh.mvert[mesh.mloop[tri.tri[0]].v].co; + const float3 v2 = mesh.mvert[mesh.mloop[tri.tri[1]].v].co; + const float3 v3 = mesh.mvert[mesh.mloop[tri.tri[2]].v].co; + + const float area = area_tri_v3(v1, v2, v3); + r_areas[i] = area; + } +} + +static BLI_NOINLINE void compute_triangle_weights(Mesh &mesh, + Span<MLoopTri> triangles, + MutableSpan<float> r_weights) +{ + assert_same_size(triangles, r_weights); + compute_triangle_areas(mesh, triangles, r_weights); +} + +static BLI_NOINLINE void compute_cumulative_distribution(Span<float> weights, + MutableSpan<float> r_cumulative_weights) +{ + BLI_assert(weights.size() + 1 == r_cumulative_weights.size()); + + r_cumulative_weights[0] = 0; + for (int i : weights.index_range()) { + r_cumulative_weights[i + 1] = r_cumulative_weights[i] + weights[i]; + } +} + +static void sample_cumulative_distribution_recursive(RandomNumberGenerator &rng, + int amount, + int start, + int one_after_end, + Span<float> cumulative_weights, + VectorAdaptor<int> &r_sampled_indices) +{ + BLI_assert(start <= one_after_end); + const int size = one_after_end - start; + if (size == 0) { + BLI_assert(amount == 0); + } + else if (amount == 0) { + return; + } + else if (size == 1) { + r_sampled_indices.append_n_times(start, amount); + } + else { + const int middle = start + size / 2; + const float left_weight = cumulative_weights[middle] - cumulative_weights[start]; + const float right_weight = cumulative_weights[one_after_end] - cumulative_weights[middle]; + BLI_assert(left_weight >= 0.0f && right_weight >= 0.0f); + const float weight_sum = left_weight + right_weight; + BLI_assert(weight_sum > 0.0f); + + const float left_factor = left_weight / weight_sum; + const float right_factor = right_weight / weight_sum; + + int left_amount = amount * left_factor; + int right_amount = amount * right_factor; + + if (left_amount + right_amount < amount) { + BLI_assert(left_amount + right_amount + 1 == amount); + const float weight_per_item = weight_sum / amount; + const float total_remaining_weight = weight_sum - + (left_amount + right_amount) * weight_per_item; + const float left_remaining_weight = left_weight - left_amount * weight_per_item; + const float left_remaining_factor = left_remaining_weight / total_remaining_weight; + if (rng.get_float() < left_remaining_factor) { + left_amount++; + } + else { + right_amount++; + } + } + + sample_cumulative_distribution_recursive( + rng, left_amount, start, middle, cumulative_weights, r_sampled_indices); + sample_cumulative_distribution_recursive( + rng, right_amount, middle, one_after_end, cumulative_weights, r_sampled_indices); + } +} + +static BLI_NOINLINE void sample_cumulative_distribution(RandomNumberGenerator &rng, + Span<float> cumulative_weights, + MutableSpan<int> r_samples) +{ + VectorAdaptor<int> sampled_indices(r_samples); + sample_cumulative_distribution_recursive(rng, + r_samples.size(), + 0, + cumulative_weights.size() - 1, + cumulative_weights, + sampled_indices); + BLI_assert(sampled_indices.is_full()); +} + +static BLI_NOINLINE bool sample_weighted_buckets(RandomNumberGenerator &rng, + Span<float> weights, + MutableSpan<int> r_samples) +{ + Array<float> cumulative_weights(weights.size() + 1); + compute_cumulative_distribution(weights, cumulative_weights); + + if (r_samples.size() > 0 && cumulative_weights.as_span().last() == 0.0f) { + /* All weights are zero. */ + return false; + } + + sample_cumulative_distribution(rng, cumulative_weights, r_samples); + return true; +} + +static BLI_NOINLINE void sample_looptris(RandomNumberGenerator &rng, + Mesh &mesh, + Span<MLoopTri> triangles, + Span<int> triangles_to_sample, + MutableSpan<float3> r_sampled_positions, + MutableSpan<float3> r_sampled_normals) +{ + assert_same_size(triangles_to_sample, r_sampled_positions, r_sampled_normals); + + MLoop *loops = mesh.mloop; + MVert *verts = mesh.mvert; + + for (uint i : triangles_to_sample.index_range()) { + const uint triangle_index = triangles_to_sample[i]; + const MLoopTri &triangle = triangles[triangle_index]; + + const float3 v1 = verts[loops[triangle.tri[0]].v].co; + const float3 v2 = verts[loops[triangle.tri[1]].v].co; + const float3 v3 = verts[loops[triangle.tri[2]].v].co; + + const float3 bary_coords = rng.get_barycentric_coordinates(); + + float3 position; + interp_v3_v3v3v3(position, v1, v2, v3, bary_coords); + + float3 normal; + normal_tri_v3(normal, v1, v2, v3); + + r_sampled_positions[i] = position; + r_sampled_normals[i] = normal; + } +} + +static BLI_NOINLINE bool compute_new_particle_attributes(ParticleEmitterContext &context, + EmitterSettings &settings, + ParticleMeshEmitterSimulationState &state, + Vector<float3> &r_positions, + Vector<float3> &r_velocities, + Vector<float> &r_birth_times) +{ + if (settings.object == nullptr) { + return false; + } + if (settings.rate <= 0.000001f) { + return false; + } + if (settings.object->type != OB_MESH) { + return false; + } + Mesh &mesh = *(Mesh *)settings.object->data; + if (mesh.totvert == 0) { + return false; + } + + const float start_time = context.emit_interval.start(); + const uint32_t seed = DefaultHash<StringRef>{}(state.head.name); + RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed}; + + compute_birth_times(settings.rate, context.emit_interval, state, r_birth_times); + const int particle_amount = r_birth_times.size(); + if (particle_amount == 0) { + return false; + } + + const float last_birth_time = r_birth_times.last(); + rng.shuffle(r_birth_times.as_mutable_span()); + + Span<MLoopTri> triangles = get_mesh_triangles(mesh); + if (triangles.is_empty()) { + return false; + } + + Array<float> triangle_weights(triangles.size()); + compute_triangle_weights(mesh, triangles, triangle_weights); + + Array<int> triangles_to_sample(particle_amount); + if (!sample_weighted_buckets(rng, triangle_weights, triangles_to_sample)) { + return false; + } + + r_positions.resize(particle_amount); + r_velocities.resize(particle_amount); + sample_looptris(rng, mesh, triangles, triangles_to_sample, r_positions, r_velocities); + + if (context.solve_context.dependency_animations.is_object_transform_changing(*settings.object)) { + Array<float4x4> local_to_world_matrices(particle_amount); + context.solve_context.dependency_animations.get_object_transforms( + *settings.object, r_birth_times, local_to_world_matrices); + + for (int i : IndexRange(particle_amount)) { + const float4x4 &position_to_world = local_to_world_matrices[i]; + const float4x4 normal_to_world = position_to_world.inverted_transposed_affine(); + r_positions[i] = position_to_world * r_positions[i]; + r_velocities[i] = normal_to_world * r_velocities[i]; + } + } + else { + const float4x4 position_to_world = settings.object->obmat; + const float4x4 normal_to_world = position_to_world.inverted_transposed_affine(); + for (int i : IndexRange(particle_amount)) { + r_positions[i] = position_to_world * r_positions[i]; + r_velocities[i] = normal_to_world * r_velocities[i]; + } + } + + for (int i : IndexRange(particle_amount)) { + r_velocities[i].normalize(); + } + + state.last_birth_time = last_birth_time; + return true; +} + +static BLI_NOINLINE EmitterSettings compute_settings(const fn::MultiFunction &inputs_fn, + ParticleEmitterContext &context) +{ + EmitterSettings parameters; + + fn::MFContextBuilder mf_context; + mf_context.add_global_context("PersistentDataHandleMap", &context.solve_context.handle_map); + + fn::MFParamsBuilder mf_params{inputs_fn, 1}; + bke::PersistentObjectHandle object_handle; + mf_params.add_uninitialized_single_output(&object_handle, "Object"); + mf_params.add_uninitialized_single_output(¶meters.rate, "Rate"); + + inputs_fn.call(IndexRange(1), mf_params, mf_context); + + parameters.object = context.solve_context.handle_map.lookup(object_handle); + return parameters; +} + +void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const +{ + auto *state = context.lookup_state<ParticleMeshEmitterSimulationState>(own_state_name_); + if (state == nullptr) { + return; + } + + EmitterSettings settings = compute_settings(inputs_fn_, context); + + Vector<float3> new_positions; + Vector<float3> new_velocities; + Vector<float> new_birth_times; + + if (!compute_new_particle_attributes( + context, settings, *state, new_positions, new_velocities, new_birth_times)) { + return; + } + + for (StringRef name : particle_names_) { + ParticleAllocator *allocator = context.try_get_particle_allocator(name); + if (allocator == nullptr) { + continue; + } + + int amount = new_positions.size(); + fn::MutableAttributesRef attributes = allocator->allocate(amount); + + attributes.get<float3>("Position").copy_from(new_positions); + attributes.get<float3>("Velocity").copy_from(new_velocities); + attributes.get<float>("Birth Time").copy_from(new_birth_times); + + if (action_ != nullptr) { + ParticleChunkContext particles{ + *context.solve_context.state_map.lookup<ParticleSimulationState>(name), + IndexRange(amount), + attributes, + nullptr}; + ParticleActionContext action_context{context.solve_context, particles}; + action_->execute(action_context); + } + } +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/particle_mesh_emitter.hh b/source/blender/simulation/intern/particle_mesh_emitter.hh new file mode 100644 index 00000000000..724d79c1aec --- /dev/null +++ b/source/blender/simulation/intern/particle_mesh_emitter.hh @@ -0,0 +1,52 @@ +/* + * 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 __SIM_PARTICLE_MESH_EMITTER_HH__ +#define __SIM_PARTICLE_MESH_EMITTER_HH__ + +#include "simulation_solver_influences.hh" + +#include "FN_multi_function.hh" + +namespace blender::sim { + +class ParticleMeshEmitter final : public ParticleEmitter { + private: + std::string own_state_name_; + Array<std::string> particle_names_; + const fn::MultiFunction &inputs_fn_; + const ParticleAction *action_; + + public: + ParticleMeshEmitter(std::string own_state_name, + Array<std::string> particle_names, + const fn::MultiFunction &inputs_fn, + const ParticleAction *action) + : own_state_name_(std::move(own_state_name)), + particle_names_(particle_names), + inputs_fn_(inputs_fn), + action_(action) + { + } + + ~ParticleMeshEmitter(); + + void emit(ParticleEmitterContext &context) const override; +}; + +} // namespace blender::sim + +#endif /* __SIM_PARTICLE_MESH_EMITTER_HH__ */ diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc index 764e587d157..818415e5d88 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.cc +++ b/source/blender/simulation/intern/simulation_collect_influences.cc @@ -16,6 +16,7 @@ #include "simulation_collect_influences.hh" #include "particle_function.hh" +#include "particle_mesh_emitter.hh" #include "FN_attributes_ref.hh" #include "FN_multi_function_network_evaluation.hh" @@ -23,38 +24,125 @@ #include "NOD_node_tree_multi_function.hh" +#include "DEG_depsgraph_query.h" + +#include "BLI_hash.h" #include "BLI_rand.hh" +#include "BLI_set.hh" namespace blender::sim { +using fn::GVSpan; +using fn::MFContextBuilder; +using fn::MFDataType; +using fn::MFDummyNode; +using fn::MFFunctionNode; +using fn::MFInputSocket; +using fn::MFNetwork; +using fn::MFNetworkEvaluator; +using fn::MFNode; +using fn::MFOutputSocket; +using fn::MFParamsBuilder; +using fn::MFParamType; +using fn::MultiFunction; +using fn::VSpan; +using nodes::DerivedNodeTree; +using nodes::DInputSocket; +using nodes::DNode; +using nodes::DOutputSocket; +using nodes::DParentNode; +using nodes::MFNetworkTreeMap; +using nodes::NodeTreeRefMap; + struct DummyDataSources { - Map<const fn::MFOutputSocket *, std::string> particle_attributes; + Map<const MFOutputSocket *, std::string> particle_attributes; + Set<const MFOutputSocket *> simulation_time; + Set<const MFOutputSocket *> scene_time; }; extern "C" { void WM_clipboard_text_set(const char *buf, bool selection); } -static std::string dnode_to_path(const nodes::DNode &dnode) +static std::string dnode_to_path(const DNode &dnode) { std::string path; - for (const nodes::DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { + for (const DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { path = parent->node_ref().name() + "/" + path; } path = path + dnode.name(); return path; } -static Span<const nodes::DNode *> get_particle_simulation_nodes(const nodes::DerivedNodeTree &tree) +struct CollectContext : NonCopyable, NonMovable { + SimulationInfluences &influences; + RequiredStates &required_states; + ResourceCollector &resources; + MFNetworkTreeMap &network_map; + MFNetwork &network; + const DerivedNodeTree &tree; + + DummyDataSources data_sources; + Span<const DNode *> particle_simulation_nodes; + Map<const DNode *, std::string> node_paths; + + CollectContext(SimulationInfluences &influences, + RequiredStates &required_states, + ResourceCollector &resources, + MFNetworkTreeMap &network_map) + : influences(influences), + required_states(required_states), + resources(resources), + network_map(network_map), + network(network_map.network()), + tree(network_map.tree()) + { + particle_simulation_nodes = tree.nodes_by_type("SimulationNodeParticleSimulation"); + } +}; + +static const ParticleAction *create_particle_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names); + +static const ParticleAction *create_particle_action(CollectContext &context, + const DInputSocket &dsocket, + Span<StringRefNull> particle_names) +{ + BLI_assert(dsocket.bsocket()->type == SOCK_CONTROL_FLOW); + if (dsocket.linked_sockets().size() != 1) { + return nullptr; + } + return create_particle_action(context, *dsocket.linked_sockets()[0], particle_names); +} + +static StringRefNull get_identifier(CollectContext &context, const DNode &dnode) +{ + return context.node_paths.lookup_or_add_cb(&dnode, [&]() { return dnode_to_path(dnode); }); +} + +static Span<const DNode *> nodes_by_type(CollectContext &context, StringRefNull idname) { - return tree.nodes_by_type("SimulationNodeParticleSimulation"); + return context.tree.nodes_by_type(idname); +} + +static Array<StringRefNull> find_linked_particle_simulations(CollectContext &context, + const DOutputSocket &output_socket) +{ + VectorSet<StringRefNull> names; + for (const DInputSocket *target_socket : output_socket.linked_sockets()) { + if (target_socket->node().idname() == "SimulationNodeParticleSimulation") { + names.add(get_identifier(context, target_socket->node())); + } + } + return names.as_span(); } /* Returns true on success. */ -static bool compute_global_inputs(nodes::MFNetworkTreeMap &network_map, +static bool compute_global_inputs(MFNetworkTreeMap &network_map, ResourceCollector &resources, - Span<const fn::MFInputSocket *> sockets, - MutableSpan<fn::GMutableSpan> r_results) + Span<const MFInputSocket *> sockets, + MutableSpan<GMutableSpan> r_results) { int amount = sockets.size(); if (amount == 0) { @@ -65,28 +153,28 @@ static bool compute_global_inputs(nodes::MFNetworkTreeMap &network_map, return false; } - fn::MFNetworkEvaluator network_fn{{}, sockets}; - fn::MFParamsBuilder params{network_fn, 1}; + MFNetworkEvaluator network_fn{{}, sockets}; + MFParamsBuilder params{network_fn, 1}; for (int param_index : network_fn.param_indices()) { - fn::MFParamType param_type = network_fn.param_type(param_index); - BLI_assert(param_type.category() == fn::MFParamType::Category::SingleOutput); /* For now. */ - const fn::CPPType &type = param_type.data_type().single_type(); + MFParamType param_type = network_fn.param_type(param_index); + BLI_assert(param_type.category() == MFParamType::Category::SingleOutput); /* For now. */ + const CPPType &type = param_type.data_type().single_type(); void *buffer = resources.linear_allocator().allocate(type.size(), type.alignment()); resources.add(buffer, type.destruct_cb(), AT); - fn::GMutableSpan span{type, buffer, 1}; + GMutableSpan span{type, buffer, 1}; r_results[param_index] = span; params.add_uninitialized_single_output(span); } - fn::MFContextBuilder context; + MFContextBuilder context; network_fn.call(IndexRange(1), params, context); return true; } static std::optional<Array<std::string>> compute_global_string_inputs( - nodes::MFNetworkTreeMap &network_map, Span<const fn::MFInputSocket *> sockets) + MFNetworkTreeMap &network_map, Span<const MFInputSocket *> sockets) { ResourceCollector local_resources; - Array<fn::GMutableSpan> computed_values(sockets.size(), NoInitialization()); + Array<GMutableSpan> computed_values(sockets.size(), NoInitialization()); if (!compute_global_inputs(network_map, local_resources, sockets, computed_values)) { return {}; } @@ -98,107 +186,177 @@ static std::optional<Array<std::string>> compute_global_string_inputs( return strings; } -static void find_and_deduplicate_particle_attribute_nodes(nodes::MFNetworkTreeMap &network_map, - DummyDataSources &r_data_sources) +/** + * This will find all the particle attribute input nodes. Then it will compute the attribute names + * by evaluating the network (those names should not depend on per particle data). In the end, + * input nodes that access the same attribute are combined. + */ +static void prepare_particle_attribute_nodes(CollectContext &context) { - fn::MFNetwork &network = network_map.network(); - const nodes::DerivedNodeTree &tree = network_map.tree(); - - Span<const nodes::DNode *> attribute_dnodes = tree.nodes_by_type( - "SimulationNodeParticleAttribute"); + Span<const DNode *> attribute_dnodes = nodes_by_type(context, "SimulationNodeParticleAttribute"); - Vector<fn::MFInputSocket *> name_sockets; - for (const nodes::DNode *dnode : attribute_dnodes) { - fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); + Vector<MFInputSocket *> name_sockets; + for (const DNode *dnode : attribute_dnodes) { + MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode->input(0)); name_sockets.append(&name_socket); } - std::optional<Array<std::string>> attribute_names = compute_global_string_inputs(network_map, - name_sockets); + std::optional<Array<std::string>> attribute_names = compute_global_string_inputs( + context.network_map, name_sockets); if (!attribute_names.has_value()) { return; } - Map<std::pair<std::string, fn::MFDataType>, Vector<fn::MFNode *>> - attribute_nodes_by_name_and_type; + MultiValueMap<std::pair<std::string, MFDataType>, MFNode *> attribute_nodes_by_name_and_type; for (int i : attribute_names->index_range()) { - attribute_nodes_by_name_and_type - .lookup_or_add_default( - {(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()}) - .append(&name_sockets[i]->node()); + attribute_nodes_by_name_and_type.add( + {(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()}, + &name_sockets[i]->node()); } - Map<const fn::MFOutputSocket *, std::string> attribute_inputs; + Map<const MFOutputSocket *, std::string> attribute_inputs; for (auto item : attribute_nodes_by_name_and_type.items()) { StringRef attribute_name = item.key.first; - fn::MFDataType data_type = item.key.second; - Span<fn::MFNode *> nodes = item.value; + MFDataType data_type = item.key.second; + Span<MFNode *> nodes = item.value; - fn::MFOutputSocket &new_attribute_socket = network.add_input( + MFOutputSocket &new_attribute_socket = context.network.add_input( "Attribute '" + attribute_name + "'", data_type); - for (fn::MFNode *node : nodes) { - network.relink(node->output(0), new_attribute_socket); + for (MFNode *node : nodes) { + context.network.relink(node->output(0), new_attribute_socket); } - network.remove(nodes); + context.network.remove(nodes); - r_data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name); + context.data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name); + } +} + +static void prepare_time_input_nodes(CollectContext &context) +{ + Span<const DNode *> time_input_dnodes = nodes_by_type(context, "SimulationNodeTime"); + Vector<const DNode *> simulation_time_inputs; + Vector<const DNode *> scene_time_inputs; + for (const DNode *dnode : time_input_dnodes) { + NodeSimInputTimeType type = (NodeSimInputTimeType)dnode->node_ref().bnode()->custom1; + switch (type) { + case NODE_SIM_INPUT_SIMULATION_TIME: { + simulation_time_inputs.append(dnode); + break; + } + case NODE_SIM_INPUT_SCENE_TIME: { + scene_time_inputs.append(dnode); + break; + } + } + } + + if (simulation_time_inputs.size() > 0) { + MFOutputSocket &new_socket = context.network.add_input("Simulation Time", + MFDataType::ForSingle<float>()); + for (const DNode *dnode : simulation_time_inputs) { + MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0)); + context.network.relink(old_socket, new_socket); + context.network.remove(old_socket.node()); + } + context.data_sources.simulation_time.add(&new_socket); + } + if (scene_time_inputs.size() > 0) { + MFOutputSocket &new_socket = context.network.add_input("Scene Time", + MFDataType::ForSingle<float>()); + for (const DNode *dnode : scene_time_inputs) { + MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0)); + context.network.relink(old_socket, new_socket); + context.network.remove(old_socket.node()); + } + context.data_sources.scene_time.add(&new_socket); } } class ParticleAttributeInput : public ParticleFunctionInput { private: std::string attribute_name_; - const fn::CPPType &attribute_type_; + const CPPType &attribute_type_; public: - ParticleAttributeInput(std::string attribute_name, const fn::CPPType &attribute_type) + ParticleAttributeInput(std::string attribute_name, const CPPType &attribute_type) : attribute_name_(std::move(attribute_name)), attribute_type_(attribute_type) { } - void add_input(fn::AttributesRef attributes, - fn::MFParamsBuilder ¶ms, + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, ResourceCollector &UNUSED(resources)) const override { - std::optional<fn::GSpan> span = attributes.try_get(attribute_name_, attribute_type_); + std::optional<GSpan> span = context.particles.attributes.try_get(attribute_name_, + attribute_type_); if (span.has_value()) { params.add_readonly_single_input(*span); } else { - params.add_readonly_single_input(fn::GVSpan::FromDefault(attribute_type_)); + params.add_readonly_single_input(GVSpan::FromDefault(attribute_type_)); } } }; +class SceneTimeInput : public ParticleFunctionInput { + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, + ResourceCollector &resources) const override + { + const float time = DEG_get_ctime(&context.solve_context.depsgraph); + float *time_ptr = &resources.construct<float>(AT, time); + params.add_readonly_single_input(time_ptr); + } +}; + +class SimulationTimeInput : public ParticleFunctionInput { + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, + ResourceCollector &resources) const override + { + /* TODO: Vary this per particle. */ + const float time = context.solve_context.solve_interval.stop(); + float *time_ptr = &resources.construct<float>(AT, time); + params.add_readonly_single_input(time_ptr); + } +}; + static const ParticleFunction *create_particle_function_for_inputs( - Span<const fn::MFInputSocket *> sockets_to_compute, - ResourceCollector &resources, - DummyDataSources &data_sources) + CollectContext &context, Span<const MFInputSocket *> sockets_to_compute) { BLI_assert(sockets_to_compute.size() >= 1); - const fn::MFNetwork &network = sockets_to_compute[0]->node().network(); + const MFNetwork &network = sockets_to_compute[0]->node().network(); - VectorSet<const fn::MFOutputSocket *> dummy_deps; - VectorSet<const fn::MFInputSocket *> unlinked_input_deps; + VectorSet<const MFOutputSocket *> dummy_deps; + VectorSet<const MFInputSocket *> unlinked_input_deps; network.find_dependencies(sockets_to_compute, dummy_deps, unlinked_input_deps); BLI_assert(unlinked_input_deps.size() == 0); Vector<const ParticleFunctionInput *> per_particle_inputs; - for (const fn::MFOutputSocket *socket : dummy_deps) { - const std::string *attribute_name = data_sources.particle_attributes.lookup_ptr(socket); - if (attribute_name == nullptr) { - return nullptr; + for (const MFOutputSocket *socket : dummy_deps) { + if (context.data_sources.particle_attributes.contains(socket)) { + const std::string *attribute_name = context.data_sources.particle_attributes.lookup_ptr( + socket); + if (attribute_name == nullptr) { + return nullptr; + } + per_particle_inputs.append(&context.resources.construct<ParticleAttributeInput>( + AT, *attribute_name, socket->data_type().single_type())); + } + else if (context.data_sources.scene_time.contains(socket)) { + per_particle_inputs.append(&context.resources.construct<SceneTimeInput>(AT)); + } + else if (context.data_sources.simulation_time.contains(socket)) { + per_particle_inputs.append(&context.resources.construct<SimulationTimeInput>(AT)); } - per_particle_inputs.append(&resources.construct<ParticleAttributeInput>( - AT, *attribute_name, socket->data_type().single_type())); } - const fn::MultiFunction &per_particle_fn = resources.construct<fn::MFNetworkEvaluator>( + const MultiFunction &per_particle_fn = context.resources.construct<MFNetworkEvaluator>( AT, dummy_deps.as_span(), sockets_to_compute); Array<bool> output_is_global(sockets_to_compute.size(), false); - const ParticleFunction &particle_fn = resources.construct<ParticleFunction>( + const ParticleFunction &particle_fn = context.resources.construct<ParticleFunction>( AT, nullptr, &per_particle_fn, @@ -209,6 +367,17 @@ static const ParticleFunction *create_particle_function_for_inputs( return &particle_fn; } +static const ParticleFunction *create_particle_function_for_inputs( + CollectContext &context, Span<const DInputSocket *> dsockets_to_compute) +{ + Vector<const MFInputSocket *> sockets_to_compute; + for (const DInputSocket *dsocket : dsockets_to_compute) { + const MFInputSocket &socket = context.network_map.lookup_dummy(*dsocket); + sockets_to_compute.append(&socket); + } + return create_particle_function_for_inputs(context, sockets_to_compute); +} + class ParticleFunctionForce : public ParticleForce { private: const ParticleFunction &particle_fn_; @@ -220,13 +389,12 @@ class ParticleFunctionForce : public ParticleForce { void add_force(ParticleForceContext &context) const override { - IndexMask mask = context.particle_chunk().index_mask(); - MutableSpan<float3> r_combined_force = context.force_dst(); + IndexMask mask = context.particles.index_mask; + MutableSpan<float3> r_combined_force = context.force_dst; - ParticleFunctionEvaluator evaluator{ - particle_fn_, context.solve_context(), context.particle_chunk()}; + ParticleFunctionEvaluator evaluator{particle_fn_, context.solve_context, context.particles}; evaluator.compute(); - fn::VSpan<float3> forces = evaluator.get<float3>(0, "Force"); + VSpan<float3> forces = evaluator.get<float3>(0, "Force"); for (int64_t i : mask) { r_combined_force[i] += forces[i]; @@ -234,218 +402,473 @@ class ParticleFunctionForce : public ParticleForce { } }; -static Vector<const ParticleForce *> create_forces_for_particle_simulation( - const nodes::DNode &simulation_node, - nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - DummyDataSources &data_sources) +static void create_forces_for_particle_simulation(CollectContext &context, + const DNode &simulation_node) { Vector<const ParticleForce *> forces; - for (const nodes::DOutputSocket *origin_socket : - simulation_node.input(2, "Forces").linked_sockets()) { - const nodes::DNode &origin_node = origin_socket->node(); + for (const DOutputSocket *origin_socket : simulation_node.input(2, "Forces").linked_sockets()) { + const DNode &origin_node = origin_socket->node(); if (origin_node.idname() != "SimulationNodeForce") { continue; } - const fn::MFInputSocket &force_socket = network_map.lookup_dummy( - origin_node.input(0, "Force")); - const ParticleFunction *particle_fn = create_particle_function_for_inputs( - {&force_socket}, resources, data_sources); + context, {&origin_node.input(0, "Force")}); if (particle_fn == nullptr) { continue; } - const ParticleForce &force = resources.construct<ParticleFunctionForce>(AT, *particle_fn); + const ParticleForce &force = context.resources.construct<ParticleFunctionForce>(AT, + *particle_fn); forces.append(&force); } - return forces; + + StringRef particle_name = get_identifier(context, simulation_node); + context.influences.particle_forces.add_multiple_as(particle_name, forces); +} + +static void collect_forces(CollectContext &context) +{ + for (const DNode *dnode : context.particle_simulation_nodes) { + create_forces_for_particle_simulation(context, *dnode); + } +} + +static ParticleEmitter *create_particle_emitter(CollectContext &context, const DNode &dnode) +{ + Array<StringRefNull> names = find_linked_particle_simulations(context, dnode.output(0)); + if (names.size() == 0) { + return nullptr; + } + + Array<const MFInputSocket *> input_sockets{2}; + for (int i : input_sockets.index_range()) { + input_sockets[i] = &context.network_map.lookup_dummy(dnode.input(i)); + } + + if (context.network.have_dummy_or_unlinked_dependencies(input_sockets)) { + return nullptr; + } + + MultiFunction &inputs_fn = context.resources.construct<MFNetworkEvaluator>( + AT, Span<const MFOutputSocket *>(), input_sockets.as_span()); + + const ParticleAction *birth_action = create_particle_action( + context, dnode.input(2, "Execute"), names); + + StringRefNull own_state_name = get_identifier(context, dnode); + context.required_states.add(own_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER); + ParticleEmitter &emitter = context.resources.construct<ParticleMeshEmitter>( + AT, own_state_name, names.as_span(), inputs_fn, birth_action); + return &emitter; +} + +static void collect_emitters(CollectContext &context) +{ + for (const DNode *dnode : nodes_by_type(context, "SimulationNodeParticleMeshEmitter")) { + ParticleEmitter *emitter = create_particle_emitter(context, *dnode); + if (emitter != nullptr) { + context.influences.particle_emitters.append(emitter); + } + } } -static void collect_forces(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - DummyDataSources &data_sources, - SimulationInfluences &r_influences) +static void collect_birth_events(CollectContext &context) { - for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { - std::string name = dnode_to_path(*dnode); - Vector<const ParticleForce *> forces = create_forces_for_particle_simulation( - *dnode, network_map, resources, data_sources); - r_influences.particle_forces.add_new(std::move(name), std::move(forces)); + for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleBirthEvent")) { + const DInputSocket &execute_input = event_dnode->input(0); + if (execute_input.linked_sockets().size() != 1) { + continue; + } + + Array<StringRefNull> particle_names = find_linked_particle_simulations(context, + event_dnode->output(0)); + + const DOutputSocket &execute_source = *execute_input.linked_sockets()[0]; + const ParticleAction *action = create_particle_action(context, execute_source, particle_names); + if (action == nullptr) { + continue; + } + + for (StringRefNull particle_name : particle_names) { + context.influences.particle_birth_actions.add_as(particle_name, action); + } + } +} + +static void collect_time_step_events(CollectContext &context) +{ + for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleTimeStepEvent")) { + const DInputSocket &execute_input = event_dnode->input(0); + Array<StringRefNull> particle_names = find_linked_particle_simulations(context, + event_dnode->output(0)); + + const ParticleAction *action = create_particle_action(context, execute_input, particle_names); + if (action == nullptr) { + continue; + } + + NodeSimParticleTimeStepEventType type = + (NodeSimParticleTimeStepEventType)event_dnode->node_ref().bnode()->custom1; + if (type == NODE_PARTICLE_TIME_STEP_EVENT_BEGIN) { + for (StringRefNull particle_name : particle_names) { + context.influences.particle_time_step_begin_actions.add_as(particle_name, action); + } + } + else { + for (StringRefNull particle_name : particle_names) { + context.influences.particle_time_step_end_actions.add_as(particle_name, action); + } + } } } -class MyBasicEmitter : public ParticleEmitter { +class SequenceParticleAction : public ParticleAction { private: - Array<std::string> names_; - std::string my_state_; - const fn::MultiFunction &inputs_fn_; - uint32_t seed_; + Vector<const ParticleAction *> actions_; public: - MyBasicEmitter(Array<std::string> names, - std::string my_state, - const fn::MultiFunction &inputs_fn, - uint32_t seed) - : names_(std::move(names)), - my_state_(std::move(my_state)), - inputs_fn_(inputs_fn), - seed_(seed) + SequenceParticleAction(Span<const ParticleAction *> actions) : actions_(std::move(actions)) { } - void emit(ParticleEmitterContext &context) const override + void execute(ParticleActionContext &context) const override { - auto *state = context.solve_context().state_map().lookup<ParticleMeshEmitterSimulationState>( - my_state_); - if (state == nullptr) { - return; + for (const ParticleAction *action : actions_) { + action->execute(context); } + } +}; - fn::MFContextBuilder mf_context; - mf_context.add_global_context("PersistentDataHandleMap", - &context.solve_context().handle_map()); +class SetParticleAttributeAction : public ParticleAction { + private: + std::string attribute_name_; + const CPPType &cpp_type_; + const ParticleFunction &inputs_fn_; - fn::MFParamsBuilder mf_params{inputs_fn_, 1}; - bke::PersistentObjectHandle object_handle; - float rate; - mf_params.add_uninitialized_single_output(&object_handle); - mf_params.add_uninitialized_single_output(&rate); - inputs_fn_.call(IndexRange(1), mf_params, mf_context); + public: + SetParticleAttributeAction(std::string attribute_name, + const CPPType &cpp_type, + const ParticleFunction &inputs_fn) + : attribute_name_(std::move(attribute_name)), cpp_type_(cpp_type), inputs_fn_(inputs_fn) + { + } - const Object *object = context.solve_context().handle_map().lookup(object_handle); - if (object == nullptr) { + void execute(ParticleActionContext &context) const override + { + std::optional<GMutableSpan> attribute_array = context.particles.attributes.try_get( + attribute_name_, cpp_type_); + if (!attribute_array.has_value()) { return; } - Vector<float3> new_positions; - Vector<float3> new_velocities; - Vector<float> new_birth_times; - - TimeInterval time_interval = context.simulation_time_interval(); - float start_time = time_interval.start(); - RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed_}; + ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles}; + evaluator.compute(); + GVSpan values = evaluator.get(0); - const float time_between_particles = 1.0f / rate; - while (state->last_birth_time + time_between_particles < time_interval.end()) { - new_positions.append(rng.get_unit_float3() * 0.3 + float3(object->loc)); - new_velocities.append(rng.get_unit_float3()); - const float birth_time = state->last_birth_time + time_between_particles; - new_birth_times.append(birth_time); - state->last_birth_time = birth_time; + if (values.is_single_element()) { + cpp_type_.fill_initialized_indices( + values.as_single_element(), attribute_array->data(), context.particles.index_mask); + } + else { + GSpan value_array = values.as_full_array(); + cpp_type_.copy_to_initialized_indices( + value_array.data(), attribute_array->data(), context.particles.index_mask); } - for (StringRef name : names_) { - ParticleAllocator *allocator = context.try_get_particle_allocator(name); - if (allocator == nullptr) { - return; - } - - int amount = new_positions.size(); - fn::MutableAttributesRef attributes = allocator->allocate(amount); - - initialized_copy_n(new_positions.data(), amount, attributes.get<float3>("Position").data()); - initialized_copy_n(new_velocities.data(), amount, attributes.get<float3>("Velocity").data()); - initialized_copy_n( - new_birth_times.data(), amount, attributes.get<float>("Birth Time").data()); + if (attribute_name_ == "Velocity") { + context.particles.update_diffs_after_velocity_change(); } } }; -static Vector<const nodes::DNode *> find_linked_particle_simulations( - const nodes::DOutputSocket &output_socket) +static const ParticleAction *concatenate_actions(CollectContext &context, + Span<const ParticleAction *> actions) { - Vector<const nodes::DNode *> simulation_nodes; - for (const nodes::DInputSocket *target_socket : output_socket.linked_sockets()) { - if (target_socket->node().idname() == "SimulationNodeParticleSimulation") { - simulation_nodes.append(&target_socket->node()); + Vector<const ParticleAction *> non_null_actions; + for (const ParticleAction *action : actions) { + if (action != nullptr) { + non_null_actions.append(action); } } - return simulation_nodes; + if (non_null_actions.size() == 0) { + return nullptr; + } + if (non_null_actions.size() == 1) { + return non_null_actions[0]; + } + return &context.resources.construct<SequenceParticleAction>(AT, std::move(non_null_actions)); } -static ParticleEmitter *create_particle_emitter(const nodes::DNode &dnode, - ResourceCollector &resources, - nodes::MFNetworkTreeMap &network_map, - RequiredStates &r_required_states) +static const ParticleAction *create_set_particle_attribute_action( + CollectContext &context, const DOutputSocket &dsocket, Span<StringRefNull> particle_names) { - Vector<const nodes::DNode *> simulation_dnodes = find_linked_particle_simulations( - dnode.output(0)); - if (simulation_dnodes.size() == 0) { - return nullptr; + const DNode &dnode = dsocket.node(); + + const ParticleAction *previous_action = create_particle_action( + context, dnode.input(0), particle_names); + + MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode.input(1)); + MFInputSocket &value_socket = name_socket.node().input(1); + std::optional<Array<std::string>> names = compute_global_string_inputs(context.network_map, + {&name_socket}); + if (!names.has_value()) { + return previous_action; } - Array<std::string> names{simulation_dnodes.size()}; - for (int i : simulation_dnodes.index_range()) { - names[i] = dnode_to_path(*simulation_dnodes[i]); + std::string attribute_name = (*names)[0]; + if (attribute_name.empty()) { + return previous_action; } + const CPPType &attribute_type = value_socket.data_type().single_type(); - Array<const fn::MFInputSocket *> input_sockets{dnode.inputs().size()}; - for (int i : input_sockets.index_range()) { - input_sockets[i] = &network_map.lookup_dummy(dnode.input(i)); + const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context, + {&value_socket}); + if (inputs_fn == nullptr) { + return previous_action; } - if (network_map.network().have_dummy_or_unlinked_dependencies(input_sockets)) { - return nullptr; + for (StringRef particle_name : particle_names) { + context.influences.particle_attributes_builder.lookup_as(particle_name) + ->add(attribute_name, attribute_type); } - fn::MultiFunction &inputs_fn = resources.construct<fn::MFNetworkEvaluator>( - AT, Span<const fn::MFOutputSocket *>(), input_sockets.as_span()); + ParticleAction &this_action = context.resources.construct<SetParticleAttributeAction>( + AT, attribute_name, attribute_type, *inputs_fn); - std::string my_state_name = dnode_to_path(dnode); - r_required_states.add(my_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER); - uint32_t seed = DefaultHash<std::string>{}(my_state_name); - ParticleEmitter &emitter = resources.construct<MyBasicEmitter>( - AT, std::move(names), std::move(my_state_name), inputs_fn, seed); - return &emitter; + return concatenate_actions(context, {previous_action, &this_action}); } -static void collect_emitters(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - SimulationInfluences &r_influences, - RequiredStates &r_required_states) +class ParticleConditionAction : public ParticleAction { + private: + const ParticleFunction &inputs_fn_; + const ParticleAction *action_true_; + const ParticleAction *action_false_; + + public: + ParticleConditionAction(const ParticleFunction &inputs_fn, + const ParticleAction *action_true, + const ParticleAction *action_false) + : inputs_fn_(inputs_fn), action_true_(action_true), action_false_(action_false) + { + } + + void execute(ParticleActionContext &context) const override + { + ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles}; + evaluator.compute(); + VSpan<bool> conditions = evaluator.get<bool>(0, "Condition"); + + if (conditions.is_single_element()) { + const bool condition = conditions.as_single_element(); + if (condition) { + if (action_true_ != nullptr) { + action_true_->execute(context); + } + } + else { + if (action_false_ != nullptr) { + action_false_->execute(context); + } + } + } + else { + Span<bool> conditions_array = conditions.as_full_array(); + + Vector<int64_t> true_indices; + Vector<int64_t> false_indices; + for (int i : context.particles.index_mask) { + if (conditions_array[i]) { + true_indices.append(i); + } + else { + false_indices.append(i); + } + } + + if (action_true_ != nullptr) { + ParticleChunkContext chunk_context{context.particles.state, + true_indices.as_span(), + context.particles.attributes, + context.particles.integration}; + ParticleActionContext action_context{context.solve_context, chunk_context}; + action_true_->execute(action_context); + } + if (action_false_ != nullptr) { + ParticleChunkContext chunk_context{context.particles.state, + false_indices.as_span(), + context.particles.attributes, + context.particles.integration}; + ParticleActionContext action_context{context.solve_context, chunk_context}; + action_false_->execute(action_context); + } + } + } +}; + +static const ParticleAction *create_particle_condition_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names) { - for (const nodes::DNode *dnode : - network_map.tree().nodes_by_type("SimulationNodeParticleMeshEmitter")) { - ParticleEmitter *emitter = create_particle_emitter( - *dnode, resources, network_map, r_required_states); - if (emitter != nullptr) { - r_influences.particle_emitters.append(emitter); + const DNode &dnode = dsocket.node(); + + const ParticleFunction *inputs_fn = create_particle_function_for_inputs( + context, {&dnode.input(0, "Condition")}); + if (inputs_fn == nullptr) { + return nullptr; + } + + const ParticleAction *true_action = create_particle_action( + context, dnode.input(1), particle_names); + const ParticleAction *false_action = create_particle_action( + context, dnode.input(2), particle_names); + + if (true_action == nullptr && false_action == nullptr) { + return nullptr; + } + return &context.resources.construct<ParticleConditionAction>( + AT, *inputs_fn, true_action, false_action); +} + +class KillParticleAction : public ParticleAction { + public: + void execute(ParticleActionContext &context) const override + { + MutableSpan<int> dead_states = context.particles.attributes.get<int>("Dead"); + for (int i : context.particles.index_mask) { + dead_states[i] = true; } } +}; + +static const ParticleAction *create_particle_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names) +{ + const DNode &dnode = dsocket.node(); + StringRef idname = dnode.idname(); + if (idname == "SimulationNodeSetParticleAttribute") { + return create_set_particle_attribute_action(context, dsocket, particle_names); + } + if (idname == "SimulationNodeExecuteCondition") { + return create_particle_condition_action(context, dsocket, particle_names); + } + if (idname == "SimulationNodeKillParticle") { + return &context.resources.construct<KillParticleAction>(AT); + } + return nullptr; } -static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - SimulationInfluences &r_influences) +static void initialize_particle_attribute_builders(CollectContext &context) { - for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { - std::string name = dnode_to_path(*dnode); - fn::AttributesInfoBuilder &builder = resources.construct<fn::AttributesInfoBuilder>(AT); - builder.add<float3>("Position", {0, 0, 0}); - builder.add<float3>("Velocity", {0, 0, 0}); - builder.add<int>("ID", 0); + for (const DNode *dnode : context.particle_simulation_nodes) { + StringRef name = get_identifier(context, *dnode); + AttributesInfoBuilder &attributes_builder = context.resources.construct<AttributesInfoBuilder>( + AT); + attributes_builder.add<float3>("Position", {0, 0, 0}); + attributes_builder.add<float3>("Velocity", {0, 0, 0}); + attributes_builder.add<int>("ID", 0); /* TODO: Use bool property, but need to add CD_PROP_BOOL first. */ - builder.add<int>("Dead", 0); - builder.add<float>("Birth Time", 0.0f); - r_influences.particle_attributes_builder.add_new(std::move(name), &builder); + attributes_builder.add<int>("Dead", 0); + /* TODO: Use uint32_t, but we don't have a corresponding custom property type. */ + attributes_builder.add<int>("Hash", 0); + attributes_builder.add<float>("Birth Time", 0.0f); + attributes_builder.add<float>("Radius", 0.02f); + context.influences.particle_attributes_builder.add_new(name, &attributes_builder); } } -static void find_used_data_blocks(const nodes::DerivedNodeTree &tree, - SimulationInfluences &r_influences) +static void optimize_function_network(CollectContext &context) +{ + fn::mf_network_optimization::constant_folding(context.network, context.resources); + fn::mf_network_optimization::common_subnetwork_elimination(context.network); + fn::mf_network_optimization::dead_node_removal(context.network); + // WM_clipboard_text_set(network.to_dot().c_str(), false); +} + +class AgeReachedEvent : public ParticleEvent { + private: + std::string attribute_name_; + const ParticleFunction &inputs_fn_; + const ParticleAction &action_; + + public: + AgeReachedEvent(std::string attribute_name, + const ParticleFunction &inputs_fn, + const ParticleAction &action) + : attribute_name_(std::move(attribute_name)), inputs_fn_(inputs_fn), action_(action) + { + } + + void filter(ParticleEventFilterContext &context) const override + { + Span<float> birth_times = context.particles.attributes.get<float>("Birth Time"); + std::optional<Span<int>> has_been_triggered = context.particles.attributes.try_get<int>( + attribute_name_); + if (!has_been_triggered.has_value()) { + return; + } + + ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles}; + evaluator.compute(); + VSpan<float> trigger_ages = evaluator.get<float>(0, "Age"); + + const float end_time = context.particles.integration->end_time; + for (int i : context.particles.index_mask) { + if ((*has_been_triggered)[i]) { + continue; + } + const float trigger_age = trigger_ages[i]; + const float birth_time = birth_times[i]; + const float trigger_time = birth_time + trigger_age; + if (trigger_time > end_time) { + continue; + } + + const float duration = context.particles.integration->durations[i]; + TimeInterval interval(end_time - duration, duration); + const float time_factor = interval.safe_factor_at_time(trigger_time); + + context.factor_dst[i] = std::max<float>(0.0f, time_factor); + } + } + + void execute(ParticleActionContext &context) const override + { + MutableSpan<int> has_been_triggered = context.particles.attributes.get<int>(attribute_name_); + for (int i : context.particles.index_mask) { + has_been_triggered[i] = 1; + } + action_.execute(context); + } +}; + +static void collect_age_reached_events(CollectContext &context) { - const bNodeSocketType *socktype = nodeSocketTypeFind("NodeSocketObject"); - BLI_assert(socktype != nullptr); - - for (const nodes::DInputSocket *dsocket : tree.input_sockets()) { - const bNodeSocket *bsocket = dsocket->bsocket(); - if (bsocket->typeinfo == socktype) { - Object *value = ((const bNodeSocketValueObject *)bsocket->default_value)->value; - if (value != nullptr) { - r_influences.used_data_blocks.add(&value->id); + for (const DNode *dnode : nodes_by_type(context, "SimulationNodeAgeReachedEvent")) { + const DInputSocket &age_input = dnode->input(0, "Age"); + const DInputSocket &execute_input = dnode->input(1, "Execute"); + Array<StringRefNull> particle_names = find_linked_particle_simulations(context, + dnode->output(0)); + const ParticleAction *action = create_particle_action(context, execute_input, particle_names); + if (action == nullptr) { + continue; + } + const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context, {&age_input}); + if (inputs_fn == nullptr) { + continue; + } + + std::string attribute_name = get_identifier(context, *dnode); + const ParticleEvent &event = context.resources.construct<AgeReachedEvent>( + AT, attribute_name, *inputs_fn, *action); + for (StringRefNull particle_name : particle_names) { + const bool added_attribute = context.influences.particle_attributes_builder + .lookup_as(particle_name) + ->add<int>(attribute_name, 0); + if (added_attribute) { + context.influences.particle_events.add_as(particle_name, &event); } } } @@ -456,30 +879,29 @@ void collect_simulation_influences(Simulation &simulation, SimulationInfluences &r_influences, RequiredStates &r_required_states) { - nodes::NodeTreeRefMap tree_refs; - const nodes::DerivedNodeTree tree{simulation.nodetree, tree_refs}; + NodeTreeRefMap tree_refs; + const DerivedNodeTree tree{simulation.nodetree, tree_refs}; - fn::MFNetwork &network = resources.construct<fn::MFNetwork>(AT); - nodes::MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); + MFNetwork &network = resources.construct<MFNetwork>(AT); + MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); - prepare_particle_attribute_builders(network_map, resources, r_influences); + CollectContext context{r_influences, r_required_states, resources, network_map}; + initialize_particle_attribute_builders(context); - DummyDataSources data_sources; - find_and_deduplicate_particle_attribute_nodes(network_map, data_sources); + prepare_particle_attribute_nodes(context); + prepare_time_input_nodes(context); - fn::mf_network_optimization::constant_folding(network, resources); - fn::mf_network_optimization::common_subnetwork_elimination(network); - fn::mf_network_optimization::dead_node_removal(network); - // WM_clipboard_text_set(network.to_dot().c_str(), false); + collect_forces(context); + collect_emitters(context); + collect_birth_events(context); + collect_time_step_events(context); + collect_age_reached_events(context); - collect_forces(network_map, resources, data_sources, r_influences); - collect_emitters(network_map, resources, r_influences, r_required_states); + optimize_function_network(context); - for (const nodes::DNode *dnode : get_particle_simulation_nodes(tree)) { - r_required_states.add(dnode_to_path(*dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION); + for (const DNode *dnode : context.particle_simulation_nodes) { + r_required_states.add(get_identifier(context, *dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION); } - - find_used_data_blocks(tree, r_influences); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_collect_influences.hh b/source/blender/simulation/intern/simulation_collect_influences.hh index 5035461191e..8673a308b04 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.hh +++ b/source/blender/simulation/intern/simulation_collect_influences.hh @@ -20,7 +20,7 @@ #include "BLI_resource_collector.hh" -#include "simulation_solver.hh" +#include "simulation_solver_influences.hh" namespace blender::sim { diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc index ee7a8d40035..d53ccd2bd49 100644 --- a/source/blender/simulation/intern/simulation_solver.cc +++ b/source/blender/simulation/intern/simulation_solver.cc @@ -17,23 +17,16 @@ #include "simulation_solver.hh" #include "BKE_customdata.h" -#include "BKE_lib_id.h" #include "BKE_persistent_data_handle.hh" #include "BLI_rand.hh" #include "BLI_set.hh" -namespace blender::sim { - -ParticleForce::~ParticleForce() -{ -} +#include "DEG_depsgraph_query.h" -ParticleEmitter::~ParticleEmitter() -{ -} +namespace blender::sim { -static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) +static CustomDataType cpp_to_custom_data_type(const CPPType &type) { if (type.is<float3>()) { return CD_PROP_FLOAT3; @@ -48,18 +41,18 @@ static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) return CD_PROP_FLOAT; } -static const fn::CPPType &custom_to_cpp_data_type(CustomDataType type) +static const CPPType &custom_to_cpp_data_type(CustomDataType type) { switch (type) { case CD_PROP_FLOAT3: - return fn::CPPType::get<float3>(); + return CPPType::get<float3>(); case CD_PROP_FLOAT: - return fn::CPPType::get<float>(); + return CPPType::get<float>(); case CD_PROP_INT32: - return fn::CPPType::get<int32_t>(); + return CPPType::get<int32_t>(); default: BLI_assert(false); - return fn::CPPType::get<float>(); + return CPPType::get<float>(); } } @@ -67,33 +60,33 @@ class CustomDataAttributesRef { private: Array<void *> buffers_; int64_t size_; - const fn::AttributesInfo &info_; + const AttributesInfo &info_; public: - CustomDataAttributesRef(CustomData &custom_data, int64_t size, const fn::AttributesInfo &info) + CustomDataAttributesRef(CustomData &custom_data, int64_t size, const AttributesInfo &info) : buffers_(info.size(), nullptr), size_(size), info_(info) { for (int attribute_index : info.index_range()) { StringRefNull name = info.name_of(attribute_index); - const fn::CPPType &cpp_type = info.type_of(attribute_index); + const CPPType &cpp_type = info.type_of(attribute_index); CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); void *data = CustomData_get_layer_named(&custom_data, custom_type, name.c_str()); buffers_[attribute_index] = data; } } - operator fn::MutableAttributesRef() + operator MutableAttributesRef() { - return fn::MutableAttributesRef(info_, buffers_, size_); + return MutableAttributesRef(info_, buffers_, size_); } - operator fn::AttributesRef() const + operator AttributesRef() const { - return fn::AttributesRef(info_, buffers_, size_); + return AttributesRef(info_, buffers_, size_); } }; -static void ensure_attributes_exist(ParticleSimulationState *state, const fn::AttributesInfo &info) +static void ensure_attributes_exist(ParticleSimulationState *state, const AttributesInfo &info) { bool found_layer_to_remove; do { @@ -101,7 +94,7 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At for (int layer_index = 0; layer_index < state->attributes.totlayer; layer_index++) { CustomDataLayer *layer = &state->attributes.layers[layer_index]; BLI_assert(layer->name != nullptr); - const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); + const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); StringRefNull name = layer->name; if (!info.has_attribute(name, cpp_type)) { found_layer_to_remove = true; @@ -113,7 +106,7 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At for (int attribute_index : info.index_range()) { StringRefNull attribute_name = info.name_of(attribute_index); - const fn::CPPType &cpp_type = info.type_of(attribute_index); + const CPPType &cpp_type = info.type_of(attribute_index); CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); if (CustomData_get_layer_named(&state->attributes, custom_type, attribute_name.c_str()) == nullptr) { @@ -128,50 +121,241 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At } } -BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context, - ParticleSimulationState &state, - const fn::AttributesInfo &attributes_info) +BLI_NOINLINE static void apply_remaining_diffs(ParticleChunkContext &context) { - CustomDataAttributesRef custom_data_attributes{ - state.attributes, state.tot_particles, attributes_info}; - fn::MutableAttributesRef attributes = custom_data_attributes; + BLI_assert(context.integration != nullptr); + MutableSpan<float3> positions = context.attributes.get<float3>("Position"); + MutableSpan<float3> velocities = context.attributes.get<float3>("Velocity"); + + for (int i : context.index_mask) { + positions[i] += context.integration->position_diffs[i]; + velocities[i] += context.integration->velocity_diffs[i]; + } +} - Array<float3> force_vectors{state.tot_particles, {0, 0, 0}}; - const Vector<const ParticleForce *> *forces = - solve_context.influences().particle_forces.lookup_ptr(state.head.name); +BLI_NOINLINE static void find_next_event_per_particle( + SimulationSolveContext &solve_context, + ParticleChunkContext &particles, + Span<const ParticleEvent *> events, + MutableSpan<int> r_next_event_indices, + MutableSpan<float> r_time_factors_to_next_event) +{ + r_next_event_indices.fill_indices(particles.index_mask, -1); + r_time_factors_to_next_event.fill_indices(particles.index_mask, 1.0f); + + Array<float> time_factors(particles.index_mask.min_array_size()); + for (int event_index : events.index_range()) { + time_factors.fill(-1.0f); + ParticleEventFilterContext event_context{solve_context, particles, time_factors}; + const ParticleEvent &event = *events[event_index]; + event.filter(event_context); + + for (int i : particles.index_mask) { + const float time_factor = time_factors[i]; + const float previously_smallest_time_factor = r_time_factors_to_next_event[i]; + if (time_factor >= 0.0f && time_factor <= previously_smallest_time_factor) { + r_time_factors_to_next_event[i] = time_factor; + r_next_event_indices[i] = event_index; + } + } + } +} - if (forces != nullptr) { - ParticleChunkContext particle_chunk_context{IndexMask(state.tot_particles), attributes}; - ParticleForceContext particle_force_context{ - solve_context, particle_chunk_context, force_vectors}; +BLI_NOINLINE static void forward_particles_to_next_event_or_end( + ParticleChunkContext &particles, Span<float> time_factors_to_next_event) +{ + MutableSpan<float3> positions = particles.attributes.get<float3>("Position"); + MutableSpan<float3> velocities = particles.attributes.get<float3>("Velocity"); + + MutableSpan<float3> position_diffs = particles.integration->position_diffs; + MutableSpan<float3> velocity_diffs = particles.integration->velocity_diffs; + MutableSpan<float> durations = particles.integration->durations; + + for (int i : particles.index_mask) { + const float time_factor = time_factors_to_next_event[i]; + positions[i] += position_diffs[i] * time_factor; + velocities[i] += velocity_diffs[i] * time_factor; + + const float remaining_time_factor = 1.0f - time_factor; + position_diffs[i] *= remaining_time_factor; + velocity_diffs[i] *= remaining_time_factor; + durations[i] *= remaining_time_factor; + } +} - for (const ParticleForce *force : *forces) { - force->add_force(particle_force_context); +BLI_NOINLINE static void group_particles_by_event( + IndexMask mask, + Span<int> next_event_indices, + MutableSpan<Vector<int64_t>> r_particles_per_event) +{ + for (int i : mask) { + int event_index = next_event_indices[i]; + if (event_index >= 0) { + r_particles_per_event[event_index].append(i); } } +} - MutableSpan<float3> positions = attributes.get<float3>("Position"); - MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); - MutableSpan<float> birth_times = attributes.get<float>("Birth Time"); - MutableSpan<int> dead_states = attributes.get<int>("Dead"); - float end_time = solve_context.solve_interval().end(); - float time_step = solve_context.solve_interval().duration(); - for (int i : positions.index_range()) { - velocities[i] += force_vectors[i] * time_step; - positions[i] += velocities[i] * time_step; - - if (end_time - birth_times[i] > 2) { - dead_states[i] = true; +BLI_NOINLINE static void execute_events(SimulationSolveContext &solve_context, + ParticleChunkContext &all_particles, + Span<const ParticleEvent *> events, + Span<Vector<int64_t>> particles_per_event) +{ + for (int event_index : events.index_range()) { + Span<int64_t> pindices = particles_per_event[event_index]; + if (pindices.is_empty()) { + continue; + } + + const ParticleEvent &event = *events[event_index]; + ParticleChunkContext particles{ + all_particles.state, pindices, all_particles.attributes, all_particles.integration}; + ParticleActionContext action_context{solve_context, particles}; + event.execute(action_context); + } +} + +BLI_NOINLINE static void find_unfinished_particles(IndexMask index_mask, + Span<float> time_factors_to_next_event, + Vector<int64_t> &r_unfinished_pindices) +{ + for (int i : index_mask) { + float time_factor = time_factors_to_next_event[i]; + if (time_factor < 1.0f) { + r_unfinished_pindices.append(i); + } + } +} + +BLI_NOINLINE static void simulate_to_next_event(SimulationSolveContext &solve_context, + ParticleChunkContext &particles, + Span<const ParticleEvent *> events, + Vector<int64_t> &r_unfinished_pindices) +{ + int array_size = particles.index_mask.min_array_size(); + Array<int> next_event_indices(array_size); + Array<float> time_factors_to_next_event(array_size); + + find_next_event_per_particle( + solve_context, particles, events, next_event_indices, time_factors_to_next_event); + + forward_particles_to_next_event_or_end(particles, time_factors_to_next_event); + + Array<Vector<int64_t>> particles_per_event(events.size()); + group_particles_by_event(particles.index_mask, next_event_indices, particles_per_event); + + execute_events(solve_context, particles, events, particles_per_event); + find_unfinished_particles( + particles.index_mask, time_factors_to_next_event, r_unfinished_pindices); +} + +BLI_NOINLINE static void simulate_with_max_n_events(SimulationSolveContext &solve_context, + ParticleSimulationState &state, + ParticleChunkContext &particles, + int max_events) +{ + Span<const ParticleEvent *> events = solve_context.influences.particle_events.lookup_as( + state.head.name); + if (events.size() == 0) { + apply_remaining_diffs(particles); + return; + } + + Vector<int64_t> unfininished_pindices = particles.index_mask.indices(); + for (int iteration : IndexRange(max_events)) { + UNUSED_VARS(iteration); + if (unfininished_pindices.is_empty()) { + break; } + + Vector<int64_t> new_unfinished_pindices; + ParticleChunkContext remaining_particles{particles.state, + unfininished_pindices.as_span(), + particles.attributes, + particles.integration}; + simulate_to_next_event(solve_context, remaining_particles, events, new_unfinished_pindices); + unfininished_pindices = std::move(new_unfinished_pindices); + } + + if (!unfininished_pindices.is_empty()) { + ParticleChunkContext remaining_particles{particles.state, + unfininished_pindices.as_span(), + particles.attributes, + particles.integration}; + apply_remaining_diffs(remaining_particles); } } +BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_context, + ParticleSimulationState &state, + MutableAttributesRef attributes, + MutableSpan<float> remaining_durations, + float end_time) +{ + int particle_amount = attributes.size(); + + Span<const ParticleAction *> begin_actions = + solve_context.influences.particle_time_step_begin_actions.lookup_as(state.head.name); + for (const ParticleAction *action : begin_actions) { + ParticleChunkContext particles{state, IndexMask(particle_amount), attributes}; + ParticleActionContext action_context{solve_context, particles}; + action->execute(action_context); + } + + Array<float3> force_vectors{particle_amount, {0, 0, 0}}; + Span<const ParticleForce *> forces = solve_context.influences.particle_forces.lookup_as( + state.head.name); + for (const ParticleForce *force : forces) { + ParticleChunkContext particles{state, IndexMask(particle_amount), attributes}; + ParticleForceContext particle_force_context{solve_context, particles, force_vectors}; + force->add_force(particle_force_context); + } + + MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); + + Array<float3> position_diffs(particle_amount); + Array<float3> velocity_diffs(particle_amount); + for (int i : IndexRange(particle_amount)) { + const float time_step = remaining_durations[i]; + velocity_diffs[i] = force_vectors[i] * time_step; + position_diffs[i] = (velocities[i] + velocity_diffs[i] / 2.0f) * time_step; + } + + ParticleChunkIntegrationContext integration_context = { + position_diffs, velocity_diffs, remaining_durations, end_time}; + ParticleChunkContext particle_chunk_context{ + state, IndexMask(particle_amount), attributes, &integration_context}; + + simulate_with_max_n_events(solve_context, state, particle_chunk_context, 10); + + Span<const ParticleAction *> end_actions = + solve_context.influences.particle_time_step_end_actions.lookup_as(state.head.name); + for (const ParticleAction *action : end_actions) { + ParticleChunkContext particles{state, IndexMask(particle_amount), attributes}; + ParticleActionContext action_context{solve_context, particles}; + action->execute(action_context); + } +} + +BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context, + ParticleSimulationState &state, + const AttributesInfo &attributes_info) +{ + CustomDataAttributesRef custom_data_attributes{ + state.attributes, state.tot_particles, attributes_info}; + MutableAttributesRef attributes = custom_data_attributes; + + Array<float> remaining_durations(state.tot_particles, solve_context.solve_interval.duration()); + simulate_particle_chunk( + solve_context, state, attributes, remaining_durations, solve_context.solve_interval.stop()); +} + BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context, ParticleAllocators &particle_allocators) { - for (const ParticleEmitter *emitter : solve_context.influences().particle_emitters) { + for (const ParticleEmitter *emitter : solve_context.influences.particle_emitters) { ParticleEmitterContext emitter_context{ - solve_context, particle_allocators, solve_context.solve_interval()}; + solve_context, particle_allocators, solve_context.solve_interval}; emitter->emit(emitter_context); } } @@ -181,10 +365,10 @@ BLI_NOINLINE static int count_particles_after_time_step(ParticleSimulationState { CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, allocator.attributes_info()}; - fn::MutableAttributesRef attributes = custom_data_attributes; + MutableAttributesRef attributes = custom_data_attributes; int new_particle_amount = attributes.get<int>("Dead").count(0); - for (fn::MutableAttributesRef emitted_attributes : allocator.get_allocations()) { + for (MutableAttributesRef emitted_attributes : allocator.get_allocations()) { new_particle_amount += emitted_attributes.get<int>("Dead").count(0); } @@ -199,7 +383,7 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, allocator.attributes_info()}; - Vector<fn::MutableAttributesRef> particle_sources; + Vector<MutableAttributesRef> particle_sources; particle_sources.append(custom_data_attributes); particle_sources.extend(allocator.get_allocations()); @@ -211,16 +395,16 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta dead_layer = &layer; continue; } - const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type); - fn::GMutableSpan new_buffer{ + const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type); + GMutableSpan new_buffer{ cpp_type, MEM_mallocN_aligned(new_particle_amount * cpp_type.size(), cpp_type.alignment(), AT), new_particle_amount}; int current = 0; - for (fn::MutableAttributesRef attributes : particle_sources) { + for (MutableAttributesRef attributes : particle_sources) { Span<int> dead_states = attributes.get<int>("Dead"); - fn::GSpan source_buffer = attributes.get(name); + GSpan source_buffer = attributes.get(name); BLI_assert(source_buffer.type() == cpp_type); for (int i : attributes.index_range()) { if (dead_states[i] == 0) { @@ -233,7 +417,7 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta if (layer.data != nullptr) { MEM_freeN(layer.data); } - layer.data = new_buffer.buffer(); + layer.data = new_buffer.data(); } BLI_assert(dead_layer != nullptr); @@ -246,56 +430,10 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta state.next_particle_id += allocator.total_allocated(); } -static void update_persistent_data_handles(Simulation &simulation, - const VectorSet<ID *> &used_data_blocks) -{ - Set<ID *> contained_ids; - Set<int> used_handles; - - /* Remove handles that have been invalidated. */ - LISTBASE_FOREACH_MUTABLE ( - PersistentDataHandleItem *, handle_item, &simulation.persistent_data_handles) { - if (handle_item->id == nullptr) { - BLI_remlink(&simulation.persistent_data_handles, handle_item); - continue; - } - if (!used_data_blocks.contains(handle_item->id)) { - id_us_min(handle_item->id); - BLI_remlink(&simulation.persistent_data_handles, handle_item); - MEM_freeN(handle_item); - continue; - } - contained_ids.add_new(handle_item->id); - used_handles.add_new(handle_item->handle); - } - - /* Add new handles that are not in the list yet. */ - int next_handle = 0; - for (ID *id : used_data_blocks) { - if (contained_ids.contains(id)) { - continue; - } - - /* Find the next available handle. */ - while (used_handles.contains(next_handle)) { - next_handle++; - } - used_handles.add_new(next_handle); - - PersistentDataHandleItem *handle_item = (PersistentDataHandleItem *)MEM_callocN( - sizeof(*handle_item), AT); - /* Cannot store const pointers in DNA. */ - id_us_plus(id); - handle_item->id = id; - handle_item->handle = next_handle; - - BLI_addtail(&simulation.persistent_data_handles, handle_item); - } -} - void initialize_simulation_states(Simulation &simulation, Depsgraph &UNUSED(depsgraph), - const SimulationInfluences &UNUSED(influences)) + const SimulationInfluences &UNUSED(influences), + const bke::PersistentDataHandleMap &UNUSED(handle_map)) { simulation.current_simulation_time = 0.0f; } @@ -303,15 +441,10 @@ void initialize_simulation_states(Simulation &simulation, void solve_simulation_time_step(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map, + const DependencyAnimations &dependency_animations, float time_step) { - update_persistent_data_handles(simulation, influences.used_data_blocks); - - bke::PersistentDataHandleMap handle_map; - LISTBASE_FOREACH (PersistentDataHandleItem *, handle, &simulation.persistent_data_handles) { - handle_map.add(handle->handle, *handle->id); - } - SimulationStateMap state_map; LISTBASE_FOREACH (SimulationState *, state, &simulation.states) { state_map.add(state); @@ -322,30 +455,32 @@ void solve_simulation_time_step(Simulation &simulation, influences, TimeInterval(simulation.current_simulation_time, time_step), state_map, - handle_map}; - TimeInterval simulation_time_interval{simulation.current_simulation_time, time_step}; + handle_map, + dependency_animations}; Span<ParticleSimulationState *> particle_simulation_states = state_map.lookup<ParticleSimulationState>(); - Map<std::string, std::unique_ptr<fn::AttributesInfo>> attribute_infos; + Map<std::string, std::unique_ptr<AttributesInfo>> attribute_infos; Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators_map; for (ParticleSimulationState *state : particle_simulation_states) { - const fn::AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( + const AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( state->head.name); - auto info = std::make_unique<fn::AttributesInfo>(builder); + auto info = std::make_unique<AttributesInfo>(builder); ensure_attributes_exist(state, *info); + uint32_t hash_seed = DefaultHash<StringRef>{}(state->head.name); particle_allocators_map.add_new( - state->head.name, std::make_unique<ParticleAllocator>(*info, state->next_particle_id)); + state->head.name, + std::make_unique<ParticleAllocator>(*info, state->next_particle_id, hash_seed)); attribute_infos.add_new(state->head.name, std::move(info)); } ParticleAllocators particle_allocators{particle_allocators_map}; for (ParticleSimulationState *state : particle_simulation_states) { - const fn::AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); + const AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); simulate_existing_particles(solve_context, *state, attributes_info); } @@ -353,10 +488,35 @@ void solve_simulation_time_step(Simulation &simulation, for (ParticleSimulationState *state : particle_simulation_states) { ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name); + + for (MutableAttributesRef attributes : allocator.get_allocations()) { + Span<const ParticleAction *> actions = influences.particle_birth_actions.lookup_as( + state->head.name); + for (const ParticleAction *action : actions) { + ParticleChunkContext chunk_context{*state, IndexRange(attributes.size()), attributes}; + ParticleActionContext action_context{solve_context, chunk_context}; + action->execute(action_context); + } + } + } + + for (ParticleSimulationState *state : particle_simulation_states) { + ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name); + + for (MutableAttributesRef attributes : allocator.get_allocations()) { + Array<float> remaining_durations(attributes.size()); + Span<float> birth_times = attributes.get<float>("Birth Time"); + const float end_time = solve_context.solve_interval.stop(); + for (int i : attributes.index_range()) { + remaining_durations[i] = end_time - birth_times[i]; + } + simulate_particle_chunk(solve_context, *state, attributes, remaining_durations, end_time); + } + remove_dead_and_add_new_particles(*state, allocator); } - simulation.current_simulation_time = simulation_time_interval.end(); + simulation.current_simulation_time = solve_context.solve_interval.stop(); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver.hh b/source/blender/simulation/intern/simulation_solver.hh index 3fc61f579e3..2cf8eb2aa27 100644 --- a/source/blender/simulation/intern/simulation_solver.hh +++ b/source/blender/simulation/intern/simulation_solver.hh @@ -16,259 +16,22 @@ #pragma once -#include "BLI_float3.hh" -#include "BLI_span.hh" - -#include "DNA_simulation_types.h" - -#include "FN_attributes_ref.hh" - -#include "BKE_persistent_data_handle.hh" -#include "BKE_simulation.h" - -#include "particle_allocator.hh" -#include "time_interval.hh" +#include "simulation_collect_influences.hh" struct Depsgraph; namespace blender::sim { -class ParticleEmitterContext; -class ParticleForceContext; - -class ParticleEmitter { - public: - virtual ~ParticleEmitter(); - virtual void emit(ParticleEmitterContext &context) const = 0; -}; - -class ParticleForce { - public: - virtual ~ParticleForce(); - virtual void add_force(ParticleForceContext &context) const = 0; -}; - -struct SimulationInfluences { - Map<std::string, Vector<const ParticleForce *>> particle_forces; - Map<std::string, fn::AttributesInfoBuilder *> particle_attributes_builder; - Vector<const ParticleEmitter *> particle_emitters; - VectorSet<ID *> used_data_blocks; -}; - -class SimulationStateMap { - private: - Map<StringRefNull, SimulationState *> states_by_name_; - Map<StringRefNull, Vector<SimulationState *>> states_by_type_; - - public: - void add(SimulationState *state) - { - states_by_name_.add_new(state->name, state); - states_by_type_.lookup_or_add_default(state->type).append(state); - } - - template<typename StateType> StateType *lookup(StringRef name) const - { - const char *type = BKE_simulation_get_state_type_name<StateType>(); - return (StateType *)this->lookup_name_type(name, type); - } - - template<typename StateType> Span<StateType *> lookup() const - { - const char *type = BKE_simulation_get_state_type_name<StateType>(); - return this->lookup_type(type).cast<StateType *>(); - } - - SimulationState *lookup_name_type(StringRef name, StringRef type) const - { - SimulationState *state = states_by_name_.lookup_default_as(name, nullptr); - if (state == nullptr) { - return nullptr; - } - if (state->type == type) { - return state; - } - return nullptr; - } - - Span<SimulationState *> lookup_type(StringRef type) const - { - const Vector<SimulationState *> *states = states_by_type_.lookup_ptr_as(type); - if (states == nullptr) { - return {}; - } - else { - return states->as_span(); - } - } -}; - -class SimulationSolveContext { - private: - Simulation &simulation_; - Depsgraph &depsgraph_; - const SimulationInfluences &influences_; - TimeInterval solve_interval_; - const SimulationStateMap &state_map_; - const bke::PersistentDataHandleMap &id_handle_map_; - - public: - SimulationSolveContext(Simulation &simulation, - Depsgraph &depsgraph, - const SimulationInfluences &influences, - TimeInterval solve_interval, - const SimulationStateMap &state_map, - const bke::PersistentDataHandleMap &handle_map) - : simulation_(simulation), - depsgraph_(depsgraph), - influences_(influences), - solve_interval_(solve_interval), - state_map_(state_map), - id_handle_map_(handle_map) - { - } - - TimeInterval solve_interval() const - { - return solve_interval_; - } - - const SimulationInfluences &influences() const - { - return influences_; - } - - const bke::PersistentDataHandleMap &handle_map() const - { - return id_handle_map_; - } - - const SimulationStateMap &state_map() const - { - return state_map_; - } -}; - -class ParticleAllocators { - private: - Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators_; - - public: - ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators) - : allocators_(allocators) - { - } - - ParticleAllocator *try_get_allocator(StringRef particle_simulation_name) - { - auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name); - if (ptr != nullptr) { - return ptr->get(); - } - else { - return nullptr; - } - } -}; - -class ParticleChunkContext { - private: - IndexMask index_mask_; - fn::MutableAttributesRef attributes_; - - public: - ParticleChunkContext(IndexMask index_mask, fn::MutableAttributesRef attributes) - : index_mask_(index_mask), attributes_(attributes) - { - } - - IndexMask index_mask() const - { - return index_mask_; - } - - fn::MutableAttributesRef attributes() - { - return attributes_; - } - - fn::AttributesRef attributes() const - { - return attributes_; - } -}; - -class ParticleEmitterContext { - private: - SimulationSolveContext &solve_context_; - ParticleAllocators &particle_allocators_; - TimeInterval simulation_time_interval_; - - public: - ParticleEmitterContext(SimulationSolveContext &solve_context, - ParticleAllocators &particle_allocators, - TimeInterval simulation_time_interval) - : solve_context_(solve_context), - particle_allocators_(particle_allocators), - simulation_time_interval_(simulation_time_interval) - { - } - - SimulationSolveContext &solve_context() - { - return solve_context_; - } - - ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) - { - return particle_allocators_.try_get_allocator(particle_simulation_name); - } - - TimeInterval simulation_time_interval() const - { - return simulation_time_interval_; - } -}; - -class ParticleForceContext { - private: - SimulationSolveContext &solve_context_; - const ParticleChunkContext &particle_chunk_context_; - MutableSpan<float3> force_dst_; - - public: - ParticleForceContext(SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context, - MutableSpan<float3> force_dst) - : solve_context_(solve_context), - particle_chunk_context_(particle_chunk_context), - force_dst_(force_dst) - { - } - - SimulationSolveContext &solve_context() - { - return solve_context_; - } - - const ParticleChunkContext &particle_chunk() const - { - return particle_chunk_context_; - } - - MutableSpan<float3> force_dst() - { - return force_dst_; - } -}; - void initialize_simulation_states(Simulation &simulation, Depsgraph &depsgraph, - const SimulationInfluences &influences); + const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map); void solve_simulation_time_step(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map, + const DependencyAnimations &dependency_animations, float time_step); } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.cc b/source/blender/simulation/intern/simulation_solver_influences.cc new file mode 100644 index 00000000000..3485d7c7bfb --- /dev/null +++ b/source/blender/simulation/intern/simulation_solver_influences.cc @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "simulation_solver_influences.hh" + +#include "DNA_object_types.h" + +namespace blender::sim { + +ParticleForce::~ParticleForce() +{ +} + +ParticleEmitter::~ParticleEmitter() +{ +} + +ParticleAction::~ParticleAction() +{ +} + +ParticleEvent::~ParticleEvent() +{ +} + +DependencyAnimations::~DependencyAnimations() +{ +} + +bool DependencyAnimations::is_object_transform_changing(Object &UNUSED(object)) const +{ + return false; +} + +void DependencyAnimations::get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const +{ + assert_same_size(simulation_times, r_transforms); + float4x4 world_matrix = object.obmat; + r_transforms.fill(world_matrix); +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.hh b/source/blender/simulation/intern/simulation_solver_influences.hh new file mode 100644 index 00000000000..f7b8affd88d --- /dev/null +++ b/source/blender/simulation/intern/simulation_solver_influences.hh @@ -0,0 +1,237 @@ +/* + * 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 __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ +#define __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ + +#include "BLI_float3.hh" +#include "BLI_float4x4.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_span.hh" + +#include "DNA_simulation_types.h" + +#include "FN_attributes_ref.hh" + +#include "BKE_persistent_data_handle.hh" +#include "BKE_simulation.h" + +#include "particle_allocator.hh" +#include "time_interval.hh" + +namespace blender::sim { + +using fn::AttributesInfo; +using fn::AttributesInfoBuilder; +using fn::AttributesRef; +using fn::CPPType; +using fn::GMutableSpan; +using fn::GSpan; +using fn::MutableAttributesRef; + +struct ParticleEmitterContext; +struct ParticleForceContext; +struct ParticleActionContext; +struct ParticleEventFilterContext; + +class ParticleEmitter { + public: + virtual ~ParticleEmitter(); + virtual void emit(ParticleEmitterContext &context) const = 0; +}; + +class ParticleForce { + public: + virtual ~ParticleForce(); + virtual void add_force(ParticleForceContext &context) const = 0; +}; + +class ParticleAction { + public: + virtual ~ParticleAction(); + virtual void execute(ParticleActionContext &context) const = 0; +}; + +class ParticleEvent { + public: + virtual ~ParticleEvent(); + virtual void filter(ParticleEventFilterContext &context) const = 0; + virtual void execute(ParticleActionContext &context) const = 0; +}; + +struct SimulationInfluences { + MultiValueMap<std::string, const ParticleForce *> particle_forces; + MultiValueMap<std::string, const ParticleAction *> particle_birth_actions; + MultiValueMap<std::string, const ParticleAction *> particle_time_step_begin_actions; + MultiValueMap<std::string, const ParticleAction *> particle_time_step_end_actions; + MultiValueMap<std::string, const ParticleEvent *> particle_events; + Map<std::string, AttributesInfoBuilder *> particle_attributes_builder; + Vector<const ParticleEmitter *> particle_emitters; +}; + +class SimulationStateMap { + private: + Map<StringRefNull, SimulationState *> states_by_name_; + MultiValueMap<StringRefNull, SimulationState *> states_by_type_; + + public: + void add(SimulationState *state) + { + states_by_name_.add_new(state->name, state); + states_by_type_.add(state->type, state); + } + + template<typename StateType> StateType *lookup(StringRef name) const + { + const char *type = BKE_simulation_get_state_type_name<StateType>(); + return (StateType *)this->lookup_name_type(name, type); + } + + template<typename StateType> Span<StateType *> lookup() const + { + const char *type = BKE_simulation_get_state_type_name<StateType>(); + return this->lookup_type(type).cast<StateType *>(); + } + + SimulationState *lookup_name_type(StringRef name, StringRef type) const + { + SimulationState *state = states_by_name_.lookup_default_as(name, nullptr); + if (state == nullptr) { + return nullptr; + } + if (state->type == type) { + return state; + } + return nullptr; + } + + Span<SimulationState *> lookup_type(StringRef type) const + { + return states_by_type_.lookup_as(type); + } +}; + +class DependencyAnimations { + public: + ~DependencyAnimations(); + + virtual bool is_object_transform_changing(Object &object) const; + virtual void get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const; +}; + +struct SimulationSolveContext { + Simulation &simulation; + Depsgraph &depsgraph; + const SimulationInfluences &influences; + TimeInterval solve_interval; + const SimulationStateMap &state_map; + const bke::PersistentDataHandleMap &handle_map; + const DependencyAnimations &dependency_animations; +}; + +class ParticleAllocators { + private: + Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators_; + + public: + ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators) + : allocators_(allocators) + { + } + + ParticleAllocator *try_get_allocator(StringRef particle_simulation_name) + { + auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name); + if (ptr != nullptr) { + return ptr->get(); + } + else { + return nullptr; + } + } +}; + +struct ParticleChunkIntegrationContext { + MutableSpan<float3> position_diffs; + MutableSpan<float3> velocity_diffs; + MutableSpan<float> durations; + float end_time; +}; + +struct ParticleChunkContext { + ParticleSimulationState &state; + IndexMask index_mask; + MutableAttributesRef attributes; + ParticleChunkIntegrationContext *integration = nullptr; + + void update_diffs_after_velocity_change() + { + if (integration == nullptr) { + return; + } + + Span<float> remaining_durations = integration->durations; + MutableSpan<float3> position_diffs = integration->position_diffs; + Span<float3> velocities = attributes.get<float3>("Velocity"); + + for (int i : index_mask) { + const float duration = remaining_durations[i]; + /* This is certainly not a perfect way to "re-integrate" the velocity, but it should be good + * enough for most use cases. Changing the velocity in an instant is not physically correct + * anyway. */ + position_diffs[i] = velocities[i] * duration; + } + } +}; + +struct ParticleEmitterContext { + SimulationSolveContext &solve_context; + ParticleAllocators &particle_allocators; + TimeInterval emit_interval; + + template<typename StateType> StateType *lookup_state(StringRef name) + { + return solve_context.state_map.lookup<StateType>(name); + } + + ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) + { + return particle_allocators.try_get_allocator(particle_simulation_name); + } +}; + +struct ParticleForceContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particles; + MutableSpan<float3> force_dst; +}; + +struct ParticleActionContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particles; +}; + +struct ParticleEventFilterContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particles; + MutableSpan<float> factor_dst; +}; + +} // namespace blender::sim + +#endif /* __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ */ diff --git a/source/blender/simulation/intern/simulation_update.cc b/source/blender/simulation/intern/simulation_update.cc index 09219e0238f..32b582977d0 100644 --- a/source/blender/simulation/intern/simulation_update.cc +++ b/source/blender/simulation/intern/simulation_update.cc @@ -17,8 +17,11 @@ #include "SIM_simulation_update.hh" #include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_object.h" #include "BKE_simulation.h" +#include "DNA_modifier_types.h" #include "DNA_scene_types.h" #include "DNA_simulation_types.h" @@ -29,8 +32,11 @@ #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_rand.h" +#include "BLI_set.hh" #include "BLI_vector.hh" +#include "NOD_node_tree_dependencies.hh" + #include "particle_function.hh" #include "simulation_collect_influences.hh" #include "simulation_solver.hh" @@ -88,6 +94,131 @@ static void update_simulation_state_list(Simulation *simulation, add_missing_states(simulation, required_states); } +class SampledDependencyAnimations : public DependencyAnimations { + private: + TimeInterval simulation_time_interval_; + MultiValueMap<Object *, float4x4> object_transforms_cache_; + + public: + SampledDependencyAnimations(TimeInterval simulation_time_interval) + : simulation_time_interval_(simulation_time_interval) + { + } + + void add_object_transforms(Object &object, Span<float4x4> transforms) + { + object_transforms_cache_.add_multiple(&object, transforms); + } + + bool is_object_transform_changing(Object &object) const + { + return object_transforms_cache_.lookup(&object).size() >= 2; + } + + void get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const + { + assert_same_size(simulation_times, r_transforms); + Span<float4x4> cached_transforms = object_transforms_cache_.lookup(&object); + if (cached_transforms.size() == 0) { + r_transforms.fill(object.obmat); + return; + } + if (cached_transforms.size() == 1) { + r_transforms.fill(cached_transforms[0]); + return; + } + + for (int i : simulation_times.index_range()) { + const float simulation_time = simulation_times[i]; + if (simulation_time <= simulation_time_interval_.start()) { + r_transforms[i] = cached_transforms.first(); + continue; + } + if (simulation_time >= simulation_time_interval_.stop()) { + r_transforms[i] = cached_transforms.last(); + continue; + } + const float factor = simulation_time_interval_.factor_at_time(simulation_time); + BLI_assert(factor > 0.0f && factor < 1.0f); + const float scaled_factor = factor * (cached_transforms.size() - 1); + const int lower_sample = (int)scaled_factor; + const int upper_sample = lower_sample + 1; + const float mix_factor = scaled_factor - lower_sample; + r_transforms[i] = float4x4::interpolate( + cached_transforms[lower_sample], cached_transforms[upper_sample], mix_factor); + } + } +}; + +static void sample_object_transforms(Object &object, + Depsgraph &depsgraph, + Scene &scene, + TimeInterval scene_frame_interval, + MutableSpan<float4x4> r_transforms) +{ + if (r_transforms.size() == 0) { + return; + } + if (r_transforms.size() == 1) { + r_transforms[0] = object.obmat; + return; + } + + Array<float> frames(r_transforms.size()); + scene_frame_interval.compute_uniform_samples(frames); + + for (int i : frames.index_range()) { + float frame = frames[i]; + const int recursion_depth = 5; + BKE_object_modifier_update_subframe( + &depsgraph, &scene, &object, false, recursion_depth, frame, eModifierType_None); + r_transforms[i] = object.obmat; + } +} + +template<typename T> static bool all_values_equal(Span<T> values) +{ + if (values.size() == 0) { + return true; + } + for (const T &value : values.drop_front(1)) { + if (value != values[0]) { + return false; + } + } + return true; +} + +static void prepare_dependency_animations(Depsgraph &depsgraph, + Scene &scene, + Simulation &simulation, + TimeInterval scene_frame_interval, + SampledDependencyAnimations &r_dependency_animations) +{ + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation.dependencies) { + ID *id_cow = DEG_get_evaluated_id(&depsgraph, dependency->id); + if (id_cow == nullptr) { + continue; + } + if (GS(id_cow->name) != ID_OB) { + continue; + } + Object &object_cow = *(Object *)id_cow; + constexpr int sample_count = 10; + Array<float4x4, sample_count> transforms(sample_count); + sample_object_transforms(object_cow, depsgraph, scene, scene_frame_interval, transforms); + + /* If all samples are the same, only store one. */ + Span<float4x4> transforms_to_use = (all_values_equal(transforms.as_span())) ? + transforms.as_span().take_front(1) : + transforms.as_span(); + + r_dependency_animations.add_object_transforms(object_cow, transforms_to_use); + } +} + void update_simulation_in_depsgraph(Depsgraph *depsgraph, Scene *scene_cow, Simulation *simulation_cow) @@ -108,13 +239,20 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, SimulationInfluences influences; RequiredStates required_states; - /* TODO: Use simulation_cow, but need to add depsgraph relations before that. */ - collect_simulation_influences(*simulation_orig, resources, influences, required_states); + collect_simulation_influences(*simulation_cow, resources, influences, required_states); + + bke::PersistentDataHandleMap handle_map; + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation_orig->dependencies) { + ID *id_cow = DEG_get_evaluated_id(depsgraph, dependency->id); + if (id_cow != nullptr) { + handle_map.add(dependency->handle, *id_cow); + } + } if (current_frame == 1) { reinitialize_empty_simulation_states(simulation_orig, required_states); - initialize_simulation_states(*simulation_orig, *depsgraph, influences); + initialize_simulation_states(*simulation_orig, *depsgraph, influences, handle_map); simulation_orig->current_frame = 1; copy_states_to_cow(simulation_orig, simulation_cow); @@ -122,12 +260,97 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, else if (current_frame == simulation_orig->current_frame + 1) { update_simulation_state_list(simulation_orig, required_states); - float time_step = 1.0f / 24.0f; - solve_simulation_time_step(*simulation_orig, *depsgraph, influences, time_step); + const float fps = scene_cow->r.frs_sec / scene_cow->r.frs_sec_base; + const float time_step = 1.0f / fps; + TimeInterval scene_frame_interval(current_frame - 1, 1); + TimeInterval simulation_time_interval(simulation_orig->current_simulation_time, time_step); + SampledDependencyAnimations dependency_animations{simulation_time_interval}; + prepare_dependency_animations( + *depsgraph, *scene_cow, *simulation_orig, scene_frame_interval, dependency_animations); + + solve_simulation_time_step( + *simulation_orig, *depsgraph, influences, handle_map, dependency_animations, time_step); simulation_orig->current_frame = current_frame; copy_states_to_cow(simulation_orig, simulation_cow); } } +/* Returns true when dependencies have changed. */ +bool update_simulation_dependencies(Simulation *simulation) +{ + nodes::NodeTreeDependencies dependencies = nodes::find_node_tree_dependencies( + *simulation->nodetree); + + ListBase *dependency_list = &simulation->dependencies; + + bool dependencies_changed = false; + + Map<ID *, SimulationDependency *> dependency_by_id; + Map<SimulationDependency *, int> old_flag_by_dependency; + Set<int> used_handles; + + /* Remove unused handle items and clear flags that are reinitialized later. */ + LISTBASE_FOREACH_MUTABLE (SimulationDependency *, dependency, dependency_list) { + if (dependencies.depends_on(dependency->id)) { + dependency_by_id.add_new(dependency->id, dependency); + used_handles.add_new(dependency->handle); + old_flag_by_dependency.add_new(dependency, dependency->flag); + dependency->flag &= ~(SIM_DEPENDS_ON_TRANSFORM | SIM_DEPENDS_ON_GEOMETRY); + } + else { + if (dependency->id != nullptr) { + id_us_min(dependency->id); + } + BLI_remlink(dependency_list, dependency); + MEM_freeN(dependency); + dependencies_changed = true; + } + } + + /* Add handle items for new id dependencies. */ + int next_handle = 0; + for (ID *id : dependencies.id_dependencies()) { + dependency_by_id.lookup_or_add_cb(id, [&]() { + while (used_handles.contains(next_handle)) { + next_handle++; + } + used_handles.add_new(next_handle); + + SimulationDependency *dependency = (SimulationDependency *)MEM_callocN(sizeof(*dependency), + AT); + id_us_plus(id); + dependency->id = id; + dependency->handle = next_handle; + BLI_addtail(dependency_list, dependency); + + return dependency; + }); + } + + /* Set appropriate dependency flags. */ + for (Object *object : dependencies.transform_dependencies()) { + SimulationDependency *dependency = dependency_by_id.lookup(&object->id); + dependency->flag |= SIM_DEPENDS_ON_TRANSFORM; + } + for (Object *object : dependencies.geometry_dependencies()) { + SimulationDependency *dependency = dependency_by_id.lookup(&object->id); + dependency->flag |= SIM_DEPENDS_ON_GEOMETRY; + } + + if (!dependencies_changed) { + /* Check if any flags have changed. */ + LISTBASE_FOREACH (SimulationDependency *, dependency, dependency_list) { + uint32_t old_flag = old_flag_by_dependency.lookup_default(dependency, 0); + uint32_t new_flag = dependency->flag; + if (old_flag != new_flag) { + dependencies_changed = true; + break; + } + } + } + + return dependencies_changed; +} + } // namespace blender::sim diff --git a/source/blender/simulation/intern/time_interval.hh b/source/blender/simulation/intern/time_interval.hh index 6f13634ed06..034628fa9be 100644 --- a/source/blender/simulation/intern/time_interval.hh +++ b/source/blender/simulation/intern/time_interval.hh @@ -21,7 +21,7 @@ namespace blender::sim { /** - * The start time is inclusive and the end time is exclusive. The duration is zero, the interval + * The start time is exclusive and the end time is inclusive. If the duration is zero, the interval * describes a single point in time. */ class TimeInterval { @@ -40,7 +40,7 @@ class TimeInterval { return start_; } - float end() const + float stop() const { return start_ + duration_; } @@ -49,6 +49,42 @@ class TimeInterval { { return duration_; } + + float time_at_factor(float factor) const + { + return start_ + factor * duration_; + } + + float factor_at_time(float time) const + { + BLI_assert(duration_ > 0.0f); + return (time - start_) / duration_; + } + + float safe_factor_at_time(float time) const + { + if (duration_ > 0.0f) { + return this->factor_at_time(time); + } + return 0.0f; + } + + void compute_uniform_samples(MutableSpan<float> r_samples) const + { + int64_t amount = r_samples.size(); + if (amount == 0) { + return; + } + if (amount == 1) { + r_samples[0] = this->time_at_factor(0.5f); + return; + } + + const float step = duration_ / (float)(amount - 1); + for (int64_t i : r_samples.index_range()) { + r_samples[i] = start_ + i * step; + } + } }; } // namespace blender::sim diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 332f53c9ee7..07746af4b60 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -98,7 +98,7 @@ void WM_main(struct bContext *C) ATTR_NORETURN; void WM_init_splash(struct bContext *C); -void WM_init_opengl(struct Main *bmain); +void WM_init_opengl(void); void WM_check(struct bContext *C); void WM_reinit_gizmomap_all(struct Main *bmain); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c index e687af15982..90d9ba45637 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c @@ -29,7 +29,6 @@ #include "BKE_context.h" #include "GPU_batch.h" -#include "GPU_glew.h" #include "RNA_access.h" #include "RNA_define.h" diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 396b59ba6e2..2038a82c9fd 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -38,7 +38,6 @@ #include "ED_select_utils.h" #include "ED_view3d.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_state.h" @@ -582,7 +581,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); rcti rect; /* Almost certainly overkill, but allow for many custom gizmos. */ - GLuint buffer[MAXPICKBUF]; + uint buffer[MAXPICKBUF]; short hits; BLI_rcti_init_pt_radius(&rect, co, hotspot); @@ -625,7 +624,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin); - GLuint *buf_iter = buffer; + uint *buf_iter = buffer; int hit_found = -1; float dot_best = FLT_MAX; @@ -649,7 +648,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, return hit_found; } else { - const GLuint *hit_near = GPU_select_buffer_near(buffer, hits); + const uint *hit_near = GPU_select_buffer_near(buffer, hits); return hit_near ? hit_near[3] : -1; } } diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 2112477e62a..43c08a2b980 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -293,7 +293,7 @@ void WM_keyconfig_init(bContext *C) /* initialize only after python init is done, for keymaps that * use python operators */ - if (CTX_py_init_get(C) && (wm->initialized & WM_KEYCONFIG_IS_INITIALIZED) == 0) { + if (CTX_py_init_get(C) && (wm->initialized & WM_KEYCONFIG_IS_INIT) == 0) { /* create default key config, only initialize once, * it's persistent across sessions */ if (!(wm->defaultconf->flag & KEYCONF_INIT_DEFAULT)) { @@ -308,7 +308,7 @@ void WM_keyconfig_init(bContext *C) WM_keyconfig_update_tag(NULL, NULL); WM_keyconfig_update(wm); - wm->initialized |= WM_KEYCONFIG_IS_INITIALIZED; + wm->initialized |= WM_KEYCONFIG_IS_INIT; } } @@ -334,7 +334,7 @@ void WM_check(bContext *C) if (!G.background) { /* case: fileread */ - if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { + if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) { WM_keyconfig_init(C); WM_autosave_init(wm); } @@ -345,9 +345,9 @@ void WM_check(bContext *C) /* case: fileread */ /* note: this runs in bg mode to set the screen context cb */ - if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { - ED_screens_initialize(bmain, wm); - wm->initialized |= WM_WINDOW_IS_INITIALIZED; + if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) { + ED_screens_init(bmain, wm); + wm->initialized |= WM_WINDOW_IS_INIT; } } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index ad3fc7a1302..5f13adcb971 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -39,7 +39,6 @@ #include "BKE_context.h" #include "BKE_idtype.h" -#include "GPU_glew.h" #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_viewport.h" @@ -424,9 +423,8 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) y, drag->imb->x, drag->imb->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, drag->imb->rect, drag->scale, drag->scale, diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index fdbc7a7d136..190db919b41 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -52,7 +52,6 @@ #include "ED_view3d.h" #include "GPU_context.h" -#include "GPU_draw.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_state.h" @@ -1001,7 +1000,7 @@ void wm_draw_update(bContext *C) wmWindow *win; GPU_context_main_lock(); - GPU_free_unused_buffers(); + BKE_image_free_unused_gpu_textures(); for (win = wm->windows.first; win; win = win->next) { #ifdef WIN32 diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index f431a6f431b..f39fc0aecf2 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -258,7 +258,7 @@ static void wm_window_match_keep_current_wm(const bContext *C, bScreen *screen = NULL; /* match oldwm to new dbase, only old files */ - wm->initialized &= ~WM_WINDOW_IS_INITIALIZED; + wm->initialized &= ~WM_WINDOW_IS_INIT; /* when loading without UI, no matching needed */ if (load_ui && (screen = CTX_wm_screen(C))) { diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index eee93fc9459..5a66cbd10eb 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -362,18 +362,8 @@ static void draw_filled_lasso(wmGesture *gt) GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex(&state, - rect.xmin, - rect.ymin, - w, - h, - GL_RED, - GL_UNSIGNED_BYTE, - GL_NEAREST, - pixel_buf, - 1.0f, - 1.0f, - NULL); + immDrawPixelsTex( + &state, rect.xmin, rect.ymin, w, h, GL_R8, false, pixel_buf, 1.0f, 1.0f, NULL); GPU_shader_unbind(); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 945d5fd42e4..85694dec9c8 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -59,6 +59,7 @@ #include "BKE_font.h" #include "BKE_global.h" #include "BKE_icons.h" +#include "BKE_image.h" #include "BKE_keyconfig.h" #include "BKE_lib_remap.h" #include "BKE_main.h" @@ -120,7 +121,6 @@ #include "UI_interface.h" #include "UI_resources.h" -#include "GPU_draw.h" #include "GPU_init_exit.h" #include "GPU_material.h" @@ -171,7 +171,7 @@ void WM_init_state_start_with_console_set(bool value) */ static bool opengl_is_init = false; -void WM_init_opengl(Main *bmain) +void WM_init_opengl(void) { /* must be called only once */ BLI_assert(opengl_is_init == false); @@ -185,9 +185,6 @@ void WM_init_opengl(Main *bmain) DRW_opengl_context_create(); GPU_init(); - GPU_set_mipmap(bmain, true); - GPU_set_linear_mipmap(true); - GPU_set_anisotropic(U.anisotropic_filter); GPU_pass_cache_init(); @@ -315,7 +312,7 @@ void WM_init(bContext *C, int argc, const char **argv) /* sets 3D mouse deadzone */ WM_ndof_deadzone_set(U.ndof_deadzone); #endif - WM_init_opengl(G_MAIN); + WM_init_opengl(); if (!WM_platform_support_perform_checks()) { exit(-1); @@ -578,7 +575,7 @@ void WM_exit_ex(bContext *C, const bool do_python) BKE_subdiv_exit(); if (opengl_is_init) { - GPU_free_unused_buffers(); + BKE_image_free_unused_gpu_textures(); } BKE_blender_free(); /* blender.c, does entire library and spacetypes */ diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index d7102a1e8af..f0ad5f1e9c6 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -363,7 +363,7 @@ void WM_keyconfig_set_active(wmWindowManager *wm, const char *idname) WM_keyconfig_update(wm); BLI_strncpy(U.keyconfigstr, idname, sizeof(U.keyconfigstr)); - if (wm->initialized & WM_KEYCONFIG_IS_INITIALIZED) { + if (wm->initialized & WM_KEYCONFIG_IS_INIT) { U.runtime.is_dirty = true; } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index d1f65b6271b..72cc5a3d7dd 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2322,7 +2322,7 @@ static void radial_control_paint_curve(uint pos, Brush *br, float radius, int li GPU_line_width(2.0f); immUniformColor4f(0.8f, 0.8f, 0.8f, 0.85f); float step = (radius * 2.0f) / (float)line_segments; - BKE_curvemapping_initialize(br->curve); + BKE_curvemapping_init(br->curve); immBegin(GPU_PRIM_LINES, line_segments * 2); for (int i = 0; i < line_segments; i++) { float h1 = BKE_brush_curve_strength_clamped(br, fabsf((i * step) - radius), radius); @@ -2968,7 +2968,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even if (rc->subtype == PROP_ANGLE) { float initial_position[2] = {UNPACK2(rc->initial_mouse)}; float current_position[2] = {UNPACK2(rc->slow_mouse)}; - rc->dial = BLI_dial_initialize(initial_position, 0.0f); + rc->dial = BLI_dial_init(initial_position, 0.0f); /* immediately set the position to get a an initial direction */ BLI_dial_angle(rc->dial, current_position); } @@ -3917,6 +3917,7 @@ static void gesture_box_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "CLIP_OT_select_box"); WM_modalkeymap_assign(keymap, "CLIP_OT_graph_select_box"); WM_modalkeymap_assign(keymap, "MASK_OT_select_box"); + WM_modalkeymap_assign(keymap, "PAINT_OT_mask_box_gesture"); WM_modalkeymap_assign(keymap, "VIEW2D_OT_zoom_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_clip_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_render_border"); diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 74e1c495a00..d0a70596957 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -335,9 +335,8 @@ static void playanim_toscreen( offs_y + (ps->draw_flip[1] ? span_y : 0.0f), ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, ibuf->rect, ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x), ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y), diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c index 477579ed620..0cb76404258 100644 --- a/source/blender/windowmanager/intern/wm_subwindow.c +++ b/source/blender/windowmanager/intern/wm_subwindow.c @@ -29,7 +29,6 @@ #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_viewport.h" diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index e444c5b2109..fc04323333b 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1206,7 +1206,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* Ghost now can call this function for life resizes, * but it should return if WM didn't initialize yet. * Can happen on file read (especially full size window). */ - if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { + if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) { return 1; } if (!ghostwin) { diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h index a26c02317d0..4997361b485 100644 --- a/source/blender/windowmanager/wm_draw.h +++ b/source/blender/windowmanager/wm_draw.h @@ -23,8 +23,6 @@ #pragma once -#include "GPU_glew.h" - struct GPUOffScreen; struct GPUTexture; struct GPUViewport; diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index aed2a9350bb..8784d9f1744 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -694,6 +694,23 @@ elseif(WIN32) ) endif() + if(WITH_GMP) + install( + FILES ${LIBDIR}/gmp/lib/libgmp-10.dll + DESTINATION "." + ) + install( + FILES ${LIBDIR}/gmp/lib/libgmpxx.dll + DESTINATION "." + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + ) + install( + FILES ${LIBDIR}/gmp/lib/libgmpxx_d.dll + DESTINATION "." + CONFIGURATIONS Debug + ) + endif() + if(WITH_WINDOWS_PDB) if(WITH_WINDOWS_STRIPPED_PDB) # Icky hack for older cmake from https://stackoverflow.com/a/21198501 diff --git a/source/creator/creator.c b/source/creator/creator.c index 9268ed15923..3d76d832d9f 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -110,25 +110,10 @@ #include "creator_intern.h" /* own include */ /* Local Function prototypes. */ -#ifdef WITH_PYTHON_MODULE -int main_python_enter(int argc, const char **argv); -void main_python_exit(void); -#endif -#ifdef WITH_USD -/** - * Workaround to make it possible to pass a path at runtime to USD. - * - * USD requires some JSON files, and it uses a static constructor to determine the possible - * file-system paths to find those files. This made it impossible for Blender to pass a path to the - * USD library at runtime, as the constructor would run before Blender's main() function. We have - * patched USD (see usd.diff) to avoid that particular static constructor, and have an - * initialization function instead. - * - * This function is implemented in the USD source code, `pxr/base/lib/plug/initConfig.cpp`. - */ -void usd_initialise_plugin_path(const char *datafiles_usd_path); -#endif +/* -------------------------------------------------------------------- */ +/** \name Local Application State + * \{ */ /* written to by 'creator_args.c' */ struct ApplicationState app_state = { @@ -143,6 +128,8 @@ struct ApplicationState app_state = { }, }; +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Application Level Callbacks * @@ -199,11 +186,19 @@ static void callback_clg_fatal(void *fp) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Main Function +/** \name Blender as a Stand-Alone Python Module (bpy) + * + * While not officially supported, this can be useful for Python developers. + * See: https://wiki.blender.org/wiki/Building_Blender/Other/BlenderAsPyModule * \{ */ #ifdef WITH_PYTHON_MODULE -/* allow python module to call main */ + +/* Called in `bpy_interface.c` when building as a Python module. */ +int main_python_enter(int argc, const char **argv); +void main_python_exit(void); + +/* Rename the 'main' function, allowing Python initialization to call it. */ # define main main_python_enter static void *evil_C = NULL; @@ -211,8 +206,15 @@ static void *evil_C = NULL; /* Environment is not available in macOS shared libraries. */ # include <crt_externs.h> char **environ = NULL; -# endif -#endif +# endif /* __APPLE__ */ + +#endif /* WITH_PYTHON_MODULE */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Function + * \{ */ /** * Blender's main function responsibilities are: @@ -295,7 +297,7 @@ int main(int argc, break; } } - MEM_initialize_memleak_detection(); + MEM_init_memleak_detection(); } #ifdef BUILD_DATE @@ -441,10 +443,21 @@ int main(int argc, BKE_materials_init(); #ifdef WITH_USD + /* Workaround to make it possible to pass a path at runtime to USD. + * + * USD requires some JSON files, and it uses a static constructor to determine the possible + * file-system paths to find those files. This made it impossible for Blender to pass a path to + * the USD library at runtime, as the constructor would run before Blender's main() function. + * We have patched USD (see usd.diff) to avoid that particular static constructor, and have an + * initialization function instead. + * + * This function is implemented in the USD source code, `pxr/base/lib/plug/initConfig.cpp`. */ + extern void usd_initialise_plugin_path(const char *datafiles_usd_path); + /* Tell USD which directory to search for its JSON files. If 'datafiles/usd' * does not exist, the USD library will not be able to read or write any files. */ usd_initialise_plugin_path(BKE_appdir_folder_id(BLENDER_DATAFILES, "usd")); -#endif +#endif /* WITH_USD */ if (G.background == 0) { #ifndef WITH_PYTHON_MODULE @@ -488,7 +501,7 @@ int main(int argc, #ifdef WITH_FREESTYLE /* Initialize Freestyle. */ - FRS_initialize(); + FRS_init(); FRS_set_context(C); #endif diff --git a/source/tools b/source/tools -Subproject 896c5f78952adb2d091d28c65086d46992dabda +Subproject 6a252de776d0b9dca3167c30a7621a4f1e9bc91 diff --git a/tests/gtests/CMakeLists.txt b/tests/gtests/CMakeLists.txt index 282eb9080f5..9a7509f81f3 100644 --- a/tests/gtests/CMakeLists.txt +++ b/tests/gtests/CMakeLists.txt @@ -12,7 +12,6 @@ if(WITH_GTESTS) add_subdirectory(blenloader) add_subdirectory(guardedalloc) add_subdirectory(bmesh) - add_subdirectory(functions) if(WITH_CODEC_FFMPEG) add_subdirectory(ffmpeg) endif() diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index 937279bceb9..bb0a5b63f71 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -39,47 +39,32 @@ else() set(BLI_path_util_extra_libs "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}") endif() -BLENDER_TEST(BLI_array "bf_blenlib") BLENDER_TEST(BLI_array_store "bf_blenlib") BLENDER_TEST(BLI_array_utils "bf_blenlib") BLENDER_TEST(BLI_delaunay_2d "bf_blenlib") -BLENDER_TEST(BLI_disjoint_set "bf_blenlib") -BLENDER_TEST(BLI_edgehash "bf_blenlib") BLENDER_TEST(BLI_expr_pylike_eval "bf_blenlib") BLENDER_TEST(BLI_ghash "bf_blenlib") BLENDER_TEST(BLI_hash_mm2a "bf_blenlib") BLENDER_TEST(BLI_heap "bf_blenlib") BLENDER_TEST(BLI_heap_simple "bf_blenlib") -BLENDER_TEST(BLI_index_mask "bf_blenlib") -BLENDER_TEST(BLI_index_range "bf_blenlib") BLENDER_TEST(BLI_kdopbvh "bf_blenlib;bf_intern_numaapi") -BLENDER_TEST(BLI_linear_allocator "bf_blenlib") BLENDER_TEST(BLI_linklist_lockfree "bf_blenlib;bf_intern_numaapi") BLENDER_TEST(BLI_listbase "bf_blenlib") -BLENDER_TEST(BLI_map "bf_blenlib") BLENDER_TEST(BLI_math_base "bf_blenlib") -BLENDER_TEST(BLI_math_base_safe "bf_blenlib") BLENDER_TEST(BLI_math_bits "bf_blenlib") BLENDER_TEST(BLI_math_color "bf_blenlib") BLENDER_TEST(BLI_math_geom "bf_blenlib") BLENDER_TEST(BLI_math_matrix "bf_blenlib") BLENDER_TEST(BLI_math_vector "bf_blenlib") BLENDER_TEST(BLI_memiter "bf_blenlib") -BLENDER_TEST(BLI_memory_utils "bf_blenlib") BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}") BLENDER_TEST(BLI_polyfill_2d "bf_blenlib") BLENDER_TEST(BLI_session_uuid "bf_blenlib") -BLENDER_TEST(BLI_set "bf_blenlib") -BLENDER_TEST(BLI_span "bf_blenlib") BLENDER_TEST(BLI_stack "bf_blenlib") -BLENDER_TEST(BLI_stack_cxx "bf_blenlib") BLENDER_TEST(BLI_string "bf_blenlib") -BLENDER_TEST(BLI_string_ref "bf_blenlib") BLENDER_TEST(BLI_string_utf8 "bf_blenlib") BLENDER_TEST(BLI_task "bf_blenlib;bf_intern_numaapi") BLENDER_TEST(BLI_task_graph "bf_blenlib;bf_intern_numaapi") -BLENDER_TEST(BLI_vector "bf_blenlib") -BLENDER_TEST(BLI_vector_set "bf_blenlib") BLENDER_TEST_PERFORMANCE(BLI_ghash_performance "bf_blenlib") BLENDER_TEST_PERFORMANCE(BLI_task_performance "bf_blenlib") diff --git a/tests/gtests/blenloader/blendfile_loading_base_test.h b/tests/gtests/blenloader/blendfile_loading_base_test.h index 6ed32168737..a5e75ef6df8 100644 --- a/tests/gtests/blenloader/blendfile_loading_base_test.h +++ b/tests/gtests/blenloader/blendfile_loading_base_test.h @@ -47,7 +47,7 @@ class BlendfileLoadingBaseTest : public testing::Test { * Requires the CLI argument --test-asset-dir to point to ../../lib/tests. * * WARNING: only files saved with Blender 2.80+ can be loaded. Since Blender - * is only partially initialised (most importantly, without window manager), + * is only partially initialized (most importantly, without window manager), * the space types are not registered, so any versioning code that handles * those will SEGFAULT. */ diff --git a/tests/gtests/functions/CMakeLists.txt b/tests/gtests/functions/CMakeLists.txt deleted file mode 100644 index 1246bbd5599..00000000000 --- a/tests/gtests/functions/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ***** END GPL LICENSE BLOCK ***** - -set(INC - . - .. - ../../../source/blender/blenlib - ../../../source/blender/functions - ../../../source/blender/makesdna - ../../../intern/guardedalloc -) - -setup_libdirs() -include_directories(${INC}) - - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}") -set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}") - -if(WITH_BUILDINFO) - set(BUILDINFO buildinfoobj) -endif() - -BLENDER_TEST(FN_array_spans "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_attributes_ref "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_cpp_type "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_generic_vector_array "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_multi_function "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_multi_function_network "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_spans "bf_blenlib;bf_functions;${BUILDINFO}") diff --git a/tests/gtests/runner/BlenderAddTests.cmake b/tests/gtests/runner/BlenderAddTests.cmake deleted file mode 100644 index c4f5c8aba8a..00000000000 --- a/tests/gtests/runner/BlenderAddTests.cmake +++ /dev/null @@ -1,3 +0,0 @@ -# Disable ASAN leak detection when trying to discover tests. -set(ENV{ASAN_OPTIONS} "detect_leaks=0") -include(GoogleTestAddTests) diff --git a/tests/gtests/runner/CMakeLists.txt b/tests/gtests/runner/CMakeLists.txt index 18d8f57364f..8b3390e7aec 100644 --- a/tests/gtests/runner/CMakeLists.txt +++ b/tests/gtests/runner/CMakeLists.txt @@ -35,16 +35,13 @@ endif() # Test libraries need to be linked "whole archive", because they're not # directly referenced from other code. get_property(_test_libs GLOBAL PROPERTY BLENDER_TEST_LIBS) -if(WIN32) - list(APPEND TEST_LIBS ${_test_libs}) -elseif(APPLE) - list(APPEND TEST_LIBS "-Wl,-force_load" ${_test_libs}) +if(WIN32 OR APPLE) + # Windows and macOS set target_link_options after target creation. elseif(UNIX) list(APPEND TEST_LIBS "-Wl,--whole-archive" ${_test_libs} "-Wl,--no-whole-archive") else() message(FATAL_ERROR "Unknown how to link whole-archive with your compiler ${CMAKE_CXX_COMPILER_ID}") endif() -unset(_test_libs) # This builds `bin/tests/blender_test`, but does not add it as a single test. setup_libdirs() @@ -56,18 +53,50 @@ BLENDER_SRC_GTEST_EX( ) setup_liblinks(blender_test) +if(WIN32) + foreach(_lib ${_test_libs}) + # Both target_link_libraries and target_link_options are required here + # target_link_libraries will add any dependend libraries, while just setting + # the wholearchive flag in target link options will not. + target_link_libraries(blender_test ${_lib}) + target_link_options(blender_test PRIVATE /wholearchive:$<TARGET_FILE:${_lib}>) + endforeach() +elseif(APPLE) + foreach(_lib ${_test_libs}) + # We need -force_load for every test library and target_link_libraries will + # deduplicate it. So explicitly set as linker option for every test lib. + target_link_libraries(blender_test ${_lib}) + target_link_options(blender_test PRIVATE "LINKER:-force_load,$<TARGET_FILE:${_lib}>") + endforeach() +endif() + +unset(_test_libs) + # This runs the blender_test executable with `--gtest_list_tests`, then # exposes those tests individually to the ctest runner. # See https://cmake.org/cmake/help/v3.18/module/GoogleTest.html -include(GoogleTest) +# +# We have our own modified copy of this CMake module. +include(GTest) set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT - ${CMAKE_CURRENT_LIST_DIR}/BlenderAddTests.cmake + ${CMAKE_SOURCE_DIR}/build_files/cmake/Modules/GTestAddTests.cmake ) +if(APPLE) + set(_test_release_dir ${TEST_INSTALL_DIR}/Blender.app/Contents/Resources/${BLENDER_VERSION}) +else() + set(_test_release_dir ${TEST_INSTALL_DIR}/${BLENDER_VERSION}) +endif() + gtest_discover_tests(blender_test - # So that unit tests know where to find files: + WORKING_DIRECTORY "${TEST_INSTALL_DIR}" +# So that it will run after the install phase that will copy the required libraries + DISCOVERY_MODE PRE_TEST +# So that unit tests know where to find files: EXTRA_ARGS --test-assets-dir "${CMAKE_SOURCE_DIR}/../lib/tests" - --test-release-dir "${CMAKE_INSTALL_PREFIX}/${BLENDER_VERSION}" + --test-release-dir "${_test_release_dir}" ) + +unset(_test_release_dir) diff --git a/tests/gtests/testing/testing_main.cc b/tests/gtests/testing/testing_main.cc index 9816e71526b..36d39a1b3b9 100644 --- a/tests/gtests/testing/testing_main.cc +++ b/tests/gtests/testing/testing_main.cc @@ -48,7 +48,7 @@ const std::string &flags_test_release_dir() int main(int argc, char **argv) { - MEM_initialize_memleak_detection(); + MEM_init_memleak_detection(); testing::InitGoogleTest(&argc, argv); BLENDER_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); |